blob: 55e69d0f3cbaff3b07025456dc5758b221342b32 [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"
Vladimir Marko6fd16062018-06-26 11:02:04 +010033#include "intrinsics.h"
Anton Kirilov5ec62182016-10-13 20:16:02 +010034#include "intrinsics_arm_vixl.h"
Vladimir Markod8dbc8d2017-09-20 13:37:47 +010035#include "linker/linker_patch.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010036#include "mirror/array-inl.h"
37#include "mirror/class-inl.h"
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +000038#include "scoped_thread_state_change-inl.h"
Scott Wakelingfe885462016-09-22 10:24:38 +010039#include "thread.h"
40#include "utils/arm/assembler_arm_vixl.h"
41#include "utils/arm/managed_register_arm.h"
42#include "utils/assembler.h"
43#include "utils/stack_checks.h"
44
Vladimir Marko0a516052019-10-14 13:00:44 +000045namespace art {
Scott Wakelingfe885462016-09-22 10:24:38 +010046namespace arm {
47
48namespace vixl32 = vixl::aarch32;
49using namespace vixl32; // NOLINT(build/namespaces)
50
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +010051using helpers::DRegisterFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010052using helpers::HighRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080053using helpers::InputDRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010054using helpers::InputOperandAt;
Scott Wakelingc34dba72016-10-03 10:14:44 +010055using helpers::InputRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010056using helpers::InputRegisterAt;
Scott Wakelingfe885462016-09-22 10:24:38 +010057using helpers::InputSRegisterAt;
Anton Kirilov644032c2016-12-06 17:51:43 +000058using helpers::InputVRegister;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010059using helpers::InputVRegisterAt;
Scott Wakelingb77051e2016-11-21 19:46:00 +000060using helpers::Int32ConstantFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000061using helpers::Int64ConstantFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010062using helpers::LocationFrom;
63using helpers::LowRegisterFrom;
64using helpers::LowSRegisterFrom;
Donghui Bai426b49c2016-11-08 14:55:38 +080065using helpers::OperandFrom;
Scott Wakelinga7812ae2016-10-17 10:03:36 +010066using helpers::OutputRegister;
67using helpers::OutputSRegister;
68using helpers::OutputVRegister;
69using helpers::RegisterFrom;
70using helpers::SRegisterFrom;
Anton Kirilov644032c2016-12-06 17:51:43 +000071using helpers::Uint64ConstantFrom;
Scott Wakelingfe885462016-09-22 10:24:38 +010072
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +010073using vixl::EmissionCheckScope;
Artem Serov0fb37192016-12-06 18:13:40 +000074using vixl::ExactAssemblyScope;
75using vixl::CodeBufferCheckScope;
76
Scott Wakelingfe885462016-09-22 10:24:38 +010077using RegisterList = vixl32::RegisterList;
78
79static bool ExpectedPairLayout(Location location) {
80 // We expected this for both core and fpu register pairs.
81 return ((location.low() & 1) == 0) && (location.low() + 1 == location.high());
82}
Artem Serovd4cc5b22016-11-04 11:19:09 +000083// Use a local definition to prevent copying mistakes.
84static constexpr size_t kArmWordSize = static_cast<size_t>(kArmPointerSize);
85static constexpr size_t kArmBitsPerWord = kArmWordSize * kBitsPerByte;
Artem Serov551b28f2016-10-18 19:11:30 +010086static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Scott Wakelingfe885462016-09-22 10:24:38 +010087
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010088// Reference load (except object array loads) is using LDR Rt, [Rn, #offset] which can handle
89// offset < 4KiB. For offsets >= 4KiB, the load shall be emitted as two or more instructions.
Vladimir Marko008e09f32018-08-06 15:42:43 +010090// For the Baker read barrier implementation using link-time generated thunks we need to split
Vladimir Markoeee1c0e2017-04-21 17:58:41 +010091// the offset explicitly.
92constexpr uint32_t kReferenceLoadMinFarOffset = 4 * KB;
93
Roland Levillain5daa4952017-07-03 17:23:56 +010094// Using a base helps identify when we hit Marking Register check breakpoints.
95constexpr int kMarkingRegisterCheckBreakCodeBaseCode = 0x10;
96
Scott Wakelingfe885462016-09-22 10:24:38 +010097#ifdef __
98#error "ARM Codegen VIXL macro-assembler macro already defined."
99#endif
100
Scott Wakelingfe885462016-09-22 10:24:38 +0100101// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
102#define __ down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler()-> // NOLINT
103#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, x).Int32Value()
104
105// Marker that code is yet to be, and must, be implemented.
106#define TODO_VIXL32(level) LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
107
Vladimir Marko88abba22017-05-03 17:09:25 +0100108static inline bool CanEmitNarrowLdr(vixl32::Register rt, vixl32::Register rn, uint32_t offset) {
109 return rt.IsLow() && rn.IsLow() && offset < 32u;
110}
111
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100112class EmitAdrCode {
113 public:
114 EmitAdrCode(ArmVIXLMacroAssembler* assembler, vixl32::Register rd, vixl32::Label* label)
115 : assembler_(assembler), rd_(rd), label_(label) {
Vladimir Markod887ed82018-08-14 13:52:12 +0000116 DCHECK(!assembler->AllowMacroInstructions()); // In ExactAssemblyScope.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +0100117 adr_location_ = assembler->GetCursorOffset();
118 assembler->adr(EncodingSize(Wide), rd, label);
119 }
120
121 ~EmitAdrCode() {
122 DCHECK(label_->IsBound());
123 // The ADR emitted by the assembler does not set the Thumb mode bit we need.
124 // TODO: Maybe extend VIXL to allow ADR for return address?
125 uint8_t* raw_adr = assembler_->GetBuffer()->GetOffsetAddress<uint8_t*>(adr_location_);
126 // Expecting ADR encoding T3 with `(offset & 1) == 0`.
127 DCHECK_EQ(raw_adr[1] & 0xfbu, 0xf2u); // Check bits 24-31, except 26.
128 DCHECK_EQ(raw_adr[0] & 0xffu, 0x0fu); // Check bits 16-23.
129 DCHECK_EQ(raw_adr[3] & 0x8fu, rd_.GetCode()); // Check bits 8-11 and 15.
130 DCHECK_EQ(raw_adr[2] & 0x01u, 0x00u); // Check bit 0, i.e. the `offset & 1`.
131 // Add the Thumb mode bit.
132 raw_adr[2] |= 0x01u;
133 }
134
135 private:
136 ArmVIXLMacroAssembler* const assembler_;
137 vixl32::Register rd_;
138 vixl32::Label* const label_;
139 int32_t adr_location_;
140};
141
Vladimir Marko3232dbb2018-07-25 15:42:46 +0100142static RegisterSet OneRegInReferenceOutSaveEverythingCallerSaves() {
143 InvokeRuntimeCallingConventionARMVIXL calling_convention;
144 RegisterSet caller_saves = RegisterSet::Empty();
145 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
146 // TODO: Add GetReturnLocation() to the calling convention so that we can DCHECK()
147 // that the the kPrimNot result register is the same as the first argument register.
148 return caller_saves;
149}
150
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100151// SaveLiveRegisters and RestoreLiveRegisters from SlowPathCodeARM operate on sets of S registers,
152// for each live D registers they treat two corresponding S registers as live ones.
153//
154// Two following functions (SaveContiguousSRegisterList, RestoreContiguousSRegisterList) build
155// from a list of contiguous S registers a list of contiguous D registers (processing first/last
156// S registers corner cases) and save/restore this new list treating them as D registers.
157// - decreasing code size
158// - avoiding hazards on Cortex-A57, when a pair of S registers for an actual live D register is
159// restored and then used in regular non SlowPath code as D register.
160//
161// For the following example (v means the S register is live):
162// D names: | D0 | D1 | D2 | D4 | ...
163// S names: | S0 | S1 | S2 | S3 | S4 | S5 | S6 | S7 | ...
164// Live? | | v | v | v | v | v | v | | ...
165//
166// S1 and S6 will be saved/restored independently; D registers list (D1, D2) will be processed
167// as D registers.
168//
169// TODO(VIXL): All this code should be unnecessary once the VIXL AArch32 backend provides helpers
170// for lists of floating-point registers.
171static size_t SaveContiguousSRegisterList(size_t first,
172 size_t last,
173 CodeGenerator* codegen,
174 size_t stack_offset) {
175 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
176 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
177 DCHECK_LE(first, last);
178 if ((first == last) && (first == 0)) {
179 __ Vstr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
180 return stack_offset + kSRegSizeInBytes;
181 }
182 if (first % 2 == 1) {
183 __ Vstr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
184 stack_offset += kSRegSizeInBytes;
185 }
186
187 bool save_last = false;
188 if (last % 2 == 0) {
189 save_last = true;
190 --last;
191 }
192
193 if (first < last) {
194 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
195 DCHECK_EQ((last - first + 1) % 2, 0u);
196 size_t number_of_d_regs = (last - first + 1) / 2;
197
198 if (number_of_d_regs == 1) {
199 __ Vstr(d_reg, MemOperand(sp, stack_offset));
200 } else if (number_of_d_regs > 1) {
201 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
202 vixl32::Register base = sp;
203 if (stack_offset != 0) {
204 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000205 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100206 }
207 __ Vstm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
208 }
209 stack_offset += number_of_d_regs * kDRegSizeInBytes;
210 }
211
212 if (save_last) {
213 __ Vstr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
214 stack_offset += kSRegSizeInBytes;
215 }
216
217 return stack_offset;
218}
219
220static size_t RestoreContiguousSRegisterList(size_t first,
221 size_t last,
222 CodeGenerator* codegen,
223 size_t stack_offset) {
224 static_assert(kSRegSizeInBytes == kArmWordSize, "Broken assumption on reg/word sizes.");
225 static_assert(kDRegSizeInBytes == 2 * kArmWordSize, "Broken assumption on reg/word sizes.");
226 DCHECK_LE(first, last);
227 if ((first == last) && (first == 0)) {
228 __ Vldr(vixl32::SRegister(first), MemOperand(sp, stack_offset));
229 return stack_offset + kSRegSizeInBytes;
230 }
231 if (first % 2 == 1) {
232 __ Vldr(vixl32::SRegister(first++), MemOperand(sp, stack_offset));
233 stack_offset += kSRegSizeInBytes;
234 }
235
236 bool restore_last = false;
237 if (last % 2 == 0) {
238 restore_last = true;
239 --last;
240 }
241
242 if (first < last) {
243 vixl32::DRegister d_reg = vixl32::DRegister(first / 2);
244 DCHECK_EQ((last - first + 1) % 2, 0u);
245 size_t number_of_d_regs = (last - first + 1) / 2;
246 if (number_of_d_regs == 1) {
247 __ Vldr(d_reg, MemOperand(sp, stack_offset));
248 } else if (number_of_d_regs > 1) {
249 UseScratchRegisterScope temps(down_cast<CodeGeneratorARMVIXL*>(codegen)->GetVIXLAssembler());
250 vixl32::Register base = sp;
251 if (stack_offset != 0) {
252 base = temps.Acquire();
Scott Wakelingb77051e2016-11-21 19:46:00 +0000253 __ Add(base, sp, Operand::From(stack_offset));
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100254 }
255 __ Vldm(F64, base, NO_WRITE_BACK, DRegisterList(d_reg, number_of_d_regs));
256 }
257 stack_offset += number_of_d_regs * kDRegSizeInBytes;
258 }
259
260 if (restore_last) {
261 __ Vldr(vixl32::SRegister(last + 1), MemOperand(sp, stack_offset));
262 stack_offset += kSRegSizeInBytes;
263 }
264
265 return stack_offset;
266}
267
Vladimir Markod5d2f2c2017-09-26 12:37:26 +0100268static LoadOperandType GetLoadOperandType(DataType::Type type) {
269 switch (type) {
270 case DataType::Type::kReference:
271 return kLoadWord;
272 case DataType::Type::kBool:
273 case DataType::Type::kUint8:
274 return kLoadUnsignedByte;
275 case DataType::Type::kInt8:
276 return kLoadSignedByte;
277 case DataType::Type::kUint16:
278 return kLoadUnsignedHalfword;
279 case DataType::Type::kInt16:
280 return kLoadSignedHalfword;
281 case DataType::Type::kInt32:
282 return kLoadWord;
283 case DataType::Type::kInt64:
284 return kLoadWordPair;
285 case DataType::Type::kFloat32:
286 return kLoadSWord;
287 case DataType::Type::kFloat64:
288 return kLoadDWord;
289 default:
290 LOG(FATAL) << "Unreachable type " << type;
291 UNREACHABLE();
292 }
293}
294
295static StoreOperandType GetStoreOperandType(DataType::Type type) {
296 switch (type) {
297 case DataType::Type::kReference:
298 return kStoreWord;
299 case DataType::Type::kBool:
300 case DataType::Type::kUint8:
301 case DataType::Type::kInt8:
302 return kStoreByte;
303 case DataType::Type::kUint16:
304 case DataType::Type::kInt16:
305 return kStoreHalfword;
306 case DataType::Type::kInt32:
307 return kStoreWord;
308 case DataType::Type::kInt64:
309 return kStoreWordPair;
310 case DataType::Type::kFloat32:
311 return kStoreSWord;
312 case DataType::Type::kFloat64:
313 return kStoreDWord;
314 default:
315 LOG(FATAL) << "Unreachable type " << type;
316 UNREACHABLE();
317 }
318}
319
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100320void SlowPathCodeARMVIXL::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
321 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
322 size_t orig_offset = stack_offset;
323
Andreas Gampe3db70682018-12-26 15:12:03 -0800324 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ true);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100325 for (uint32_t i : LowToHighBits(core_spills)) {
326 // If the register holds an object, update the stack mask.
327 if (locations->RegisterContainsObject(i)) {
328 locations->SetStackBit(stack_offset / kVRegSize);
329 }
330 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
331 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
332 saved_core_stack_offsets_[i] = stack_offset;
333 stack_offset += kArmWordSize;
334 }
335
336 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
337 arm_codegen->GetAssembler()->StoreRegisterList(core_spills, orig_offset);
338
Andreas Gampe3db70682018-12-26 15:12:03 -0800339 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100340 orig_offset = stack_offset;
341 for (uint32_t i : LowToHighBits(fp_spills)) {
342 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
343 saved_fpu_stack_offsets_[i] = stack_offset;
344 stack_offset += kArmWordSize;
345 }
346
347 stack_offset = orig_offset;
348 while (fp_spills != 0u) {
349 uint32_t begin = CTZ(fp_spills);
350 uint32_t tmp = fp_spills + (1u << begin);
351 fp_spills &= tmp; // Clear the contiguous range of 1s.
352 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
353 stack_offset = SaveContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
354 }
355 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
356}
357
358void SlowPathCodeARMVIXL::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
359 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
360 size_t orig_offset = stack_offset;
361
Andreas Gampe3db70682018-12-26 15:12:03 -0800362 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ true);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100363 for (uint32_t i : LowToHighBits(core_spills)) {
364 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
365 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
366 stack_offset += kArmWordSize;
367 }
368
369 // TODO(VIXL): Check the coherency of stack_offset after this with a test.
370 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
371 arm_codegen->GetAssembler()->LoadRegisterList(core_spills, orig_offset);
372
Andreas Gampe3db70682018-12-26 15:12:03 -0800373 uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers= */ false);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100374 while (fp_spills != 0u) {
375 uint32_t begin = CTZ(fp_spills);
376 uint32_t tmp = fp_spills + (1u << begin);
377 fp_spills &= tmp; // Clear the contiguous range of 1s.
378 uint32_t end = (tmp == 0u) ? 32u : CTZ(tmp); // CTZ(0) is undefined.
379 stack_offset = RestoreContiguousSRegisterList(begin, end - 1, codegen, stack_offset);
380 }
381 DCHECK_LE(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
382}
383
384class NullCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
385 public:
386 explicit NullCheckSlowPathARMVIXL(HNullCheck* instruction) : SlowPathCodeARMVIXL(instruction) {}
387
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100388 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100389 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
390 __ Bind(GetEntryLabel());
391 if (instruction_->CanThrowIntoCatchBlock()) {
392 // Live registers will be restored in the catch block if caught.
393 SaveLiveRegisters(codegen, instruction_->GetLocations());
394 }
395 arm_codegen->InvokeRuntime(kQuickThrowNullPointer,
396 instruction_,
397 instruction_->GetDexPc(),
398 this);
399 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
400 }
401
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100402 bool IsFatal() const override { return true; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100403
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100404 const char* GetDescription() const override { return "NullCheckSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100405
406 private:
407 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARMVIXL);
408};
409
Scott Wakelingfe885462016-09-22 10:24:38 +0100410class DivZeroCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
411 public:
412 explicit DivZeroCheckSlowPathARMVIXL(HDivZeroCheck* instruction)
413 : SlowPathCodeARMVIXL(instruction) {}
414
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100415 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100416 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
Scott Wakelingfe885462016-09-22 10:24:38 +0100417 __ Bind(GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100418 arm_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Scott Wakelingfe885462016-09-22 10:24:38 +0100419 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
420 }
421
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100422 bool IsFatal() const override { return true; }
Scott Wakelingfe885462016-09-22 10:24:38 +0100423
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100424 const char* GetDescription() const override { return "DivZeroCheckSlowPathARMVIXL"; }
Scott Wakelingfe885462016-09-22 10:24:38 +0100425
426 private:
427 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARMVIXL);
428};
429
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100430class SuspendCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
431 public:
432 SuspendCheckSlowPathARMVIXL(HSuspendCheck* instruction, HBasicBlock* successor)
433 : SlowPathCodeARMVIXL(instruction), successor_(successor) {}
434
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100435 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100436 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
437 __ Bind(GetEntryLabel());
438 arm_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
439 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
440 if (successor_ == nullptr) {
441 __ B(GetReturnLabel());
442 } else {
443 __ B(arm_codegen->GetLabelOf(successor_));
444 }
445 }
446
447 vixl32::Label* GetReturnLabel() {
448 DCHECK(successor_ == nullptr);
449 return &return_label_;
450 }
451
452 HBasicBlock* GetSuccessor() const {
453 return successor_;
454 }
455
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100456 const char* GetDescription() const override { return "SuspendCheckSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100457
458 private:
459 // If not null, the block to branch to after the suspend check.
460 HBasicBlock* const successor_;
461
462 // If `successor_` is null, the label to branch to after the suspend check.
463 vixl32::Label return_label_;
464
465 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARMVIXL);
466};
467
Scott Wakelingc34dba72016-10-03 10:14:44 +0100468class BoundsCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
469 public:
470 explicit BoundsCheckSlowPathARMVIXL(HBoundsCheck* instruction)
471 : SlowPathCodeARMVIXL(instruction) {}
472
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100473 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100474 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
475 LocationSummary* locations = instruction_->GetLocations();
476
477 __ Bind(GetEntryLabel());
478 if (instruction_->CanThrowIntoCatchBlock()) {
479 // Live registers will be restored in the catch block if caught.
480 SaveLiveRegisters(codegen, instruction_->GetLocations());
481 }
482 // We're moving two locations to locations that could overlap, so we need a parallel
483 // move resolver.
484 InvokeRuntimeCallingConventionARMVIXL calling_convention;
485 codegen->EmitParallelMoves(
486 locations->InAt(0),
487 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100488 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100489 locations->InAt(1),
490 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100491 DataType::Type::kInt32);
Scott Wakelingc34dba72016-10-03 10:14:44 +0100492 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
493 ? kQuickThrowStringBounds
494 : kQuickThrowArrayBounds;
495 arm_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
496 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
497 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
498 }
499
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100500 bool IsFatal() const override { return true; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100501
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100502 const char* GetDescription() const override { return "BoundsCheckSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100503
504 private:
505 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARMVIXL);
506};
507
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100508class LoadClassSlowPathARMVIXL : public SlowPathCodeARMVIXL {
509 public:
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100510 LoadClassSlowPathARMVIXL(HLoadClass* cls, HInstruction* at)
511 : SlowPathCodeARMVIXL(at), cls_(cls) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100512 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100513 DCHECK_EQ(instruction_->IsLoadClass(), cls_ == instruction_);
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100514 }
515
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100516 void EmitNativeCode(CodeGenerator* codegen) override {
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000517 LocationSummary* locations = instruction_->GetLocations();
Vladimir Markoea4c1262017-02-06 19:59:33 +0000518 Location out = locations->Out();
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100519 const uint32_t dex_pc = instruction_->GetDexPc();
520 bool must_resolve_type = instruction_->IsLoadClass() && cls_->MustResolveTypeOnSlowPath();
521 bool must_do_clinit = instruction_->IsClinitCheck() || cls_->MustGenerateClinitCheck();
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100522
523 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
524 __ Bind(GetEntryLabel());
525 SaveLiveRegisters(codegen, locations);
526
527 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100528 if (must_resolve_type) {
529 DCHECK(IsSameDexFile(cls_->GetDexFile(), arm_codegen->GetGraph()->GetDexFile()));
530 dex::TypeIndex type_index = cls_->GetTypeIndex();
531 __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
Vladimir Marko9d479252018-07-24 11:35:20 +0100532 arm_codegen->InvokeRuntime(kQuickResolveType, instruction_, dex_pc, this);
533 CheckEntrypointTypes<kQuickResolveType, void*, uint32_t>();
Vladimir Markoa9f303c2018-07-20 16:43:56 +0100534 // If we also must_do_clinit, the resolved type is now in the correct register.
535 } else {
536 DCHECK(must_do_clinit);
537 Location source = instruction_->IsLoadClass() ? out : locations->InAt(0);
538 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), source);
539 }
540 if (must_do_clinit) {
541 arm_codegen->InvokeRuntime(kQuickInitializeStaticStorage, instruction_, dex_pc, this);
542 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, mirror::Class*>();
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100543 }
544
545 // Move the class to the desired location.
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100546 if (out.IsValid()) {
547 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
548 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
549 }
550 RestoreLiveRegisters(codegen, locations);
551 __ B(GetExitLabel());
552 }
553
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100554 const char* GetDescription() const override { return "LoadClassSlowPathARMVIXL"; }
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100555
556 private:
557 // The class this slow path will load.
558 HLoadClass* const cls_;
559
Scott Wakelinga7812ae2016-10-17 10:03:36 +0100560 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARMVIXL);
561};
562
Artem Serovd4cc5b22016-11-04 11:19:09 +0000563class LoadStringSlowPathARMVIXL : public SlowPathCodeARMVIXL {
564 public:
565 explicit LoadStringSlowPathARMVIXL(HLoadString* instruction)
566 : SlowPathCodeARMVIXL(instruction) {}
567
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100568 void EmitNativeCode(CodeGenerator* codegen) override {
Vladimir Markoea4c1262017-02-06 19:59:33 +0000569 DCHECK(instruction_->IsLoadString());
570 DCHECK_EQ(instruction_->AsLoadString()->GetLoadKind(), HLoadString::LoadKind::kBssEntry);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000571 LocationSummary* locations = instruction_->GetLocations();
572 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
Vladimir Markof3c52b42017-11-17 17:32:12 +0000573 const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
Artem Serovd4cc5b22016-11-04 11:19:09 +0000574
575 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
576 __ Bind(GetEntryLabel());
577 SaveLiveRegisters(codegen, locations);
578
579 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000580 __ Mov(calling_convention.GetRegisterAt(0), string_index.index_);
Artem Serovd4cc5b22016-11-04 11:19:09 +0000581 arm_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
582 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
583
Artem Serovd4cc5b22016-11-04 11:19:09 +0000584 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
585 RestoreLiveRegisters(codegen, locations);
586
587 __ B(GetExitLabel());
588 }
589
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100590 const char* GetDescription() const override { return "LoadStringSlowPathARMVIXL"; }
Artem Serovd4cc5b22016-11-04 11:19:09 +0000591
592 private:
593 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARMVIXL);
594};
595
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100596class TypeCheckSlowPathARMVIXL : public SlowPathCodeARMVIXL {
597 public:
598 TypeCheckSlowPathARMVIXL(HInstruction* instruction, bool is_fatal)
599 : SlowPathCodeARMVIXL(instruction), is_fatal_(is_fatal) {}
600
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100601 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100602 LocationSummary* locations = instruction_->GetLocations();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100603 DCHECK(instruction_->IsCheckCast()
604 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
605
606 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
607 __ Bind(GetEntryLabel());
608
Vladimir Marko87584542017-12-12 17:47:52 +0000609 if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100610 SaveLiveRegisters(codegen, locations);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100611 }
612
613 // We're moving two locations to locations that could overlap, so we need a parallel
614 // move resolver.
615 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100616
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800617 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800618 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100619 DataType::Type::kReference,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800620 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800621 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100622 DataType::Type::kReference);
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100623 if (instruction_->IsInstanceOf()) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100624 arm_codegen->InvokeRuntime(kQuickInstanceofNonTrivial,
625 instruction_,
626 instruction_->GetDexPc(),
627 this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800628 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Artem Serovcfbe9132016-10-14 15:58:56 +0100629 arm_codegen->Move32(locations->Out(), LocationFrom(r0));
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100630 } else {
631 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800632 arm_codegen->InvokeRuntime(kQuickCheckInstanceOf,
633 instruction_,
634 instruction_->GetDexPc(),
635 this);
636 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100637 }
638
639 if (!is_fatal_) {
Artem Serovcfbe9132016-10-14 15:58:56 +0100640 RestoreLiveRegisters(codegen, locations);
641 __ B(GetExitLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100642 }
643 }
644
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100645 const char* GetDescription() const override { return "TypeCheckSlowPathARMVIXL"; }
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100646
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100647 bool IsFatal() const override { return is_fatal_; }
Anton Kirilove28d9ae2016-10-25 18:17:23 +0100648
649 private:
650 const bool is_fatal_;
651
652 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARMVIXL);
653};
654
Scott Wakelingc34dba72016-10-03 10:14:44 +0100655class DeoptimizationSlowPathARMVIXL : public SlowPathCodeARMVIXL {
656 public:
657 explicit DeoptimizationSlowPathARMVIXL(HDeoptimize* instruction)
658 : SlowPathCodeARMVIXL(instruction) {}
659
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100660 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100661 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
662 __ Bind(GetEntryLabel());
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100663 LocationSummary* locations = instruction_->GetLocations();
664 SaveLiveRegisters(codegen, locations);
665 InvokeRuntimeCallingConventionARMVIXL calling_convention;
666 __ Mov(calling_convention.GetRegisterAt(0),
667 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
668
Scott Wakelingc34dba72016-10-03 10:14:44 +0100669 arm_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100670 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
Scott Wakelingc34dba72016-10-03 10:14:44 +0100671 }
672
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100673 const char* GetDescription() const override { return "DeoptimizationSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100674
675 private:
676 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARMVIXL);
677};
678
679class ArraySetSlowPathARMVIXL : public SlowPathCodeARMVIXL {
680 public:
681 explicit ArraySetSlowPathARMVIXL(HInstruction* instruction) : SlowPathCodeARMVIXL(instruction) {}
682
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100683 void EmitNativeCode(CodeGenerator* codegen) override {
Scott Wakelingc34dba72016-10-03 10:14:44 +0100684 LocationSummary* locations = instruction_->GetLocations();
685 __ Bind(GetEntryLabel());
686 SaveLiveRegisters(codegen, locations);
687
688 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100689 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Scott Wakelingc34dba72016-10-03 10:14:44 +0100690 parallel_move.AddMove(
691 locations->InAt(0),
692 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100693 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100694 nullptr);
695 parallel_move.AddMove(
696 locations->InAt(1),
697 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100698 DataType::Type::kInt32,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100699 nullptr);
700 parallel_move.AddMove(
701 locations->InAt(2),
702 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100703 DataType::Type::kReference,
Scott Wakelingc34dba72016-10-03 10:14:44 +0100704 nullptr);
705 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
706
707 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
708 arm_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
709 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
710 RestoreLiveRegisters(codegen, locations);
711 __ B(GetExitLabel());
712 }
713
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100714 const char* GetDescription() const override { return "ArraySetSlowPathARMVIXL"; }
Scott Wakelingc34dba72016-10-03 10:14:44 +0100715
716 private:
717 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARMVIXL);
718};
719
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000720// Slow path generating a read barrier for a heap reference.
721class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL {
722 public:
723 ReadBarrierForHeapReferenceSlowPathARMVIXL(HInstruction* instruction,
724 Location out,
725 Location ref,
726 Location obj,
727 uint32_t offset,
728 Location index)
729 : SlowPathCodeARMVIXL(instruction),
730 out_(out),
731 ref_(ref),
732 obj_(obj),
733 offset_(offset),
734 index_(index) {
735 DCHECK(kEmitCompilerReadBarrier);
736 // If `obj` is equal to `out` or `ref`, it means the initial object
737 // has been overwritten by (or after) the heap object reference load
738 // to be instrumented, e.g.:
739 //
740 // __ LoadFromOffset(kLoadWord, out, out, offset);
741 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
742 //
743 // In that case, we have lost the information about the original
744 // object, and the emitted read barrier cannot work properly.
745 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
746 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
747 }
748
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100749 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000750 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
751 LocationSummary* locations = instruction_->GetLocations();
752 vixl32::Register reg_out = RegisterFrom(out_);
753 DCHECK(locations->CanCall());
754 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
755 DCHECK(instruction_->IsInstanceFieldGet() ||
756 instruction_->IsStaticFieldGet() ||
757 instruction_->IsArrayGet() ||
758 instruction_->IsInstanceOf() ||
759 instruction_->IsCheckCast() ||
Andreas Gamped9911ee2017-03-27 13:27:24 -0700760 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000761 << "Unexpected instruction in read barrier for heap reference slow path: "
762 << instruction_->DebugName();
763 // The read barrier instrumentation of object ArrayGet
764 // instructions does not support the HIntermediateAddress
765 // instruction.
766 DCHECK(!(instruction_->IsArrayGet() &&
767 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
768
769 __ Bind(GetEntryLabel());
770 SaveLiveRegisters(codegen, locations);
771
772 // We may have to change the index's value, but as `index_` is a
773 // constant member (like other "inputs" of this slow path),
774 // introduce a copy of it, `index`.
775 Location index = index_;
776 if (index_.IsValid()) {
777 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
778 if (instruction_->IsArrayGet()) {
779 // Compute the actual memory offset and store it in `index`.
780 vixl32::Register index_reg = RegisterFrom(index_);
781 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_reg.GetCode()));
782 if (codegen->IsCoreCalleeSaveRegister(index_reg.GetCode())) {
783 // We are about to change the value of `index_reg` (see the
Roland Levillain9983e302017-07-14 14:34:22 +0100784 // calls to art::arm::ArmVIXLMacroAssembler::Lsl and
785 // art::arm::ArmVIXLMacroAssembler::Add below), but it has
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000786 // not been saved by the previous call to
787 // art::SlowPathCode::SaveLiveRegisters, as it is a
788 // callee-save register --
789 // art::SlowPathCode::SaveLiveRegisters does not consider
790 // callee-save registers, as it has been designed with the
791 // assumption that callee-save registers are supposed to be
792 // handled by the called function. So, as a callee-save
793 // register, `index_reg` _would_ eventually be saved onto
794 // the stack, but it would be too late: we would have
795 // changed its value earlier. Therefore, we manually save
796 // it here into another freely available register,
797 // `free_reg`, chosen of course among the caller-save
798 // registers (as a callee-save `free_reg` register would
799 // exhibit the same problem).
800 //
801 // Note we could have requested a temporary register from
802 // the register allocator instead; but we prefer not to, as
803 // this is a slow path, and we know we can find a
804 // caller-save register that is available.
805 vixl32::Register free_reg = FindAvailableCallerSaveRegister(codegen);
806 __ Mov(free_reg, index_reg);
807 index_reg = free_reg;
808 index = LocationFrom(index_reg);
809 } else {
810 // The initial register stored in `index_` has already been
811 // saved in the call to art::SlowPathCode::SaveLiveRegisters
812 // (as it is not a callee-save register), so we can freely
813 // use it.
814 }
815 // Shifting the index value contained in `index_reg` by the scale
816 // factor (2) cannot overflow in practice, as the runtime is
817 // unable to allocate object arrays with a size larger than
818 // 2^26 - 1 (that is, 2^28 - 4 bytes).
819 __ Lsl(index_reg, index_reg, TIMES_4);
820 static_assert(
821 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
822 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
823 __ Add(index_reg, index_reg, offset_);
824 } else {
825 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
826 // intrinsics, `index_` is not shifted by a scale factor of 2
827 // (as in the case of ArrayGet), as it is actually an offset
828 // to an object field within an object.
829 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
830 DCHECK(instruction_->GetLocations()->Intrinsified());
831 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
832 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
833 << instruction_->AsInvoke()->GetIntrinsic();
834 DCHECK_EQ(offset_, 0U);
835 DCHECK(index_.IsRegisterPair());
836 // UnsafeGet's offset location is a register pair, the low
837 // part contains the correct offset.
838 index = index_.ToLow();
839 }
840 }
841
842 // We're moving two or three locations to locations that could
843 // overlap, so we need a parallel move resolver.
844 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100845 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000846 parallel_move.AddMove(ref_,
847 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100848 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000849 nullptr);
850 parallel_move.AddMove(obj_,
851 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100852 DataType::Type::kReference,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000853 nullptr);
854 if (index.IsValid()) {
855 parallel_move.AddMove(index,
856 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100857 DataType::Type::kInt32,
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000858 nullptr);
859 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
860 } else {
861 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
862 __ Mov(calling_convention.GetRegisterAt(2), offset_);
863 }
864 arm_codegen->InvokeRuntime(kQuickReadBarrierSlow, instruction_, instruction_->GetDexPc(), this);
865 CheckEntrypointTypes<
866 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
867 arm_codegen->Move32(out_, LocationFrom(r0));
868
869 RestoreLiveRegisters(codegen, locations);
870 __ B(GetExitLabel());
871 }
872
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100873 const char* GetDescription() const override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000874 return "ReadBarrierForHeapReferenceSlowPathARMVIXL";
875 }
876
877 private:
878 vixl32::Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
879 uint32_t ref = RegisterFrom(ref_).GetCode();
880 uint32_t obj = RegisterFrom(obj_).GetCode();
881 for (uint32_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
882 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
883 return vixl32::Register(i);
884 }
885 }
886 // We shall never fail to find a free caller-save register, as
887 // there are more than two core caller-save registers on ARM
888 // (meaning it is possible to find one which is different from
889 // `ref` and `obj`).
890 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
891 LOG(FATAL) << "Could not find a free caller-save register";
892 UNREACHABLE();
893 }
894
895 const Location out_;
896 const Location ref_;
897 const Location obj_;
898 const uint32_t offset_;
899 // An additional location containing an index to an array.
900 // Only used for HArrayGet and the UnsafeGetObject &
901 // UnsafeGetObjectVolatile intrinsics.
902 const Location index_;
903
904 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARMVIXL);
905};
906
907// Slow path generating a read barrier for a GC root.
908class ReadBarrierForRootSlowPathARMVIXL : public SlowPathCodeARMVIXL {
909 public:
910 ReadBarrierForRootSlowPathARMVIXL(HInstruction* instruction, Location out, Location root)
911 : SlowPathCodeARMVIXL(instruction), out_(out), root_(root) {
912 DCHECK(kEmitCompilerReadBarrier);
913 }
914
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100915 void EmitNativeCode(CodeGenerator* codegen) override {
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000916 LocationSummary* locations = instruction_->GetLocations();
917 vixl32::Register reg_out = RegisterFrom(out_);
918 DCHECK(locations->CanCall());
919 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode()));
920 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
921 << "Unexpected instruction in read barrier for GC root slow path: "
922 << instruction_->DebugName();
923
924 __ Bind(GetEntryLabel());
925 SaveLiveRegisters(codegen, locations);
926
927 InvokeRuntimeCallingConventionARMVIXL calling_convention;
928 CodeGeneratorARMVIXL* arm_codegen = down_cast<CodeGeneratorARMVIXL*>(codegen);
929 arm_codegen->Move32(LocationFrom(calling_convention.GetRegisterAt(0)), root_);
930 arm_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
931 instruction_,
932 instruction_->GetDexPc(),
933 this);
934 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
935 arm_codegen->Move32(out_, LocationFrom(r0));
936
937 RestoreLiveRegisters(codegen, locations);
938 __ B(GetExitLabel());
939 }
940
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100941 const char* GetDescription() const override { return "ReadBarrierForRootSlowPathARMVIXL"; }
Anton Kirilovedb2ac32016-11-30 15:14:10 +0000942
943 private:
944 const Location out_;
945 const Location root_;
946
947 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARMVIXL);
948};
Scott Wakelingc34dba72016-10-03 10:14:44 +0100949
Scott Wakelingfe885462016-09-22 10:24:38 +0100950inline vixl32::Condition ARMCondition(IfCondition cond) {
951 switch (cond) {
952 case kCondEQ: return eq;
953 case kCondNE: return ne;
954 case kCondLT: return lt;
955 case kCondLE: return le;
956 case kCondGT: return gt;
957 case kCondGE: return ge;
958 case kCondB: return lo;
959 case kCondBE: return ls;
960 case kCondA: return hi;
961 case kCondAE: return hs;
962 }
963 LOG(FATAL) << "Unreachable";
964 UNREACHABLE();
965}
966
967// Maps signed condition to unsigned condition.
968inline vixl32::Condition ARMUnsignedCondition(IfCondition cond) {
969 switch (cond) {
970 case kCondEQ: return eq;
971 case kCondNE: return ne;
972 // Signed to unsigned.
973 case kCondLT: return lo;
974 case kCondLE: return ls;
975 case kCondGT: return hi;
976 case kCondGE: return hs;
977 // Unsigned remain unchanged.
978 case kCondB: return lo;
979 case kCondBE: return ls;
980 case kCondA: return hi;
981 case kCondAE: return hs;
982 }
983 LOG(FATAL) << "Unreachable";
984 UNREACHABLE();
985}
986
987inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) {
988 // The ARM condition codes can express all the necessary branches, see the
989 // "Meaning (floating-point)" column in the table A8-1 of the ARMv7 reference manual.
990 // There is no dex instruction or HIR that would need the missing conditions
991 // "equal or unordered" or "not equal".
992 switch (cond) {
993 case kCondEQ: return eq;
994 case kCondNE: return ne /* unordered */;
995 case kCondLT: return gt_bias ? cc : lt /* unordered */;
996 case kCondLE: return gt_bias ? ls : le /* unordered */;
997 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
998 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
999 default:
1000 LOG(FATAL) << "UNREACHABLE";
1001 UNREACHABLE();
1002 }
1003}
1004
Anton Kirilov74234da2017-01-13 14:42:47 +00001005inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) {
1006 switch (op_kind) {
1007 case HDataProcWithShifterOp::kASR: return ShiftType::ASR;
1008 case HDataProcWithShifterOp::kLSL: return ShiftType::LSL;
1009 case HDataProcWithShifterOp::kLSR: return ShiftType::LSR;
1010 default:
1011 LOG(FATAL) << "Unexpected op kind " << op_kind;
1012 UNREACHABLE();
1013 }
1014}
1015
Scott Wakelingfe885462016-09-22 10:24:38 +01001016void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const {
1017 stream << vixl32::Register(reg);
1018}
1019
1020void CodeGeneratorARMVIXL::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
1021 stream << vixl32::SRegister(reg);
1022}
1023
Vladimir Markoa0431112018-06-25 09:32:54 +01001024const ArmInstructionSetFeatures& CodeGeneratorARMVIXL::GetInstructionSetFeatures() const {
1025 return *GetCompilerOptions().GetInstructionSetFeatures()->AsArmInstructionSetFeatures();
1026}
1027
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001028static uint32_t ComputeSRegisterListMask(const SRegisterList& regs) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001029 uint32_t mask = 0;
1030 for (uint32_t i = regs.GetFirstSRegister().GetCode();
1031 i <= regs.GetLastSRegister().GetCode();
1032 ++i) {
1033 mask |= (1 << i);
1034 }
1035 return mask;
1036}
1037
Artem Serovd4cc5b22016-11-04 11:19:09 +00001038// Saves the register in the stack. Returns the size taken on stack.
1039size_t CodeGeneratorARMVIXL::SaveCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1040 uint32_t reg_id ATTRIBUTE_UNUSED) {
1041 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001042 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00001043}
1044
1045// Restores the register from the stack. Returns the size taken on stack.
1046size_t CodeGeneratorARMVIXL::RestoreCoreRegister(size_t stack_index ATTRIBUTE_UNUSED,
1047 uint32_t reg_id ATTRIBUTE_UNUSED) {
1048 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001049 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00001050}
1051
1052size_t CodeGeneratorARMVIXL::SaveFloatingPointRegister(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
1058size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATTRIBUTE_UNUSED,
1059 uint32_t reg_id ATTRIBUTE_UNUSED) {
1060 TODO_VIXL32(FATAL);
Elliott Hughesc1896c92018-11-29 11:33:18 -08001061 UNREACHABLE();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01001062}
1063
Anton Kirilov74234da2017-01-13 14:42:47 +00001064static void GenerateDataProcInstruction(HInstruction::InstructionKind kind,
1065 vixl32::Register out,
1066 vixl32::Register first,
1067 const Operand& second,
1068 CodeGeneratorARMVIXL* codegen) {
1069 if (second.IsImmediate() && second.GetImmediate() == 0) {
1070 const Operand in = kind == HInstruction::kAnd
1071 ? Operand(0)
1072 : Operand(first);
1073
1074 __ Mov(out, in);
1075 } else {
1076 switch (kind) {
1077 case HInstruction::kAdd:
1078 __ Add(out, first, second);
1079 break;
1080 case HInstruction::kAnd:
1081 __ And(out, first, second);
1082 break;
1083 case HInstruction::kOr:
1084 __ Orr(out, first, second);
1085 break;
1086 case HInstruction::kSub:
1087 __ Sub(out, first, second);
1088 break;
1089 case HInstruction::kXor:
1090 __ Eor(out, first, second);
1091 break;
1092 default:
1093 LOG(FATAL) << "Unexpected instruction kind: " << kind;
1094 UNREACHABLE();
1095 }
1096 }
1097}
1098
1099static void GenerateDataProc(HInstruction::InstructionKind kind,
1100 const Location& out,
1101 const Location& first,
1102 const Operand& second_lo,
1103 const Operand& second_hi,
1104 CodeGeneratorARMVIXL* codegen) {
1105 const vixl32::Register first_hi = HighRegisterFrom(first);
1106 const vixl32::Register first_lo = LowRegisterFrom(first);
1107 const vixl32::Register out_hi = HighRegisterFrom(out);
1108 const vixl32::Register out_lo = LowRegisterFrom(out);
1109
1110 if (kind == HInstruction::kAdd) {
1111 __ Adds(out_lo, first_lo, second_lo);
1112 __ Adc(out_hi, first_hi, second_hi);
1113 } else if (kind == HInstruction::kSub) {
1114 __ Subs(out_lo, first_lo, second_lo);
1115 __ Sbc(out_hi, first_hi, second_hi);
1116 } else {
1117 GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen);
1118 GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen);
1119 }
1120}
1121
1122static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) {
1123 return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm);
1124}
1125
1126static void GenerateLongDataProc(HDataProcWithShifterOp* instruction,
1127 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001128 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00001129 DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind()));
1130
1131 const LocationSummary* const locations = instruction->GetLocations();
1132 const uint32_t shift_value = instruction->GetShiftAmount();
1133 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
1134 const Location first = locations->InAt(0);
1135 const Location second = locations->InAt(1);
1136 const Location out = locations->Out();
1137 const vixl32::Register first_hi = HighRegisterFrom(first);
1138 const vixl32::Register first_lo = LowRegisterFrom(first);
1139 const vixl32::Register out_hi = HighRegisterFrom(out);
1140 const vixl32::Register out_lo = LowRegisterFrom(out);
1141 const vixl32::Register second_hi = HighRegisterFrom(second);
1142 const vixl32::Register second_lo = LowRegisterFrom(second);
1143 const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind());
1144
1145 if (shift_value >= 32) {
1146 if (shift == ShiftType::LSL) {
1147 GenerateDataProcInstruction(kind,
1148 out_hi,
1149 first_hi,
1150 Operand(second_lo, ShiftType::LSL, shift_value - 32),
1151 codegen);
1152 GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen);
1153 } else if (shift == ShiftType::ASR) {
1154 GenerateDataProc(kind,
1155 out,
1156 first,
1157 GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32),
1158 Operand(second_hi, ShiftType::ASR, 31),
1159 codegen);
1160 } else {
1161 DCHECK_EQ(shift, ShiftType::LSR);
1162 GenerateDataProc(kind,
1163 out,
1164 first,
1165 GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32),
1166 0,
1167 codegen);
1168 }
1169 } else {
1170 DCHECK_GT(shift_value, 1U);
1171 DCHECK_LT(shift_value, 32U);
1172
1173 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1174
1175 if (shift == ShiftType::LSL) {
1176 // We are not doing this for HInstruction::kAdd because the output will require
1177 // Location::kOutputOverlap; not applicable to other cases.
1178 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1179 GenerateDataProcInstruction(kind,
1180 out_hi,
1181 first_hi,
1182 Operand(second_hi, ShiftType::LSL, shift_value),
1183 codegen);
1184 GenerateDataProcInstruction(kind,
1185 out_hi,
1186 out_hi,
1187 Operand(second_lo, ShiftType::LSR, 32 - shift_value),
1188 codegen);
1189 GenerateDataProcInstruction(kind,
1190 out_lo,
1191 first_lo,
1192 Operand(second_lo, ShiftType::LSL, shift_value),
1193 codegen);
1194 } else {
1195 const vixl32::Register temp = temps.Acquire();
1196
1197 __ Lsl(temp, second_hi, shift_value);
1198 __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value));
1199 GenerateDataProc(kind,
1200 out,
1201 first,
1202 Operand(second_lo, ShiftType::LSL, shift_value),
1203 temp,
1204 codegen);
1205 }
1206 } else {
1207 DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR);
1208
1209 // We are not doing this for HInstruction::kAdd because the output will require
1210 // Location::kOutputOverlap; not applicable to other cases.
1211 if (kind == HInstruction::kOr || kind == HInstruction::kXor) {
1212 GenerateDataProcInstruction(kind,
1213 out_lo,
1214 first_lo,
1215 Operand(second_lo, ShiftType::LSR, shift_value),
1216 codegen);
1217 GenerateDataProcInstruction(kind,
1218 out_lo,
1219 out_lo,
1220 Operand(second_hi, ShiftType::LSL, 32 - shift_value),
1221 codegen);
1222 GenerateDataProcInstruction(kind,
1223 out_hi,
1224 first_hi,
1225 Operand(second_hi, shift, shift_value),
1226 codegen);
1227 } else {
1228 const vixl32::Register temp = temps.Acquire();
1229
1230 __ Lsr(temp, second_lo, shift_value);
1231 __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value));
1232 GenerateDataProc(kind,
1233 out,
1234 first,
1235 temp,
1236 Operand(second_hi, shift, shift_value),
1237 codegen);
1238 }
1239 }
1240 }
1241}
1242
Donghui Bai426b49c2016-11-08 14:55:38 +08001243static void GenerateVcmp(HInstruction* instruction, CodeGeneratorARMVIXL* codegen) {
1244 const Location rhs_loc = instruction->GetLocations()->InAt(1);
1245 if (rhs_loc.IsConstant()) {
1246 // 0.0 is the only immediate that can be encoded directly in
1247 // a VCMP instruction.
1248 //
1249 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
1250 // specify that in a floating-point comparison, positive zero
1251 // and negative zero are considered equal, so we can use the
1252 // literal 0.0 for both cases here.
1253 //
1254 // Note however that some methods (Float.equal, Float.compare,
1255 // Float.compareTo, Double.equal, Double.compare,
1256 // Double.compareTo, Math.max, Math.min, StrictMath.max,
1257 // StrictMath.min) consider 0.0 to be (strictly) greater than
1258 // -0.0. So if we ever translate calls to these methods into a
1259 // HCompare instruction, we must handle the -0.0 case with
1260 // care here.
1261 DCHECK(rhs_loc.GetConstant()->IsArithmeticZero());
1262
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001263 const DataType::Type type = instruction->InputAt(0)->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001264
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001265 if (type == DataType::Type::kFloat32) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001266 __ Vcmp(F32, InputSRegisterAt(instruction, 0), 0.0);
1267 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001268 DCHECK_EQ(type, DataType::Type::kFloat64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001269 __ Vcmp(F64, InputDRegisterAt(instruction, 0), 0.0);
1270 }
1271 } else {
1272 __ Vcmp(InputVRegisterAt(instruction, 0), InputVRegisterAt(instruction, 1));
1273 }
1274}
1275
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001276static int64_t AdjustConstantForCondition(int64_t value,
1277 IfCondition* condition,
1278 IfCondition* opposite) {
1279 if (value == 1) {
1280 if (*condition == kCondB) {
1281 value = 0;
1282 *condition = kCondEQ;
1283 *opposite = kCondNE;
1284 } else if (*condition == kCondAE) {
1285 value = 0;
1286 *condition = kCondNE;
1287 *opposite = kCondEQ;
1288 }
1289 } else if (value == -1) {
1290 if (*condition == kCondGT) {
1291 value = 0;
1292 *condition = kCondGE;
1293 *opposite = kCondLT;
1294 } else if (*condition == kCondLE) {
1295 value = 0;
1296 *condition = kCondLT;
1297 *opposite = kCondGE;
1298 }
1299 }
1300
1301 return value;
1302}
1303
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001304static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTestConstant(
1305 HCondition* condition,
1306 bool invert,
1307 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001308 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001309
1310 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001311 IfCondition cond = condition->GetCondition();
1312 IfCondition opposite = condition->GetOppositeCondition();
1313
1314 if (invert) {
1315 std::swap(cond, opposite);
1316 }
1317
1318 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001319 const Location left = locations->InAt(0);
1320 const Location right = locations->InAt(1);
1321
1322 DCHECK(right.IsConstant());
1323
1324 const vixl32::Register left_high = HighRegisterFrom(left);
1325 const vixl32::Register left_low = LowRegisterFrom(left);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001326 int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right), &cond, &opposite);
1327 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1328
1329 // Comparisons against 0 are common enough to deserve special attention.
1330 if (value == 0) {
1331 switch (cond) {
1332 case kCondNE:
1333 // x > 0 iff x != 0 when the comparison is unsigned.
1334 case kCondA:
1335 ret = std::make_pair(ne, eq);
1336 FALLTHROUGH_INTENDED;
1337 case kCondEQ:
1338 // x <= 0 iff x == 0 when the comparison is unsigned.
1339 case kCondBE:
1340 __ Orrs(temps.Acquire(), left_low, left_high);
1341 return ret;
1342 case kCondLT:
1343 case kCondGE:
1344 __ Cmp(left_high, 0);
1345 return std::make_pair(ARMCondition(cond), ARMCondition(opposite));
1346 // Trivially true or false.
1347 case kCondB:
1348 ret = std::make_pair(ne, eq);
1349 FALLTHROUGH_INTENDED;
1350 case kCondAE:
1351 __ Cmp(left_low, left_low);
1352 return ret;
1353 default:
1354 break;
1355 }
1356 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001357
1358 switch (cond) {
1359 case kCondEQ:
1360 case kCondNE:
1361 case kCondB:
1362 case kCondBE:
1363 case kCondA:
1364 case kCondAE: {
Anton Kirilov23b752b2017-07-20 14:40:44 +01001365 const uint32_t value_low = Low32Bits(value);
1366 Operand operand_low(value_low);
1367
Donghui Bai426b49c2016-11-08 14:55:38 +08001368 __ Cmp(left_high, High32Bits(value));
1369
Anton Kirilov23b752b2017-07-20 14:40:44 +01001370 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1371 // we must ensure that the operands corresponding to the least significant
1372 // halves of the inputs fit into a 16-bit CMP encoding.
1373 if (!left_low.IsLow() || !IsUint<8>(value_low)) {
1374 operand_low = Operand(temps.Acquire());
1375 __ Mov(LeaveFlags, operand_low.GetBaseRegister(), value_low);
1376 }
1377
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001378 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001379 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1380 2 * vixl32::k16BitT32InstructionSizeInBytes,
1381 CodeBufferCheckScope::kExactSize);
1382
1383 __ it(eq);
Anton Kirilov23b752b2017-07-20 14:40:44 +01001384 __ cmp(eq, left_low, operand_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001385 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001386 break;
1387 }
1388 case kCondLE:
1389 case kCondGT:
1390 // Trivially true or false.
1391 if (value == std::numeric_limits<int64_t>::max()) {
1392 __ Cmp(left_low, left_low);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001393 ret = cond == kCondLE ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
Donghui Bai426b49c2016-11-08 14:55:38 +08001394 break;
1395 }
1396
1397 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001398 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001399 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001400 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001401 } else {
1402 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001403 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001404 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001405 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001406 }
1407
1408 value++;
1409 FALLTHROUGH_INTENDED;
1410 case kCondGE:
1411 case kCondLT: {
Donghui Bai426b49c2016-11-08 14:55:38 +08001412 __ Cmp(left_low, Low32Bits(value));
1413 __ Sbcs(temps.Acquire(), left_high, High32Bits(value));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001414 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001415 break;
1416 }
1417 default:
1418 LOG(FATAL) << "Unreachable";
1419 UNREACHABLE();
1420 }
1421
1422 return ret;
1423}
1424
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001425static std::pair<vixl32::Condition, vixl32::Condition> GenerateLongTest(
1426 HCondition* condition,
1427 bool invert,
1428 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001429 DCHECK_EQ(condition->GetLeft()->GetType(), DataType::Type::kInt64);
Donghui Bai426b49c2016-11-08 14:55:38 +08001430
1431 const LocationSummary* const locations = condition->GetLocations();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001432 IfCondition cond = condition->GetCondition();
1433 IfCondition opposite = condition->GetOppositeCondition();
1434
1435 if (invert) {
1436 std::swap(cond, opposite);
1437 }
1438
1439 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001440 Location left = locations->InAt(0);
1441 Location right = locations->InAt(1);
1442
1443 DCHECK(right.IsRegisterPair());
1444
1445 switch (cond) {
1446 case kCondEQ:
1447 case kCondNE:
1448 case kCondB:
1449 case kCondBE:
1450 case kCondA:
1451 case kCondAE: {
1452 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right));
1453
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001454 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08001455 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1456 2 * vixl32::k16BitT32InstructionSizeInBytes,
1457 CodeBufferCheckScope::kExactSize);
1458
1459 __ it(eq);
1460 __ cmp(eq, LowRegisterFrom(left), LowRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001461 ret = std::make_pair(ARMUnsignedCondition(cond), ARMUnsignedCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001462 break;
1463 }
1464 case kCondLE:
1465 case kCondGT:
1466 if (cond == kCondLE) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001467 DCHECK_EQ(opposite, kCondGT);
Donghui Bai426b49c2016-11-08 14:55:38 +08001468 cond = kCondGE;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001469 opposite = kCondLT;
Donghui Bai426b49c2016-11-08 14:55:38 +08001470 } else {
1471 DCHECK_EQ(cond, kCondGT);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001472 DCHECK_EQ(opposite, kCondLE);
Donghui Bai426b49c2016-11-08 14:55:38 +08001473 cond = kCondLT;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001474 opposite = kCondGE;
Donghui Bai426b49c2016-11-08 14:55:38 +08001475 }
1476
1477 std::swap(left, right);
1478 FALLTHROUGH_INTENDED;
1479 case kCondGE:
1480 case kCondLT: {
1481 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1482
1483 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right));
1484 __ Sbcs(temps.Acquire(), HighRegisterFrom(left), HighRegisterFrom(right));
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001485 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001486 break;
1487 }
1488 default:
1489 LOG(FATAL) << "Unreachable";
1490 UNREACHABLE();
1491 }
1492
1493 return ret;
1494}
1495
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001496static std::pair<vixl32::Condition, vixl32::Condition> GenerateTest(HCondition* condition,
1497 bool invert,
1498 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001499 const DataType::Type type = condition->GetLeft()->GetType();
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001500 IfCondition cond = condition->GetCondition();
1501 IfCondition opposite = condition->GetOppositeCondition();
1502 std::pair<vixl32::Condition, vixl32::Condition> ret(eq, ne);
Donghui Bai426b49c2016-11-08 14:55:38 +08001503
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001504 if (invert) {
1505 std::swap(cond, opposite);
1506 }
Donghui Bai426b49c2016-11-08 14:55:38 +08001507
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001508 if (type == DataType::Type::kInt64) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001509 ret = condition->GetLocations()->InAt(1).IsConstant()
1510 ? GenerateLongTestConstant(condition, invert, codegen)
1511 : GenerateLongTest(condition, invert, codegen);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001512 } else if (DataType::IsFloatingPointType(type)) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001513 GenerateVcmp(condition, codegen);
1514 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
1515 ret = std::make_pair(ARMFPCondition(cond, condition->IsGtBias()),
1516 ARMFPCondition(opposite, condition->IsGtBias()));
Donghui Bai426b49c2016-11-08 14:55:38 +08001517 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001518 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00001519 __ Cmp(InputRegisterAt(condition, 0), InputOperandAt(condition, 1));
1520 ret = std::make_pair(ARMCondition(cond), ARMCondition(opposite));
Donghui Bai426b49c2016-11-08 14:55:38 +08001521 }
1522
1523 return ret;
1524}
1525
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001526static void GenerateConditionGeneric(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001527 const vixl32::Register out = OutputRegister(cond);
1528 const auto condition = GenerateTest(cond, false, codegen);
1529
1530 __ Mov(LeaveFlags, out, 0);
1531
1532 if (out.IsLow()) {
1533 // We use the scope because of the IT block that follows.
1534 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1535 2 * vixl32::k16BitT32InstructionSizeInBytes,
1536 CodeBufferCheckScope::kExactSize);
1537
1538 __ it(condition.first);
1539 __ mov(condition.first, out, 1);
1540 } else {
1541 vixl32::Label done_label;
1542 vixl32::Label* const final_label = codegen->GetFinalLabel(cond, &done_label);
1543
Andreas Gampe3db70682018-12-26 15:12:03 -08001544 __ B(condition.second, final_label, /* is_far_target= */ false);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001545 __ Mov(out, 1);
1546
1547 if (done_label.IsReferenced()) {
1548 __ Bind(&done_label);
1549 }
1550 }
1551}
1552
1553static void GenerateEqualLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001554 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001555
1556 const LocationSummary* const locations = cond->GetLocations();
1557 IfCondition condition = cond->GetCondition();
1558 const vixl32::Register out = OutputRegister(cond);
1559 const Location left = locations->InAt(0);
1560 const Location right = locations->InAt(1);
1561 vixl32::Register left_high = HighRegisterFrom(left);
1562 vixl32::Register left_low = LowRegisterFrom(left);
1563 vixl32::Register temp;
1564 UseScratchRegisterScope temps(codegen->GetVIXLAssembler());
1565
1566 if (right.IsConstant()) {
1567 IfCondition opposite = cond->GetOppositeCondition();
1568 const int64_t value = AdjustConstantForCondition(Int64ConstantFrom(right),
1569 &condition,
1570 &opposite);
1571 Operand right_high = High32Bits(value);
1572 Operand right_low = Low32Bits(value);
1573
1574 // The output uses Location::kNoOutputOverlap.
1575 if (out.Is(left_high)) {
1576 std::swap(left_low, left_high);
1577 std::swap(right_low, right_high);
1578 }
1579
1580 __ Sub(out, left_low, right_low);
1581 temp = temps.Acquire();
1582 __ Sub(temp, left_high, right_high);
1583 } else {
1584 DCHECK(right.IsRegisterPair());
1585 temp = temps.Acquire();
1586 __ Sub(temp, left_high, HighRegisterFrom(right));
1587 __ Sub(out, left_low, LowRegisterFrom(right));
1588 }
1589
1590 // Need to check after calling AdjustConstantForCondition().
1591 DCHECK(condition == kCondEQ || condition == kCondNE) << condition;
1592
1593 if (condition == kCondNE && out.IsLow()) {
1594 __ Orrs(out, out, temp);
1595
1596 // We use the scope because of the IT block that follows.
1597 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1598 2 * vixl32::k16BitT32InstructionSizeInBytes,
1599 CodeBufferCheckScope::kExactSize);
1600
1601 __ it(ne);
1602 __ mov(ne, out, 1);
1603 } else {
1604 __ Orr(out, out, temp);
1605 codegen->GenerateConditionWithZero(condition, out, out, temp);
1606 }
1607}
1608
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001609static void GenerateConditionLong(HCondition* cond, CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001610 DCHECK_EQ(cond->GetLeft()->GetType(), DataType::Type::kInt64);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001611
1612 const LocationSummary* const locations = cond->GetLocations();
1613 IfCondition condition = cond->GetCondition();
1614 const vixl32::Register out = OutputRegister(cond);
1615 const Location left = locations->InAt(0);
1616 const Location right = locations->InAt(1);
1617
1618 if (right.IsConstant()) {
1619 IfCondition opposite = cond->GetOppositeCondition();
1620
1621 // Comparisons against 0 are common enough to deserve special attention.
1622 if (AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite) == 0) {
1623 switch (condition) {
1624 case kCondNE:
1625 case kCondA:
1626 if (out.IsLow()) {
1627 // We only care if both input registers are 0 or not.
1628 __ Orrs(out, LowRegisterFrom(left), HighRegisterFrom(left));
1629
1630 // We use the scope because of the IT block that follows.
1631 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1632 2 * vixl32::k16BitT32InstructionSizeInBytes,
1633 CodeBufferCheckScope::kExactSize);
1634
1635 __ it(ne);
1636 __ mov(ne, out, 1);
1637 return;
1638 }
1639
1640 FALLTHROUGH_INTENDED;
1641 case kCondEQ:
1642 case kCondBE:
1643 // We only care if both input registers are 0 or not.
1644 __ Orr(out, LowRegisterFrom(left), HighRegisterFrom(left));
1645 codegen->GenerateConditionWithZero(condition, out, out);
1646 return;
1647 case kCondLT:
1648 case kCondGE:
1649 // We only care about the sign bit.
1650 FALLTHROUGH_INTENDED;
1651 case kCondAE:
1652 case kCondB:
1653 codegen->GenerateConditionWithZero(condition, out, HighRegisterFrom(left));
1654 return;
1655 case kCondLE:
1656 case kCondGT:
1657 default:
1658 break;
1659 }
1660 }
1661 }
1662
Anton Kirilov23b752b2017-07-20 14:40:44 +01001663 // If `out` is a low register, then the GenerateConditionGeneric()
1664 // function generates a shorter code sequence that is still branchless.
1665 if ((condition == kCondEQ || condition == kCondNE) && !out.IsLow()) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001666 GenerateEqualLong(cond, codegen);
1667 return;
1668 }
1669
Anton Kirilov23b752b2017-07-20 14:40:44 +01001670 GenerateConditionGeneric(cond, codegen);
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001671}
1672
Roland Levillain6d729a72017-06-30 18:34:01 +01001673static void GenerateConditionIntegralOrNonPrimitive(HCondition* cond,
1674 CodeGeneratorARMVIXL* codegen) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001675 const DataType::Type type = cond->GetLeft()->GetType();
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001676
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001677 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001678
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001679 if (type == DataType::Type::kInt64) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01001680 GenerateConditionLong(cond, codegen);
1681 return;
1682 }
1683
1684 IfCondition condition = cond->GetCondition();
1685 vixl32::Register in = InputRegisterAt(cond, 0);
1686 const vixl32::Register out = OutputRegister(cond);
1687 const Location right = cond->GetLocations()->InAt(1);
1688 int64_t value;
1689
1690 if (right.IsConstant()) {
1691 IfCondition opposite = cond->GetOppositeCondition();
1692
1693 value = AdjustConstantForCondition(Int64ConstantFrom(right), &condition, &opposite);
1694
1695 // Comparisons against 0 are common enough to deserve special attention.
1696 if (value == 0) {
1697 switch (condition) {
1698 case kCondNE:
1699 case kCondA:
1700 if (out.IsLow() && out.Is(in)) {
1701 __ Cmp(out, 0);
1702
1703 // We use the scope because of the IT block that follows.
1704 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1705 2 * vixl32::k16BitT32InstructionSizeInBytes,
1706 CodeBufferCheckScope::kExactSize);
1707
1708 __ it(ne);
1709 __ mov(ne, out, 1);
1710 return;
1711 }
1712
1713 FALLTHROUGH_INTENDED;
1714 case kCondEQ:
1715 case kCondBE:
1716 case kCondLT:
1717 case kCondGE:
1718 case kCondAE:
1719 case kCondB:
1720 codegen->GenerateConditionWithZero(condition, out, in);
1721 return;
1722 case kCondLE:
1723 case kCondGT:
1724 default:
1725 break;
1726 }
1727 }
1728 }
1729
1730 if (condition == kCondEQ || condition == kCondNE) {
1731 Operand operand(0);
1732
1733 if (right.IsConstant()) {
1734 operand = Operand::From(value);
1735 } else if (out.Is(RegisterFrom(right))) {
1736 // Avoid 32-bit instructions if possible.
1737 operand = InputOperandAt(cond, 0);
1738 in = RegisterFrom(right);
1739 } else {
1740 operand = InputOperandAt(cond, 1);
1741 }
1742
1743 if (condition == kCondNE && out.IsLow()) {
1744 __ Subs(out, in, operand);
1745
1746 // We use the scope because of the IT block that follows.
1747 ExactAssemblyScope guard(codegen->GetVIXLAssembler(),
1748 2 * vixl32::k16BitT32InstructionSizeInBytes,
1749 CodeBufferCheckScope::kExactSize);
1750
1751 __ it(ne);
1752 __ mov(ne, out, 1);
1753 } else {
1754 __ Sub(out, in, operand);
1755 codegen->GenerateConditionWithZero(condition, out, out);
1756 }
1757
1758 return;
1759 }
1760
1761 GenerateConditionGeneric(cond, codegen);
1762}
1763
Donghui Bai426b49c2016-11-08 14:55:38 +08001764static bool CanEncodeConstantAs8BitImmediate(HConstant* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001765 const DataType::Type type = constant->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08001766 bool ret = false;
1767
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001768 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Donghui Bai426b49c2016-11-08 14:55:38 +08001769
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001770 if (type == DataType::Type::kInt64) {
Donghui Bai426b49c2016-11-08 14:55:38 +08001771 const uint64_t value = Uint64ConstantFrom(constant);
1772
1773 ret = IsUint<8>(Low32Bits(value)) && IsUint<8>(High32Bits(value));
1774 } else {
1775 ret = IsUint<8>(Int32ConstantFrom(constant));
1776 }
1777
1778 return ret;
1779}
1780
1781static Location Arm8BitEncodableConstantOrRegister(HInstruction* constant) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001782 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Donghui Bai426b49c2016-11-08 14:55:38 +08001783
1784 if (constant->IsConstant() && CanEncodeConstantAs8BitImmediate(constant->AsConstant())) {
1785 return Location::ConstantLocation(constant->AsConstant());
1786 }
1787
1788 return Location::RequiresRegister();
1789}
1790
1791static bool CanGenerateConditionalMove(const Location& out, const Location& src) {
1792 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
1793 // we check that we are not dealing with floating-point output (there is no
1794 // 16-bit VMOV encoding).
1795 if (!out.IsRegister() && !out.IsRegisterPair()) {
1796 return false;
1797 }
1798
1799 // For constants, we also check that the output is in one or two low registers,
1800 // and that the constants fit in an 8-bit unsigned integer, so that a 16-bit
1801 // MOV encoding can be used.
1802 if (src.IsConstant()) {
1803 if (!CanEncodeConstantAs8BitImmediate(src.GetConstant())) {
1804 return false;
1805 }
1806
1807 if (out.IsRegister()) {
1808 if (!RegisterFrom(out).IsLow()) {
1809 return false;
1810 }
1811 } else {
1812 DCHECK(out.IsRegisterPair());
1813
1814 if (!HighRegisterFrom(out).IsLow()) {
1815 return false;
1816 }
1817 }
1818 }
1819
1820 return true;
1821}
1822
Scott Wakelingfe885462016-09-22 10:24:38 +01001823#undef __
1824
Donghui Bai426b49c2016-11-08 14:55:38 +08001825vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction,
1826 vixl32::Label* final_label) {
1827 DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck());
Anton Kirilov6f644202017-02-27 18:29:45 +00001828 DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall());
Donghui Bai426b49c2016-11-08 14:55:38 +08001829
1830 const HBasicBlock* const block = instruction->GetBlock();
1831 const HLoopInformation* const info = block->GetLoopInformation();
1832 HInstruction* const next = instruction->GetNext();
1833
1834 // Avoid a branch to a branch.
1835 if (next->IsGoto() && (info == nullptr ||
1836 !info->IsBackEdge(*block) ||
1837 !info->HasSuspendCheck())) {
1838 final_label = GetLabelOf(next->AsGoto()->GetSuccessor());
1839 }
1840
1841 return final_label;
1842}
1843
Scott Wakelingfe885462016-09-22 10:24:38 +01001844CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph,
Scott Wakelingfe885462016-09-22 10:24:38 +01001845 const CompilerOptions& compiler_options,
1846 OptimizingCompilerStats* stats)
1847 : CodeGenerator(graph,
1848 kNumberOfCoreRegisters,
1849 kNumberOfSRegisters,
1850 kNumberOfRegisterPairs,
1851 kCoreCalleeSaves.GetList(),
Scott Wakelinga7812ae2016-10-17 10:03:36 +01001852 ComputeSRegisterListMask(kFpuCalleeSaves),
Scott Wakelingfe885462016-09-22 10:24:38 +01001853 compiler_options,
1854 stats),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001855 block_labels_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1856 jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Scott Wakelingfe885462016-09-22 10:24:38 +01001857 location_builder_(graph, this),
1858 instruction_visitor_(graph, this),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001859 move_resolver_(graph->GetAllocator(), this),
1860 assembler_(graph->GetAllocator()),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001861 boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001862 method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001863 boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001864 type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001865 boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001866 string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko2d06e022019-07-08 15:45:19 +01001867 boot_image_other_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markof6675082019-05-17 12:05:28 +01001868 call_entrypoint_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001869 baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markof6675082019-05-17 12:05:28 +01001870 uint32_literals_(std::less<uint32_t>(),
1871 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00001872 jit_string_patches_(StringReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001873 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Artem Serovc5fcb442016-12-02 19:19:58 +00001874 jit_class_patches_(TypeReferenceValueComparator(),
Vladimir Marko966b46f2018-08-03 10:20:19 +00001875 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1876 jit_baker_read_barrier_slow_paths_(std::less<uint32_t>(),
1877 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
Scott Wakelingfe885462016-09-22 10:24:38 +01001878 // Always save the LR register to mimic Quick.
1879 AddAllocatedRegister(Location::RegisterLocation(LR));
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00001880 // Give D30 and D31 as scratch register to VIXL. The register allocator only works on
1881 // S0-S31, which alias to D0-D15.
1882 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d31);
1883 GetVIXLAssembler()->GetScratchVRegisterList()->Combine(d30);
Scott Wakelingfe885462016-09-22 10:24:38 +01001884}
1885
Artem Serov551b28f2016-10-18 19:11:30 +01001886void JumpTableARMVIXL::EmitTable(CodeGeneratorARMVIXL* codegen) {
1887 uint32_t num_entries = switch_instr_->GetNumEntries();
1888 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1889
1890 // We are about to use the assembler to place literals directly. Make sure we have enough
Scott Wakelingb77051e2016-11-21 19:46:00 +00001891 // underlying code buffer and we have generated a jump table of the right size, using
1892 // codegen->GetVIXLAssembler()->GetBuffer().Align();
Artem Serov0fb37192016-12-06 18:13:40 +00001893 ExactAssemblyScope aas(codegen->GetVIXLAssembler(),
1894 num_entries * sizeof(int32_t),
1895 CodeBufferCheckScope::kMaximumSize);
Artem Serov551b28f2016-10-18 19:11:30 +01001896 // TODO(VIXL): Check that using lower case bind is fine here.
1897 codegen->GetVIXLAssembler()->bind(&table_start_);
Artem Serov09a940d2016-11-11 16:15:11 +00001898 for (uint32_t i = 0; i < num_entries; i++) {
1899 codegen->GetVIXLAssembler()->place(bb_addresses_[i].get());
1900 }
1901}
1902
1903void JumpTableARMVIXL::FixTable(CodeGeneratorARMVIXL* codegen) {
1904 uint32_t num_entries = switch_instr_->GetNumEntries();
1905 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
1906
Artem Serov551b28f2016-10-18 19:11:30 +01001907 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
1908 for (uint32_t i = 0; i < num_entries; i++) {
1909 vixl32::Label* target_label = codegen->GetLabelOf(successors[i]);
1910 DCHECK(target_label->IsBound());
1911 int32_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
1912 // When doing BX to address we need to have lower bit set to 1 in T32.
1913 if (codegen->GetVIXLAssembler()->IsUsingT32()) {
1914 jump_offset++;
1915 }
1916 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
1917 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
Artem Serov09a940d2016-11-11 16:15:11 +00001918
Scott Wakelingb77051e2016-11-21 19:46:00 +00001919 bb_addresses_[i].get()->UpdateValue(jump_offset, codegen->GetVIXLAssembler()->GetBuffer());
Artem Serov551b28f2016-10-18 19:11:30 +01001920 }
1921}
1922
Artem Serov09a940d2016-11-11 16:15:11 +00001923void CodeGeneratorARMVIXL::FixJumpTables() {
Artem Serov551b28f2016-10-18 19:11:30 +01001924 for (auto&& jump_table : jump_tables_) {
Artem Serov09a940d2016-11-11 16:15:11 +00001925 jump_table->FixTable(this);
Artem Serov551b28f2016-10-18 19:11:30 +01001926 }
1927}
1928
Andreas Gampeca620d72016-11-08 08:09:33 -08001929#define __ reinterpret_cast<ArmVIXLAssembler*>(GetAssembler())->GetVIXLAssembler()-> // NOLINT
Scott Wakelingfe885462016-09-22 10:24:38 +01001930
1931void CodeGeneratorARMVIXL::Finalize(CodeAllocator* allocator) {
Artem Serov09a940d2016-11-11 16:15:11 +00001932 FixJumpTables();
Vladimir Marko966b46f2018-08-03 10:20:19 +00001933
1934 // Emit JIT baker read barrier slow paths.
Vladimir Marko695348f2020-05-19 14:42:02 +01001935 DCHECK(GetCompilerOptions().IsJitCompiler() || jit_baker_read_barrier_slow_paths_.empty());
Vladimir Marko966b46f2018-08-03 10:20:19 +00001936 for (auto& entry : jit_baker_read_barrier_slow_paths_) {
1937 uint32_t encoded_data = entry.first;
1938 vixl::aarch32::Label* slow_path_entry = &entry.second.label;
1939 __ Bind(slow_path_entry);
Andreas Gampe3db70682018-12-26 15:12:03 -08001940 CompileBakerReadBarrierThunk(*GetAssembler(), encoded_data, /* debug_name= */ nullptr);
Vladimir Marko966b46f2018-08-03 10:20:19 +00001941 }
1942
Scott Wakelingfe885462016-09-22 10:24:38 +01001943 GetAssembler()->FinalizeCode();
1944 CodeGenerator::Finalize(allocator);
Vladimir Markoca1e0382018-04-11 09:58:41 +00001945
1946 // Verify Baker read barrier linker patches.
1947 if (kIsDebugBuild) {
1948 ArrayRef<const uint8_t> code = allocator->GetMemory();
1949 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
1950 DCHECK(info.label.IsBound());
1951 uint32_t literal_offset = info.label.GetLocation();
1952 DCHECK_ALIGNED(literal_offset, 2u);
1953
1954 auto GetInsn16 = [&code](uint32_t offset) {
1955 DCHECK_ALIGNED(offset, 2u);
1956 return (static_cast<uint32_t>(code[offset + 0]) << 0) +
1957 (static_cast<uint32_t>(code[offset + 1]) << 8);
1958 };
1959 auto GetInsn32 = [=](uint32_t offset) {
1960 return (GetInsn16(offset) << 16) + (GetInsn16(offset + 2u) << 0);
1961 };
1962
1963 uint32_t encoded_data = info.custom_data;
1964 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
1965 // Check that the next instruction matches the expected LDR.
1966 switch (kind) {
1967 case BakerReadBarrierKind::kField: {
1968 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
1969 if (width == BakerReadBarrierWidth::kWide) {
1970 DCHECK_GE(code.size() - literal_offset, 8u);
1971 uint32_t next_insn = GetInsn32(literal_offset + 4u);
1972 // LDR (immediate), encoding T3, with correct base_reg.
1973 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
1974 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
1975 CHECK_EQ(next_insn & 0xffff0000u, 0xf8d00000u | (base_reg << 16));
1976 } else {
1977 DCHECK_GE(code.size() - literal_offset, 6u);
1978 uint32_t next_insn = GetInsn16(literal_offset + 4u);
1979 // LDR (immediate), encoding T1, with correct base_reg.
1980 CheckValidReg(next_insn & 0x7u); // Check destination register.
1981 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
1982 CHECK_EQ(next_insn & 0xf838u, 0x6800u | (base_reg << 3));
1983 }
1984 break;
1985 }
1986 case BakerReadBarrierKind::kArray: {
1987 DCHECK_GE(code.size() - literal_offset, 8u);
1988 uint32_t next_insn = GetInsn32(literal_offset + 4u);
1989 // LDR (register) with correct base_reg, S=1 and option=011 (LDR Wt, [Xn, Xm, LSL #2]).
1990 CheckValidReg((next_insn >> 12) & 0xfu); // Check destination register.
1991 const uint32_t base_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
1992 CHECK_EQ(next_insn & 0xffff0ff0u, 0xf8500020u | (base_reg << 16));
1993 CheckValidReg(next_insn & 0xf); // Check index register
1994 break;
1995 }
1996 case BakerReadBarrierKind::kGcRoot: {
1997 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
1998 if (width == BakerReadBarrierWidth::kWide) {
1999 DCHECK_GE(literal_offset, 4u);
2000 uint32_t prev_insn = GetInsn32(literal_offset - 4u);
Vladimir Markof28be432018-08-14 12:20:51 +00002001 // LDR (immediate), encoding T3, with correct root_reg.
Vladimir Markoca1e0382018-04-11 09:58:41 +00002002 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
Vladimir Markof28be432018-08-14 12:20:51 +00002003 CHECK_EQ(prev_insn & 0xfff0f000u, 0xf8d00000u | (root_reg << 12));
Vladimir Markoca1e0382018-04-11 09:58:41 +00002004 } else {
2005 DCHECK_GE(literal_offset, 2u);
2006 uint32_t prev_insn = GetInsn16(literal_offset - 2u);
2007 // LDR (immediate), encoding T1, with correct root_reg.
2008 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2009 CHECK_EQ(prev_insn & 0xf807u, 0x6800u | root_reg);
2010 }
2011 break;
2012 }
Vladimir Markod887ed82018-08-14 13:52:12 +00002013 case BakerReadBarrierKind::kUnsafeCas: {
2014 DCHECK_GE(literal_offset, 4u);
2015 uint32_t prev_insn = GetInsn32(literal_offset - 4u);
2016 // ADD (register), encoding T3, with correct root_reg.
2017 const uint32_t root_reg = BakerReadBarrierFirstRegField::Decode(encoded_data);
2018 CHECK_EQ(prev_insn & 0xfff0fff0u, 0xeb000000u | (root_reg << 8));
2019 break;
2020 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00002021 default:
2022 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
2023 UNREACHABLE();
2024 }
2025 }
2026 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002027}
2028
2029void CodeGeneratorARMVIXL::SetupBlockedRegisters() const {
Scott Wakelingfe885462016-09-22 10:24:38 +01002030 // Stack register, LR and PC are always reserved.
2031 blocked_core_registers_[SP] = true;
2032 blocked_core_registers_[LR] = true;
2033 blocked_core_registers_[PC] = true;
2034
Roland Levillain6d729a72017-06-30 18:34:01 +01002035 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
2036 // Reserve marking register.
2037 blocked_core_registers_[MR] = true;
2038 }
2039
Scott Wakelingfe885462016-09-22 10:24:38 +01002040 // Reserve thread register.
2041 blocked_core_registers_[TR] = true;
2042
2043 // Reserve temp register.
2044 blocked_core_registers_[IP] = true;
2045
2046 if (GetGraph()->IsDebuggable()) {
2047 // Stubs do not save callee-save floating point registers. If the graph
2048 // is debuggable, we need to deal with these registers differently. For
2049 // now, just block them.
2050 for (uint32_t i = kFpuCalleeSaves.GetFirstSRegister().GetCode();
2051 i <= kFpuCalleeSaves.GetLastSRegister().GetCode();
2052 ++i) {
2053 blocked_fpu_registers_[i] = true;
2054 }
2055 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002056}
2057
Scott Wakelingfe885462016-09-22 10:24:38 +01002058InstructionCodeGeneratorARMVIXL::InstructionCodeGeneratorARMVIXL(HGraph* graph,
2059 CodeGeneratorARMVIXL* codegen)
2060 : InstructionCodeGenerator(graph, codegen),
2061 assembler_(codegen->GetAssembler()),
2062 codegen_(codegen) {}
2063
2064void CodeGeneratorARMVIXL::ComputeSpillMask() {
2065 core_spill_mask_ = allocated_registers_.GetCoreRegisters() & core_callee_save_mask_;
Vladimir Marko460f0542019-07-04 14:02:08 +01002066 DCHECK_NE(core_spill_mask_ & (1u << kLrCode), 0u)
2067 << "At least the return address register must be saved";
2068 // 16-bit PUSH/POP (T1) can save/restore just the LR/PC.
2069 DCHECK(GetVIXLAssembler()->IsUsingT32());
Scott Wakelingfe885462016-09-22 10:24:38 +01002070 fpu_spill_mask_ = allocated_registers_.GetFloatingPointRegisters() & fpu_callee_save_mask_;
2071 // We use vpush and vpop for saving and restoring floating point registers, which take
2072 // a SRegister and the number of registers to save/restore after that SRegister. We
2073 // therefore update the `fpu_spill_mask_` to also contain those registers not allocated,
2074 // but in the range.
2075 if (fpu_spill_mask_ != 0) {
2076 uint32_t least_significant_bit = LeastSignificantBit(fpu_spill_mask_);
2077 uint32_t most_significant_bit = MostSignificantBit(fpu_spill_mask_);
2078 for (uint32_t i = least_significant_bit + 1 ; i < most_significant_bit; ++i) {
2079 fpu_spill_mask_ |= (1 << i);
2080 }
2081 }
2082}
2083
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002084void CodeGeneratorARMVIXL::MaybeIncrementHotness(bool is_frame_entry) {
2085 if (GetCompilerOptions().CountHotnessInCompiledCode()) {
2086 UseScratchRegisterScope temps(GetVIXLAssembler());
2087 vixl32::Register temp = temps.Acquire();
2088 static_assert(ArtMethod::MaxCounter() == 0xFFFF, "asm is probably wrong");
2089 if (!is_frame_entry) {
2090 __ Push(vixl32::Register(kMethodRegister));
Vladimir Markodec78172020-06-19 15:31:23 +01002091 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize);
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002092 GetAssembler()->LoadFromOffset(kLoadWord, kMethodRegister, sp, kArmWordSize);
2093 }
2094 // Load with zero extend to clear the high bits for integer overflow check.
2095 __ Ldrh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2096 __ Add(temp, temp, 1);
2097 // Subtract one if the counter would overflow.
2098 __ Sub(temp, temp, Operand(temp, ShiftType::LSR, 16));
2099 __ Strh(temp, MemOperand(kMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
2100 if (!is_frame_entry) {
2101 __ Pop(vixl32::Register(kMethodRegister));
Vladimir Markodec78172020-06-19 15:31:23 +01002102 GetAssembler()->cfi().AdjustCFAOffset(-static_cast<int>(kArmWordSize));
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002103 }
2104 }
2105
2106 if (GetGraph()->IsCompilingBaseline() && !Runtime::Current()->IsAotCompiler()) {
2107 ScopedObjectAccess soa(Thread::Current());
2108 ProfilingInfo* info = GetGraph()->GetArtMethod()->GetProfilingInfo(kRuntimePointerSize);
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002109 if (info != nullptr) {
2110 uint32_t address = reinterpret_cast32<uint32_t>(info);
2111 vixl::aarch32::Label done;
2112 UseScratchRegisterScope temps(GetVIXLAssembler());
2113 temps.Exclude(ip);
2114 if (!is_frame_entry) {
2115 __ Push(r4); // Will be used as temporary. For frame entry, r4 is always available.
Vladimir Markodec78172020-06-19 15:31:23 +01002116 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize);
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002117 }
2118 __ Mov(r4, address);
2119 __ Ldrh(ip, MemOperand(r4, ProfilingInfo::BaselineHotnessCountOffset().Int32Value()));
2120 __ Add(ip, ip, 1);
2121 __ Strh(ip, MemOperand(r4, ProfilingInfo::BaselineHotnessCountOffset().Int32Value()));
2122 if (!is_frame_entry) {
2123 __ Pop(r4);
Vladimir Markodec78172020-06-19 15:31:23 +01002124 GetAssembler()->cfi().AdjustCFAOffset(-static_cast<int>(kArmWordSize));
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002125 }
2126 __ Lsls(ip, ip, 16);
2127 __ B(ne, &done);
2128 uint32_t entry_point_offset =
2129 GetThreadOffset<kArmPointerSize>(kQuickCompileOptimized).Int32Value();
2130 if (HasEmptyFrame()) {
2131 CHECK(is_frame_entry);
2132 // For leaf methods, we need to spill lr and r0. Also spill r1 and r2 for
2133 // alignment.
2134 uint32_t core_spill_mask =
2135 (1 << lr.GetCode()) | (1 << r0.GetCode()) | (1 << r1.GetCode()) | (1 << r2.GetCode());
2136 __ Push(RegisterList(core_spill_mask));
Vladimir Markodec78172020-06-19 15:31:23 +01002137 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask));
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002138 __ Ldr(lr, MemOperand(tr, entry_point_offset));
2139 __ Blx(lr);
2140 __ Pop(RegisterList(core_spill_mask));
Vladimir Markodec78172020-06-19 15:31:23 +01002141 GetAssembler()->cfi().AdjustCFAOffset(
2142 -static_cast<int>(kArmWordSize) * POPCOUNT(core_spill_mask));
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002143 } else {
2144 if (!RequiresCurrentMethod()) {
2145 CHECK(is_frame_entry);
2146 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2147 }
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002148 __ Ldr(lr, MemOperand(tr, entry_point_offset));
2149 __ Blx(lr);
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002150 }
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00002151 __ Bind(&done);
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002152 }
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002153 }
2154}
2155
Scott Wakelingfe885462016-09-22 10:24:38 +01002156void CodeGeneratorARMVIXL::GenerateFrameEntry() {
2157 bool skip_overflow_check =
2158 IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm);
2159 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
2160 __ Bind(&frame_entry_label_);
2161
2162 if (HasEmptyFrame()) {
David Srbecky30021842019-02-13 14:19:36 +00002163 // Ensure that the CFI opcode list is not empty.
2164 GetAssembler()->cfi().Nop();
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002165 MaybeIncrementHotness(/* is_frame_entry= */ true);
Scott Wakelingfe885462016-09-22 10:24:38 +01002166 return;
2167 }
2168
Scott Wakelingfe885462016-09-22 10:24:38 +01002169 if (!skip_overflow_check) {
xueliang.zhong10049552018-01-31 17:10:36 +00002170 // Using r4 instead of IP saves 2 bytes.
Nicolas Geoffray1a4f3ca2018-01-25 14:07:15 +00002171 UseScratchRegisterScope temps(GetVIXLAssembler());
xueliang.zhong10049552018-01-31 17:10:36 +00002172 vixl32::Register temp;
2173 // TODO: Remove this check when R4 is made a callee-save register
2174 // in ART compiled code (b/72801708). Currently we need to make
2175 // sure r4 is not blocked, e.g. in special purpose
2176 // TestCodeGeneratorARMVIXL; also asserting that r4 is available
2177 // here.
2178 if (!blocked_core_registers_[R4]) {
2179 for (vixl32::Register reg : kParameterCoreRegistersVIXL) {
2180 DCHECK(!reg.Is(r4));
2181 }
2182 DCHECK(!kCoreCalleeSaves.Includes(r4));
2183 temp = r4;
2184 } else {
2185 temp = temps.Acquire();
2186 }
Vladimir Marko33bff252017-11-01 14:35:42 +00002187 __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(InstructionSet::kArm)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002188 // The load must immediately precede RecordPcInfo.
Artem Serov0fb37192016-12-06 18:13:40 +00002189 ExactAssemblyScope aas(GetVIXLAssembler(),
2190 vixl32::kMaxInstructionSizeInBytes,
2191 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002192 __ ldr(temp, MemOperand(temp));
2193 RecordPcInfo(nullptr, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01002194 }
2195
Vladimir Marko1a225a72019-07-05 13:37:42 +01002196 uint32_t frame_size = GetFrameSize();
2197 uint32_t core_spills_offset = frame_size - GetCoreSpillSize();
2198 uint32_t fp_spills_offset = frame_size - FrameEntrySpillSize();
2199 if ((fpu_spill_mask_ == 0u || IsPowerOfTwo(fpu_spill_mask_)) &&
2200 core_spills_offset <= 3u * kArmWordSize) {
2201 // Do a single PUSH for core registers including the method and up to two
2202 // filler registers. Then store the single FP spill if any.
2203 // (The worst case is when the method is not required and we actually
2204 // store 3 extra registers but they are stored in the same properly
2205 // aligned 16-byte chunk where we're already writing anyway.)
2206 DCHECK_EQ(kMethodRegister.GetCode(), 0u);
2207 uint32_t extra_regs = MaxInt<uint32_t>(core_spills_offset / kArmWordSize);
2208 DCHECK_LT(MostSignificantBit(extra_regs), LeastSignificantBit(core_spill_mask_));
2209 __ Push(RegisterList(core_spill_mask_ | extra_regs));
2210 GetAssembler()->cfi().AdjustCFAOffset(frame_size);
2211 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2212 core_spills_offset,
2213 core_spill_mask_,
2214 kArmWordSize);
2215 if (fpu_spill_mask_ != 0u) {
2216 DCHECK(IsPowerOfTwo(fpu_spill_mask_));
2217 vixl::aarch32::SRegister sreg(LeastSignificantBit(fpu_spill_mask_));
2218 GetAssembler()->StoreSToOffset(sreg, sp, fp_spills_offset);
2219 GetAssembler()->cfi().RelOffset(DWARFReg(sreg), /*offset=*/ fp_spills_offset);
2220 }
2221 } else {
2222 __ Push(RegisterList(core_spill_mask_));
2223 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(core_spill_mask_));
2224 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(kMethodRegister),
2225 /*offset=*/ 0,
2226 core_spill_mask_,
2227 kArmWordSize);
2228 if (fpu_spill_mask_ != 0) {
2229 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
Scott Wakelingfe885462016-09-22 10:24:38 +01002230
Vladimir Marko1a225a72019-07-05 13:37:42 +01002231 // Check that list is contiguous.
2232 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
Scott Wakelingfe885462016-09-22 10:24:38 +01002233
Vladimir Marko1a225a72019-07-05 13:37:42 +01002234 __ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2235 GetAssembler()->cfi().AdjustCFAOffset(kArmWordSize * POPCOUNT(fpu_spill_mask_));
2236 GetAssembler()->cfi().RelOffsetForMany(DWARFReg(s0),
2237 /*offset=*/ 0,
2238 fpu_spill_mask_,
2239 kArmWordSize);
2240 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002241
Vladimir Marko1a225a72019-07-05 13:37:42 +01002242 // Adjust SP and save the current method if we need it. Note that we do
2243 // not save the method in HCurrentMethod, as the instruction might have
2244 // been removed in the SSA graph.
2245 if (RequiresCurrentMethod() && fp_spills_offset <= 3 * kArmWordSize) {
2246 DCHECK_EQ(kMethodRegister.GetCode(), 0u);
2247 __ Push(RegisterList(MaxInt<uint32_t>(fp_spills_offset / kArmWordSize)));
2248 GetAssembler()->cfi().AdjustCFAOffset(fp_spills_offset);
2249 } else {
Vladimir Markodec78172020-06-19 15:31:23 +01002250 IncreaseFrame(fp_spills_offset);
Vladimir Marko1a225a72019-07-05 13:37:42 +01002251 if (RequiresCurrentMethod()) {
2252 GetAssembler()->StoreToOffset(kStoreWord, kMethodRegister, sp, 0);
2253 }
2254 }
Scott Wakelingbffdc702016-12-07 17:46:03 +00002255 }
Nicolas Geoffrayf7893532017-06-15 12:34:36 +01002256
2257 if (GetGraph()->HasShouldDeoptimizeFlag()) {
2258 UseScratchRegisterScope temps(GetVIXLAssembler());
2259 vixl32::Register temp = temps.Acquire();
2260 // Initialize should_deoptimize flag to 0.
2261 __ Mov(temp, 0);
2262 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, GetStackOffsetOfShouldDeoptimizeFlag());
2263 }
Roland Levillain5daa4952017-07-03 17:23:56 +01002264
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002265 MaybeIncrementHotness(/* is_frame_entry= */ true);
Andreas Gampe3db70682018-12-26 15:12:03 -08002266 MaybeGenerateMarkingRegisterCheck(/* code= */ 1);
Scott Wakelingfe885462016-09-22 10:24:38 +01002267}
2268
2269void CodeGeneratorARMVIXL::GenerateFrameExit() {
2270 if (HasEmptyFrame()) {
2271 __ Bx(lr);
2272 return;
2273 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002274
Scott Wakelingfe885462016-09-22 10:24:38 +01002275 // Pop LR into PC to return.
2276 DCHECK_NE(core_spill_mask_ & (1 << kLrCode), 0U);
2277 uint32_t pop_mask = (core_spill_mask_ & (~(1 << kLrCode))) | 1 << kPcCode;
Vladimir Marko1a225a72019-07-05 13:37:42 +01002278
2279 uint32_t frame_size = GetFrameSize();
2280 uint32_t core_spills_offset = frame_size - GetCoreSpillSize();
2281 uint32_t fp_spills_offset = frame_size - FrameEntrySpillSize();
2282 if ((fpu_spill_mask_ == 0u || IsPowerOfTwo(fpu_spill_mask_)) &&
2283 // r4 is blocked by TestCodeGeneratorARMVIXL used by some tests.
2284 core_spills_offset <= (blocked_core_registers_[r4.GetCode()] ? 2u : 3u) * kArmWordSize) {
2285 // Load the FP spill if any and then do a single POP including the method
2286 // and up to two filler registers. If we have no FP spills, this also has
2287 // the advantage that we do not need to emit CFI directives.
2288 if (fpu_spill_mask_ != 0u) {
2289 DCHECK(IsPowerOfTwo(fpu_spill_mask_));
2290 vixl::aarch32::SRegister sreg(LeastSignificantBit(fpu_spill_mask_));
2291 GetAssembler()->cfi().RememberState();
2292 GetAssembler()->LoadSFromOffset(sreg, sp, fp_spills_offset);
2293 GetAssembler()->cfi().Restore(DWARFReg(sreg));
2294 }
2295 // Clobber registers r2-r4 as they are caller-save in ART managed ABI and
2296 // never hold the return value.
2297 uint32_t extra_regs = MaxInt<uint32_t>(core_spills_offset / kArmWordSize) << r2.GetCode();
2298 DCHECK_EQ(extra_regs & kCoreCalleeSaves.GetList(), 0u);
2299 DCHECK_LT(MostSignificantBit(extra_regs), LeastSignificantBit(pop_mask));
2300 __ Pop(RegisterList(pop_mask | extra_regs));
2301 if (fpu_spill_mask_ != 0u) {
2302 GetAssembler()->cfi().RestoreState();
2303 }
2304 } else {
2305 GetAssembler()->cfi().RememberState();
Vladimir Markodec78172020-06-19 15:31:23 +01002306 DecreaseFrame(fp_spills_offset);
Vladimir Marko1a225a72019-07-05 13:37:42 +01002307 if (fpu_spill_mask_ != 0) {
2308 uint32_t first = LeastSignificantBit(fpu_spill_mask_);
2309
2310 // Check that list is contiguous.
2311 DCHECK_EQ(fpu_spill_mask_ >> CTZ(fpu_spill_mask_), ~0u >> (32 - POPCOUNT(fpu_spill_mask_)));
2312
2313 __ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fpu_spill_mask_)));
2314 GetAssembler()->cfi().AdjustCFAOffset(
2315 -static_cast<int>(kArmWordSize) * POPCOUNT(fpu_spill_mask_));
2316 GetAssembler()->cfi().RestoreMany(DWARFReg(vixl32::SRegister(0)), fpu_spill_mask_);
2317 }
2318 __ Pop(RegisterList(pop_mask));
2319 GetAssembler()->cfi().RestoreState();
2320 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
2321 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002322}
2323
2324void CodeGeneratorARMVIXL::Bind(HBasicBlock* block) {
2325 __ Bind(GetLabelOf(block));
2326}
2327
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002328Location InvokeDexCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002329 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002330 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002331 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002332 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002333 case DataType::Type::kInt8:
2334 case DataType::Type::kUint16:
2335 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002336 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002337 uint32_t index = gp_index_++;
2338 uint32_t stack_index = stack_index_++;
2339 if (index < calling_convention.GetNumberOfRegisters()) {
2340 return LocationFrom(calling_convention.GetRegisterAt(index));
2341 } else {
2342 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2343 }
2344 }
2345
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002346 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002347 uint32_t index = gp_index_;
2348 uint32_t stack_index = stack_index_;
2349 gp_index_ += 2;
2350 stack_index_ += 2;
2351 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2352 if (calling_convention.GetRegisterAt(index).Is(r1)) {
2353 // Skip R1, and use R2_R3 instead.
2354 gp_index_++;
2355 index++;
2356 }
2357 }
2358 if (index + 1 < calling_convention.GetNumberOfRegisters()) {
2359 DCHECK_EQ(calling_convention.GetRegisterAt(index).GetCode() + 1,
2360 calling_convention.GetRegisterAt(index + 1).GetCode());
2361
2362 return LocationFrom(calling_convention.GetRegisterAt(index),
2363 calling_convention.GetRegisterAt(index + 1));
2364 } else {
2365 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2366 }
2367 }
2368
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002369 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002370 uint32_t stack_index = stack_index_++;
2371 if (float_index_ % 2 == 0) {
2372 float_index_ = std::max(double_index_, float_index_);
2373 }
2374 if (float_index_ < calling_convention.GetNumberOfFpuRegisters()) {
2375 return LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
2376 } else {
2377 return Location::StackSlot(calling_convention.GetStackOffsetOf(stack_index));
2378 }
2379 }
2380
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002381 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002382 double_index_ = std::max(double_index_, RoundUp(float_index_, 2));
2383 uint32_t stack_index = stack_index_;
2384 stack_index_ += 2;
2385 if (double_index_ + 1 < calling_convention.GetNumberOfFpuRegisters()) {
2386 uint32_t index = double_index_;
2387 double_index_ += 2;
2388 Location result = LocationFrom(
2389 calling_convention.GetFpuRegisterAt(index),
2390 calling_convention.GetFpuRegisterAt(index + 1));
2391 DCHECK(ExpectedPairLayout(result));
2392 return result;
2393 } else {
2394 return Location::DoubleStackSlot(calling_convention.GetStackOffsetOf(stack_index));
2395 }
2396 }
2397
Aart Bik66c158e2018-01-31 12:55:04 -08002398 case DataType::Type::kUint32:
2399 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002400 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002401 LOG(FATAL) << "Unexpected parameter type " << type;
Elliott Hughesc1896c92018-11-29 11:33:18 -08002402 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00002403 }
2404 return Location::NoLocation();
2405}
2406
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002407Location InvokeDexCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type) const {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002408 switch (type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002409 case DataType::Type::kReference:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002410 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002411 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002412 case DataType::Type::kInt8:
2413 case DataType::Type::kUint16:
2414 case DataType::Type::kInt16:
Aart Bik66c158e2018-01-31 12:55:04 -08002415 case DataType::Type::kUint32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002416 case DataType::Type::kInt32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002417 return LocationFrom(r0);
2418 }
2419
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002420 case DataType::Type::kFloat32: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002421 return LocationFrom(s0);
2422 }
2423
Aart Bik66c158e2018-01-31 12:55:04 -08002424 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002425 case DataType::Type::kInt64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002426 return LocationFrom(r0, r1);
2427 }
2428
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002429 case DataType::Type::kFloat64: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00002430 return LocationFrom(s0, s1);
2431 }
2432
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002433 case DataType::Type::kVoid:
Artem Serovd4cc5b22016-11-04 11:19:09 +00002434 return Location::NoLocation();
2435 }
2436
2437 UNREACHABLE();
2438}
2439
2440Location InvokeDexCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2441 return LocationFrom(kMethodRegister);
2442}
2443
Vladimir Marko86c87522020-05-11 16:55:55 +01002444Location CriticalNativeCallingConventionVisitorARMVIXL::GetNextLocation(DataType::Type type) {
2445 DCHECK_NE(type, DataType::Type::kReference);
2446
2447 // Native ABI uses the same registers as managed, except that the method register r0
2448 // is a normal argument.
2449 Location location = Location::NoLocation();
2450 if (DataType::Is64BitType(type)) {
2451 gpr_index_ = RoundUp(gpr_index_, 2u);
2452 stack_offset_ = RoundUp(stack_offset_, 2 * kFramePointerSize);
2453 if (gpr_index_ < 1u + kParameterCoreRegistersLengthVIXL) {
2454 location = LocationFrom(gpr_index_ == 0u ? r0 : kParameterCoreRegistersVIXL[gpr_index_ - 1u],
2455 kParameterCoreRegistersVIXL[gpr_index_]);
2456 gpr_index_ += 2u;
2457 }
2458 } else {
2459 if (gpr_index_ < 1u + kParameterCoreRegistersLengthVIXL) {
2460 location = LocationFrom(gpr_index_ == 0u ? r0 : kParameterCoreRegistersVIXL[gpr_index_ - 1u]);
2461 ++gpr_index_;
2462 }
2463 }
2464 if (location.IsInvalid()) {
2465 if (DataType::Is64BitType(type)) {
2466 location = Location::DoubleStackSlot(stack_offset_);
2467 stack_offset_ += 2 * kFramePointerSize;
2468 } else {
2469 location = Location::StackSlot(stack_offset_);
2470 stack_offset_ += kFramePointerSize;
2471 }
2472
2473 if (for_register_allocation_) {
2474 location = Location::Any();
2475 }
2476 }
2477 return location;
2478}
2479
2480Location CriticalNativeCallingConventionVisitorARMVIXL::GetReturnLocation(DataType::Type type)
2481 const {
2482 // We perform conversion to the managed ABI return register after the call if needed.
2483 InvokeDexCallingConventionVisitorARMVIXL dex_calling_convention;
2484 return dex_calling_convention.GetReturnLocation(type);
2485}
2486
2487Location CriticalNativeCallingConventionVisitorARMVIXL::GetMethodLocation() const {
2488 // Pass the method in the hidden argument R4.
2489 return Location::RegisterLocation(R4);
2490}
2491
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002492void CodeGeneratorARMVIXL::Move32(Location destination, Location source) {
2493 if (source.Equals(destination)) {
2494 return;
2495 }
2496 if (destination.IsRegister()) {
2497 if (source.IsRegister()) {
2498 __ Mov(RegisterFrom(destination), RegisterFrom(source));
2499 } else if (source.IsFpuRegister()) {
2500 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
2501 } else {
2502 GetAssembler()->LoadFromOffset(kLoadWord,
2503 RegisterFrom(destination),
2504 sp,
2505 source.GetStackIndex());
2506 }
2507 } else if (destination.IsFpuRegister()) {
2508 if (source.IsRegister()) {
2509 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
2510 } else if (source.IsFpuRegister()) {
2511 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
2512 } else {
2513 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
2514 }
2515 } else {
2516 DCHECK(destination.IsStackSlot()) << destination;
2517 if (source.IsRegister()) {
2518 GetAssembler()->StoreToOffset(kStoreWord,
2519 RegisterFrom(source),
2520 sp,
2521 destination.GetStackIndex());
2522 } else if (source.IsFpuRegister()) {
2523 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
2524 } else {
2525 DCHECK(source.IsStackSlot()) << source;
2526 UseScratchRegisterScope temps(GetVIXLAssembler());
2527 vixl32::Register temp = temps.Acquire();
2528 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
2529 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
2530 }
2531 }
2532}
2533
Artem Serovcfbe9132016-10-14 15:58:56 +01002534void CodeGeneratorARMVIXL::MoveConstant(Location location, int32_t value) {
2535 DCHECK(location.IsRegister());
2536 __ Mov(RegisterFrom(location), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01002537}
2538
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002539void CodeGeneratorARMVIXL::MoveLocation(Location dst, Location src, DataType::Type dst_type) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002540 // TODO(VIXL): Maybe refactor to have the 'move' implementation here and use it in
2541 // `ParallelMoveResolverARMVIXL::EmitMove`, as is done in the `arm64` backend.
Vladimir Markoca6fff82017-10-03 14:49:14 +01002542 HParallelMove move(GetGraph()->GetAllocator());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002543 move.AddMove(src, dst, dst_type, nullptr);
2544 GetMoveResolver()->EmitNativeCode(&move);
Scott Wakelingfe885462016-09-22 10:24:38 +01002545}
2546
Artem Serovcfbe9132016-10-14 15:58:56 +01002547void CodeGeneratorARMVIXL::AddLocationAsTemp(Location location, LocationSummary* locations) {
2548 if (location.IsRegister()) {
2549 locations->AddTemp(location);
2550 } else if (location.IsRegisterPair()) {
2551 locations->AddTemp(LocationFrom(LowRegisterFrom(location)));
2552 locations->AddTemp(LocationFrom(HighRegisterFrom(location)));
2553 } else {
2554 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
2555 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002556}
2557
2558void CodeGeneratorARMVIXL::InvokeRuntime(QuickEntrypointEnum entrypoint,
2559 HInstruction* instruction,
2560 uint32_t dex_pc,
2561 SlowPathCode* slow_path) {
2562 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Vladimir Markof6675082019-05-17 12:05:28 +01002563
2564 ThreadOffset32 entrypoint_offset = GetThreadOffset<kArmPointerSize>(entrypoint);
2565 // Reduce code size for AOT by using shared trampolines for slow path runtime calls across the
2566 // entire oat file. This adds an extra branch and we do not want to slow down the main path.
2567 // For JIT, thunk sharing is per-method, so the gains would be smaller or even negative.
Vladimir Marko695348f2020-05-19 14:42:02 +01002568 if (slow_path == nullptr || GetCompilerOptions().IsJitCompiler()) {
Vladimir Markof6675082019-05-17 12:05:28 +01002569 __ Ldr(lr, MemOperand(tr, entrypoint_offset.Int32Value()));
2570 // Ensure the pc position is recorded immediately after the `blx` instruction.
2571 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
2572 ExactAssemblyScope aas(GetVIXLAssembler(),
2573 vixl32::k16BitT32InstructionSizeInBytes,
2574 CodeBufferCheckScope::kExactSize);
2575 __ blx(lr);
2576 if (EntrypointRequiresStackMap(entrypoint)) {
2577 RecordPcInfo(instruction, dex_pc, slow_path);
2578 }
2579 } else {
2580 // Ensure the pc position is recorded immediately after the `bl` instruction.
2581 ExactAssemblyScope aas(GetVIXLAssembler(),
2582 vixl32::k32BitT32InstructionSizeInBytes,
2583 CodeBufferCheckScope::kExactSize);
2584 EmitEntrypointThunkCall(entrypoint_offset);
2585 if (EntrypointRequiresStackMap(entrypoint)) {
2586 RecordPcInfo(instruction, dex_pc, slow_path);
2587 }
Scott Wakelingfe885462016-09-22 10:24:38 +01002588 }
2589}
2590
2591void CodeGeneratorARMVIXL::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2592 HInstruction* instruction,
2593 SlowPathCode* slow_path) {
2594 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Alexandre Rames374ddf32016-11-04 10:40:49 +00002595 __ Ldr(lr, MemOperand(tr, entry_point_offset));
Scott Wakelingfe885462016-09-22 10:24:38 +01002596 __ Blx(lr);
2597}
2598
Scott Wakelingfe885462016-09-22 10:24:38 +01002599void InstructionCodeGeneratorARMVIXL::HandleGoto(HInstruction* got, HBasicBlock* successor) {
Aart Bika8b8e9b2018-01-09 11:01:02 -08002600 if (successor->IsExitBlock()) {
2601 DCHECK(got->GetPrevious()->AlwaysThrows());
2602 return; // no code needed
2603 }
2604
Scott Wakelingfe885462016-09-22 10:24:38 +01002605 HBasicBlock* block = got->GetBlock();
2606 HInstruction* previous = got->GetPrevious();
2607 HLoopInformation* info = block->GetLoopInformation();
2608
2609 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
Nicolas Geoffraya59af8a2019-11-27 17:42:32 +00002610 codegen_->MaybeIncrementHotness(/* is_frame_entry= */ false);
Scott Wakelingfe885462016-09-22 10:24:38 +01002611 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
2612 return;
2613 }
2614 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
2615 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
Andreas Gampe3db70682018-12-26 15:12:03 -08002616 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 2);
Scott Wakelingfe885462016-09-22 10:24:38 +01002617 }
2618 if (!codegen_->GoesToNextBlock(block, successor)) {
2619 __ B(codegen_->GetLabelOf(successor));
2620 }
2621}
2622
2623void LocationsBuilderARMVIXL::VisitGoto(HGoto* got) {
2624 got->SetLocations(nullptr);
2625}
2626
2627void InstructionCodeGeneratorARMVIXL::VisitGoto(HGoto* got) {
2628 HandleGoto(got, got->GetSuccessor());
2629}
2630
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002631void LocationsBuilderARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2632 try_boundary->SetLocations(nullptr);
2633}
2634
2635void InstructionCodeGeneratorARMVIXL::VisitTryBoundary(HTryBoundary* try_boundary) {
2636 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
2637 if (!successor->IsExitBlock()) {
2638 HandleGoto(try_boundary, successor);
2639 }
2640}
2641
Scott Wakelingfe885462016-09-22 10:24:38 +01002642void LocationsBuilderARMVIXL::VisitExit(HExit* exit) {
2643 exit->SetLocations(nullptr);
2644}
2645
2646void InstructionCodeGeneratorARMVIXL::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
2647}
2648
Scott Wakelingfe885462016-09-22 10:24:38 +01002649void InstructionCodeGeneratorARMVIXL::GenerateCompareTestAndBranch(HCondition* condition,
Anton Kirilov23b752b2017-07-20 14:40:44 +01002650 vixl32::Label* true_target,
2651 vixl32::Label* false_target,
Anton Kirilovfd522532017-05-10 12:46:57 +01002652 bool is_far_target) {
Anton Kirilov23b752b2017-07-20 14:40:44 +01002653 if (true_target == false_target) {
2654 DCHECK(true_target != nullptr);
2655 __ B(true_target);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002656 return;
2657 }
2658
Anton Kirilov23b752b2017-07-20 14:40:44 +01002659 vixl32::Label* non_fallthrough_target;
2660 bool invert;
2661 bool emit_both_branches;
Scott Wakelingfe885462016-09-22 10:24:38 +01002662
Anton Kirilov23b752b2017-07-20 14:40:44 +01002663 if (true_target == nullptr) {
2664 // The true target is fallthrough.
2665 DCHECK(false_target != nullptr);
2666 non_fallthrough_target = false_target;
2667 invert = true;
2668 emit_both_branches = false;
2669 } else {
2670 non_fallthrough_target = true_target;
2671 invert = false;
2672 // Either the false target is fallthrough, or there is no fallthrough
2673 // and both branches must be emitted.
2674 emit_both_branches = (false_target != nullptr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002675 }
2676
Anton Kirilov23b752b2017-07-20 14:40:44 +01002677 const auto cond = GenerateTest(condition, invert, codegen_);
2678
2679 __ B(cond.first, non_fallthrough_target, is_far_target);
2680
2681 if (emit_both_branches) {
2682 // No target falls through, we need to branch.
2683 __ B(false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002684 }
2685}
2686
2687void InstructionCodeGeneratorARMVIXL::GenerateTestAndBranch(HInstruction* instruction,
2688 size_t condition_input_index,
2689 vixl32::Label* true_target,
xueliang.zhongf51bc622016-11-04 09:23:32 +00002690 vixl32::Label* false_target,
2691 bool far_target) {
Scott Wakelingfe885462016-09-22 10:24:38 +01002692 HInstruction* cond = instruction->InputAt(condition_input_index);
2693
2694 if (true_target == nullptr && false_target == nullptr) {
2695 // Nothing to do. The code always falls through.
2696 return;
2697 } else if (cond->IsIntConstant()) {
2698 // Constant condition, statically compared against "true" (integer value 1).
2699 if (cond->AsIntConstant()->IsTrue()) {
2700 if (true_target != nullptr) {
2701 __ B(true_target);
2702 }
2703 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00002704 DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
Scott Wakelingfe885462016-09-22 10:24:38 +01002705 if (false_target != nullptr) {
2706 __ B(false_target);
2707 }
2708 }
2709 return;
2710 }
2711
2712 // The following code generates these patterns:
2713 // (1) true_target == nullptr && false_target != nullptr
2714 // - opposite condition true => branch to false_target
2715 // (2) true_target != nullptr && false_target == nullptr
2716 // - condition true => branch to true_target
2717 // (3) true_target != nullptr && false_target != nullptr
2718 // - condition true => branch to true_target
2719 // - branch to false_target
2720 if (IsBooleanValueOrMaterializedCondition(cond)) {
2721 // Condition has been materialized, compare the output to 0.
2722 if (kIsDebugBuild) {
2723 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
2724 DCHECK(cond_val.IsRegister());
2725 }
2726 if (true_target == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002727 __ CompareAndBranchIfZero(InputRegisterAt(instruction, condition_input_index),
2728 false_target,
2729 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002730 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00002731 __ CompareAndBranchIfNonZero(InputRegisterAt(instruction, condition_input_index),
2732 true_target,
2733 far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002734 }
2735 } else {
2736 // Condition has not been materialized. Use its inputs as the comparison and
2737 // its condition as the branch condition.
2738 HCondition* condition = cond->AsCondition();
2739
2740 // If this is a long or FP comparison that has been folded into
2741 // the HCondition, generate the comparison directly.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002742 DataType::Type type = condition->InputAt(0)->GetType();
2743 if (type == DataType::Type::kInt64 || DataType::IsFloatingPointType(type)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01002744 GenerateCompareTestAndBranch(condition, true_target, false_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002745 return;
2746 }
2747
Donghui Bai426b49c2016-11-08 14:55:38 +08002748 vixl32::Label* non_fallthrough_target;
2749 vixl32::Condition arm_cond = vixl32::Condition::None();
2750 const vixl32::Register left = InputRegisterAt(cond, 0);
2751 const Operand right = InputOperandAt(cond, 1);
2752
Scott Wakelingfe885462016-09-22 10:24:38 +01002753 if (true_target == nullptr) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002754 arm_cond = ARMCondition(condition->GetOppositeCondition());
2755 non_fallthrough_target = false_target;
Scott Wakelingfe885462016-09-22 10:24:38 +01002756 } else {
Donghui Bai426b49c2016-11-08 14:55:38 +08002757 arm_cond = ARMCondition(condition->GetCondition());
2758 non_fallthrough_target = true_target;
2759 }
2760
2761 if (right.IsImmediate() && right.GetImmediate() == 0 && (arm_cond.Is(ne) || arm_cond.Is(eq))) {
2762 if (arm_cond.Is(eq)) {
Anton Kirilovfd522532017-05-10 12:46:57 +01002763 __ CompareAndBranchIfZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08002764 } else {
2765 DCHECK(arm_cond.Is(ne));
Anton Kirilovfd522532017-05-10 12:46:57 +01002766 __ CompareAndBranchIfNonZero(left, non_fallthrough_target, far_target);
Donghui Bai426b49c2016-11-08 14:55:38 +08002767 }
2768 } else {
2769 __ Cmp(left, right);
Anton Kirilovfd522532017-05-10 12:46:57 +01002770 __ B(arm_cond, non_fallthrough_target, far_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002771 }
2772 }
2773
2774 // If neither branch falls through (case 3), the conditional branch to `true_target`
2775 // was already emitted (case 2) and we need to emit a jump to `false_target`.
2776 if (true_target != nullptr && false_target != nullptr) {
2777 __ B(false_target);
2778 }
2779}
2780
2781void LocationsBuilderARMVIXL::VisitIf(HIf* if_instr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002782 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
Scott Wakelingfe885462016-09-22 10:24:38 +01002783 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
2784 locations->SetInAt(0, Location::RequiresRegister());
2785 }
2786}
2787
2788void InstructionCodeGeneratorARMVIXL::VisitIf(HIf* if_instr) {
2789 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
2790 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002791 vixl32::Label* true_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor) ?
2792 nullptr : codegen_->GetLabelOf(true_successor);
2793 vixl32::Label* false_target = codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor) ?
2794 nullptr : codegen_->GetLabelOf(false_successor);
Andreas Gampe3db70682018-12-26 15:12:03 -08002795 GenerateTestAndBranch(if_instr, /* condition_input_index= */ 0, true_target, false_target);
Scott Wakelingfe885462016-09-22 10:24:38 +01002796}
2797
Scott Wakelingc34dba72016-10-03 10:14:44 +01002798void LocationsBuilderARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002799 LocationSummary* locations = new (GetGraph()->GetAllocator())
Scott Wakelingc34dba72016-10-03 10:14:44 +01002800 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +01002801 InvokeRuntimeCallingConventionARMVIXL calling_convention;
2802 RegisterSet caller_saves = RegisterSet::Empty();
2803 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
2804 locations->SetCustomSlowPathCallerSaves(caller_saves);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002805 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
2806 locations->SetInAt(0, Location::RequiresRegister());
2807 }
2808}
2809
2810void InstructionCodeGeneratorARMVIXL::VisitDeoptimize(HDeoptimize* deoptimize) {
2811 SlowPathCodeARMVIXL* slow_path =
2812 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARMVIXL>(deoptimize);
2813 GenerateTestAndBranch(deoptimize,
Andreas Gampe3db70682018-12-26 15:12:03 -08002814 /* condition_input_index= */ 0,
Scott Wakelingc34dba72016-10-03 10:14:44 +01002815 slow_path->GetEntryLabel(),
Andreas Gampe3db70682018-12-26 15:12:03 -08002816 /* false_target= */ nullptr);
Scott Wakelingc34dba72016-10-03 10:14:44 +01002817}
2818
Artem Serovd4cc5b22016-11-04 11:19:09 +00002819void LocationsBuilderARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002820 LocationSummary* locations = new (GetGraph()->GetAllocator())
Artem Serovd4cc5b22016-11-04 11:19:09 +00002821 LocationSummary(flag, LocationSummary::kNoCall);
2822 locations->SetOut(Location::RequiresRegister());
2823}
2824
2825void InstructionCodeGeneratorARMVIXL::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
2826 GetAssembler()->LoadFromOffset(kLoadWord,
2827 OutputRegister(flag),
2828 sp,
2829 codegen_->GetStackOffsetOfShouldDeoptimizeFlag());
2830}
2831
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002832void LocationsBuilderARMVIXL::VisitSelect(HSelect* select) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002833 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002834 const bool is_floating_point = DataType::IsFloatingPointType(select->GetType());
Donghui Bai426b49c2016-11-08 14:55:38 +08002835
2836 if (is_floating_point) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002837 locations->SetInAt(0, Location::RequiresFpuRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08002838 locations->SetInAt(1, Location::FpuRegisterOrConstant(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002839 } else {
2840 locations->SetInAt(0, Location::RequiresRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08002841 locations->SetInAt(1, Arm8BitEncodableConstantOrRegister(select->GetTrueValue()));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002842 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002843
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002844 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002845 locations->SetInAt(2, Location::RegisterOrConstant(select->GetCondition()));
2846 // The code generator handles overlap with the values, but not with the condition.
2847 locations->SetOut(Location::SameAsFirstInput());
2848 } else if (is_floating_point) {
2849 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2850 } else {
2851 if (!locations->InAt(1).IsConstant()) {
2852 locations->SetInAt(0, Arm8BitEncodableConstantOrRegister(select->GetFalseValue()));
2853 }
2854
2855 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002856 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002857}
2858
2859void InstructionCodeGeneratorARMVIXL::VisitSelect(HSelect* select) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002860 HInstruction* const condition = select->GetCondition();
2861 const LocationSummary* const locations = select->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002862 const DataType::Type type = select->GetType();
Donghui Bai426b49c2016-11-08 14:55:38 +08002863 const Location first = locations->InAt(0);
2864 const Location out = locations->Out();
2865 const Location second = locations->InAt(1);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002866
2867 // In the unlucky case the output of this instruction overlaps
2868 // with an input of an "emitted-at-use-site" condition, and
2869 // the output of this instruction is not one of its inputs, we'll
2870 // need to fallback to branches instead of conditional ARM instructions.
2871 bool output_overlaps_with_condition_inputs =
2872 !IsBooleanValueOrMaterializedCondition(condition) &&
2873 !out.Equals(first) &&
2874 !out.Equals(second) &&
2875 (condition->GetLocations()->InAt(0).Equals(out) ||
2876 condition->GetLocations()->InAt(1).Equals(out));
2877 DCHECK(!output_overlaps_with_condition_inputs || condition->IsCondition());
Donghui Bai426b49c2016-11-08 14:55:38 +08002878 Location src;
2879
2880 if (condition->IsIntConstant()) {
2881 if (condition->AsIntConstant()->IsFalse()) {
2882 src = first;
2883 } else {
2884 src = second;
2885 }
2886
2887 codegen_->MoveLocation(out, src, type);
2888 return;
2889 }
2890
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002891 if (!DataType::IsFloatingPointType(type) && !output_overlaps_with_condition_inputs) {
Donghui Bai426b49c2016-11-08 14:55:38 +08002892 bool invert = false;
2893
2894 if (out.Equals(second)) {
2895 src = first;
2896 invert = true;
2897 } else if (out.Equals(first)) {
2898 src = second;
2899 } else if (second.IsConstant()) {
2900 DCHECK(CanEncodeConstantAs8BitImmediate(second.GetConstant()));
2901 src = second;
2902 } else if (first.IsConstant()) {
2903 DCHECK(CanEncodeConstantAs8BitImmediate(first.GetConstant()));
2904 src = first;
2905 invert = true;
2906 } else {
2907 src = second;
2908 }
2909
2910 if (CanGenerateConditionalMove(out, src)) {
2911 if (!out.Equals(first) && !out.Equals(second)) {
2912 codegen_->MoveLocation(out, src.Equals(first) ? second : first, type);
2913 }
2914
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002915 std::pair<vixl32::Condition, vixl32::Condition> cond(eq, ne);
2916
2917 if (IsBooleanValueOrMaterializedCondition(condition)) {
2918 __ Cmp(InputRegisterAt(select, 2), 0);
2919 cond = invert ? std::make_pair(eq, ne) : std::make_pair(ne, eq);
2920 } else {
2921 cond = GenerateTest(condition->AsCondition(), invert, codegen_);
2922 }
2923
Donghui Bai426b49c2016-11-08 14:55:38 +08002924 const size_t instr_count = out.IsRegisterPair() ? 4 : 2;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002925 // We use the scope because of the IT block that follows.
Donghui Bai426b49c2016-11-08 14:55:38 +08002926 ExactAssemblyScope guard(GetVIXLAssembler(),
2927 instr_count * vixl32::k16BitT32InstructionSizeInBytes,
2928 CodeBufferCheckScope::kExactSize);
2929
2930 if (out.IsRegister()) {
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002931 __ it(cond.first);
2932 __ mov(cond.first, RegisterFrom(out), OperandFrom(src, type));
Donghui Bai426b49c2016-11-08 14:55:38 +08002933 } else {
2934 DCHECK(out.IsRegisterPair());
2935
2936 Operand operand_high(0);
2937 Operand operand_low(0);
2938
2939 if (src.IsConstant()) {
2940 const int64_t value = Int64ConstantFrom(src);
2941
2942 operand_high = High32Bits(value);
2943 operand_low = Low32Bits(value);
2944 } else {
2945 DCHECK(src.IsRegisterPair());
2946 operand_high = HighRegisterFrom(src);
2947 operand_low = LowRegisterFrom(src);
2948 }
2949
Anton Kirilov217b2ce2017-03-16 11:47:12 +00002950 __ it(cond.first);
2951 __ mov(cond.first, LowRegisterFrom(out), operand_low);
2952 __ it(cond.first);
2953 __ mov(cond.first, HighRegisterFrom(out), operand_high);
Donghui Bai426b49c2016-11-08 14:55:38 +08002954 }
2955
2956 return;
2957 }
2958 }
2959
2960 vixl32::Label* false_target = nullptr;
2961 vixl32::Label* true_target = nullptr;
2962 vixl32::Label select_end;
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002963 vixl32::Label other_case;
Donghui Bai426b49c2016-11-08 14:55:38 +08002964 vixl32::Label* const target = codegen_->GetFinalLabel(select, &select_end);
2965
2966 if (out.Equals(second)) {
2967 true_target = target;
2968 src = first;
2969 } else {
2970 false_target = target;
2971 src = second;
2972
2973 if (!out.Equals(first)) {
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002974 if (output_overlaps_with_condition_inputs) {
2975 false_target = &other_case;
2976 } else {
2977 codegen_->MoveLocation(out, first, type);
2978 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002979 }
2980 }
2981
Andreas Gampe3db70682018-12-26 15:12:03 -08002982 GenerateTestAndBranch(select, 2, true_target, false_target, /* far_target= */ false);
Donghui Bai426b49c2016-11-08 14:55:38 +08002983 codegen_->MoveLocation(out, src, type);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01002984 if (output_overlaps_with_condition_inputs) {
2985 __ B(target);
2986 __ Bind(&other_case);
2987 codegen_->MoveLocation(out, first, type);
2988 }
Donghui Bai426b49c2016-11-08 14:55:38 +08002989
2990 if (select_end.IsReferenced()) {
2991 __ Bind(&select_end);
2992 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01002993}
2994
Artem Serov551b28f2016-10-18 19:11:30 +01002995void LocationsBuilderARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo* info) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002996 new (GetGraph()->GetAllocator()) LocationSummary(info);
Artem Serov551b28f2016-10-18 19:11:30 +01002997}
2998
2999void InstructionCodeGeneratorARMVIXL::VisitNativeDebugInfo(HNativeDebugInfo*) {
3000 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
3001}
3002
Vladimir Markodec78172020-06-19 15:31:23 +01003003void CodeGeneratorARMVIXL::IncreaseFrame(size_t adjustment) {
3004 __ Claim(adjustment);
3005 GetAssembler()->cfi().AdjustCFAOffset(adjustment);
3006}
3007
3008void CodeGeneratorARMVIXL::DecreaseFrame(size_t adjustment) {
3009 __ Drop(adjustment);
3010 GetAssembler()->cfi().AdjustCFAOffset(-adjustment);
3011}
3012
Scott Wakelingfe885462016-09-22 10:24:38 +01003013void CodeGeneratorARMVIXL::GenerateNop() {
3014 __ Nop();
3015}
3016
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003017// `temp` is an extra temporary register that is used for some conditions;
3018// callers may not specify it, in which case the method will use a scratch
3019// register instead.
3020void CodeGeneratorARMVIXL::GenerateConditionWithZero(IfCondition condition,
3021 vixl32::Register out,
3022 vixl32::Register in,
3023 vixl32::Register temp) {
3024 switch (condition) {
3025 case kCondEQ:
3026 // x <= 0 iff x == 0 when the comparison is unsigned.
3027 case kCondBE:
3028 if (!temp.IsValid() || (out.IsLow() && !out.Is(in))) {
3029 temp = out;
3030 }
3031
3032 // Avoid 32-bit instructions if possible; note that `in` and `temp` must be
3033 // different as well.
3034 if (in.IsLow() && temp.IsLow() && !in.Is(temp)) {
3035 // temp = - in; only 0 sets the carry flag.
3036 __ Rsbs(temp, in, 0);
3037
3038 if (out.Is(in)) {
3039 std::swap(in, temp);
3040 }
3041
3042 // out = - in + in + carry = carry
3043 __ Adc(out, temp, in);
3044 } else {
3045 // If `in` is 0, then it has 32 leading zeros, and less than that otherwise.
3046 __ Clz(out, in);
3047 // Any number less than 32 logically shifted right by 5 bits results in 0;
3048 // the same operation on 32 yields 1.
3049 __ Lsr(out, out, 5);
3050 }
3051
3052 break;
3053 case kCondNE:
3054 // x > 0 iff x != 0 when the comparison is unsigned.
3055 case kCondA: {
3056 UseScratchRegisterScope temps(GetVIXLAssembler());
3057
3058 if (out.Is(in)) {
3059 if (!temp.IsValid() || in.Is(temp)) {
3060 temp = temps.Acquire();
3061 }
3062 } else if (!temp.IsValid() || !temp.IsLow()) {
3063 temp = out;
3064 }
3065
3066 // temp = in - 1; only 0 does not set the carry flag.
3067 __ Subs(temp, in, 1);
3068 // out = in + ~temp + carry = in + (-(in - 1) - 1) + carry = in - in + 1 - 1 + carry = carry
3069 __ Sbc(out, in, temp);
3070 break;
3071 }
3072 case kCondGE:
3073 __ Mvn(out, in);
3074 in = out;
3075 FALLTHROUGH_INTENDED;
3076 case kCondLT:
3077 // We only care about the sign bit.
3078 __ Lsr(out, in, 31);
3079 break;
3080 case kCondAE:
3081 // Trivially true.
3082 __ Mov(out, 1);
3083 break;
3084 case kCondB:
3085 // Trivially false.
3086 __ Mov(out, 0);
3087 break;
3088 default:
3089 LOG(FATAL) << "Unexpected condition " << condition;
3090 UNREACHABLE();
3091 }
3092}
3093
Scott Wakelingfe885462016-09-22 10:24:38 +01003094void LocationsBuilderARMVIXL::HandleCondition(HCondition* cond) {
3095 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003096 new (GetGraph()->GetAllocator()) LocationSummary(cond, LocationSummary::kNoCall);
Nicolas Geoffray7b05c5f2018-09-21 11:31:38 +01003097 const DataType::Type type = cond->InputAt(0)->GetType();
3098 if (DataType::IsFloatingPointType(type)) {
3099 locations->SetInAt(0, Location::RequiresFpuRegister());
3100 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(cond->InputAt(1)));
3101 } else {
3102 locations->SetInAt(0, Location::RequiresRegister());
3103 locations->SetInAt(1, Location::RegisterOrConstant(cond->InputAt(1)));
3104 }
3105 if (!cond->IsEmittedAtUseSite()) {
3106 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003107 }
3108}
3109
3110void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) {
3111 if (cond->IsEmittedAtUseSite()) {
3112 return;
3113 }
3114
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003115 const DataType::Type type = cond->GetLeft()->GetType();
Scott Wakelingfe885462016-09-22 10:24:38 +01003116
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003117 if (DataType::IsFloatingPointType(type)) {
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003118 GenerateConditionGeneric(cond, codegen_);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003119 return;
Scott Wakelingfe885462016-09-22 10:24:38 +01003120 }
3121
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003122 DCHECK(DataType::IsIntegralType(type) || type == DataType::Type::kReference) << type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003123
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003124 const IfCondition condition = cond->GetCondition();
Scott Wakelingfe885462016-09-22 10:24:38 +01003125
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003126 // A condition with only one boolean input, or two boolean inputs without being equality or
3127 // inequality results from transformations done by the instruction simplifier, and is handled
3128 // as a regular condition with integral inputs.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003129 if (type == DataType::Type::kBool &&
3130 cond->GetRight()->GetType() == DataType::Type::kBool &&
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003131 (condition == kCondEQ || condition == kCondNE)) {
3132 vixl32::Register left = InputRegisterAt(cond, 0);
3133 const vixl32::Register out = OutputRegister(cond);
3134 const Location right_loc = cond->GetLocations()->InAt(1);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003135
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003136 // The constant case is handled by the instruction simplifier.
3137 DCHECK(!right_loc.IsConstant());
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003138
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003139 vixl32::Register right = RegisterFrom(right_loc);
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003140
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003141 // Avoid 32-bit instructions if possible.
3142 if (out.Is(right)) {
3143 std::swap(left, right);
3144 }
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003145
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003146 __ Eor(out, left, right);
3147
3148 if (condition == kCondEQ) {
3149 __ Eor(out, out, 1);
3150 }
3151
3152 return;
Anton Kirilov217b2ce2017-03-16 11:47:12 +00003153 }
Anton Kirilov6f644202017-02-27 18:29:45 +00003154
Anton Kirilov5601d4e2017-05-11 19:33:50 +01003155 GenerateConditionIntegralOrNonPrimitive(cond, codegen_);
Scott Wakelingfe885462016-09-22 10:24:38 +01003156}
3157
3158void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) {
3159 HandleCondition(comp);
3160}
3161
3162void InstructionCodeGeneratorARMVIXL::VisitEqual(HEqual* comp) {
3163 HandleCondition(comp);
3164}
3165
3166void LocationsBuilderARMVIXL::VisitNotEqual(HNotEqual* comp) {
3167 HandleCondition(comp);
3168}
3169
3170void InstructionCodeGeneratorARMVIXL::VisitNotEqual(HNotEqual* comp) {
3171 HandleCondition(comp);
3172}
3173
3174void LocationsBuilderARMVIXL::VisitLessThan(HLessThan* comp) {
3175 HandleCondition(comp);
3176}
3177
3178void InstructionCodeGeneratorARMVIXL::VisitLessThan(HLessThan* comp) {
3179 HandleCondition(comp);
3180}
3181
3182void LocationsBuilderARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3183 HandleCondition(comp);
3184}
3185
3186void InstructionCodeGeneratorARMVIXL::VisitLessThanOrEqual(HLessThanOrEqual* comp) {
3187 HandleCondition(comp);
3188}
3189
3190void LocationsBuilderARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3191 HandleCondition(comp);
3192}
3193
3194void InstructionCodeGeneratorARMVIXL::VisitGreaterThan(HGreaterThan* comp) {
3195 HandleCondition(comp);
3196}
3197
3198void LocationsBuilderARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3199 HandleCondition(comp);
3200}
3201
3202void InstructionCodeGeneratorARMVIXL::VisitGreaterThanOrEqual(HGreaterThanOrEqual* comp) {
3203 HandleCondition(comp);
3204}
3205
3206void LocationsBuilderARMVIXL::VisitBelow(HBelow* comp) {
3207 HandleCondition(comp);
3208}
3209
3210void InstructionCodeGeneratorARMVIXL::VisitBelow(HBelow* comp) {
3211 HandleCondition(comp);
3212}
3213
3214void LocationsBuilderARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3215 HandleCondition(comp);
3216}
3217
3218void InstructionCodeGeneratorARMVIXL::VisitBelowOrEqual(HBelowOrEqual* comp) {
3219 HandleCondition(comp);
3220}
3221
3222void LocationsBuilderARMVIXL::VisitAbove(HAbove* comp) {
3223 HandleCondition(comp);
3224}
3225
3226void InstructionCodeGeneratorARMVIXL::VisitAbove(HAbove* comp) {
3227 HandleCondition(comp);
3228}
3229
3230void LocationsBuilderARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3231 HandleCondition(comp);
3232}
3233
3234void InstructionCodeGeneratorARMVIXL::VisitAboveOrEqual(HAboveOrEqual* comp) {
3235 HandleCondition(comp);
3236}
3237
3238void LocationsBuilderARMVIXL::VisitIntConstant(HIntConstant* constant) {
3239 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003240 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003241 locations->SetOut(Location::ConstantLocation(constant));
3242}
3243
3244void InstructionCodeGeneratorARMVIXL::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
3245 // Will be generated at use site.
3246}
3247
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003248void LocationsBuilderARMVIXL::VisitNullConstant(HNullConstant* constant) {
3249 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003250 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003251 locations->SetOut(Location::ConstantLocation(constant));
3252}
3253
3254void InstructionCodeGeneratorARMVIXL::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
3255 // Will be generated at use site.
3256}
3257
Scott Wakelingfe885462016-09-22 10:24:38 +01003258void LocationsBuilderARMVIXL::VisitLongConstant(HLongConstant* constant) {
3259 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003260 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003261 locations->SetOut(Location::ConstantLocation(constant));
3262}
3263
3264void InstructionCodeGeneratorARMVIXL::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
3265 // Will be generated at use site.
3266}
3267
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003268void LocationsBuilderARMVIXL::VisitFloatConstant(HFloatConstant* constant) {
3269 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003270 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003271 locations->SetOut(Location::ConstantLocation(constant));
3272}
3273
Scott Wakelingc34dba72016-10-03 10:14:44 +01003274void InstructionCodeGeneratorARMVIXL::VisitFloatConstant(
3275 HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003276 // Will be generated at use site.
3277}
3278
3279void LocationsBuilderARMVIXL::VisitDoubleConstant(HDoubleConstant* constant) {
3280 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003281 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003282 locations->SetOut(Location::ConstantLocation(constant));
3283}
3284
Scott Wakelingc34dba72016-10-03 10:14:44 +01003285void InstructionCodeGeneratorARMVIXL::VisitDoubleConstant(
3286 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01003287 // Will be generated at use site.
3288}
3289
Igor Murashkind01745e2017-04-05 16:40:31 -07003290void LocationsBuilderARMVIXL::VisitConstructorFence(HConstructorFence* constructor_fence) {
3291 constructor_fence->SetLocations(nullptr);
3292}
3293
3294void InstructionCodeGeneratorARMVIXL::VisitConstructorFence(
3295 HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
3296 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
3297}
3298
Scott Wakelingfe885462016-09-22 10:24:38 +01003299void LocationsBuilderARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3300 memory_barrier->SetLocations(nullptr);
3301}
3302
3303void InstructionCodeGeneratorARMVIXL::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
3304 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
3305}
3306
3307void LocationsBuilderARMVIXL::VisitReturnVoid(HReturnVoid* ret) {
3308 ret->SetLocations(nullptr);
3309}
3310
3311void InstructionCodeGeneratorARMVIXL::VisitReturnVoid(HReturnVoid* ret ATTRIBUTE_UNUSED) {
3312 codegen_->GenerateFrameExit();
3313}
3314
3315void LocationsBuilderARMVIXL::VisitReturn(HReturn* ret) {
3316 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003317 new (GetGraph()->GetAllocator()) LocationSummary(ret, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003318 locations->SetInAt(0, parameter_visitor_.GetReturnLocation(ret->InputAt(0)->GetType()));
3319}
3320
Nicolas Geoffray57cacb72019-12-08 22:07:08 +00003321void InstructionCodeGeneratorARMVIXL::VisitReturn(HReturn* ret) {
3322 if (GetGraph()->IsCompilingOsr()) {
3323 // To simplify callers of an OSR method, we put the return value in both
3324 // floating point and core registers.
3325 switch (ret->InputAt(0)->GetType()) {
3326 case DataType::Type::kFloat32:
3327 __ Vmov(r0, s0);
3328 break;
3329 case DataType::Type::kFloat64:
3330 __ Vmov(r0, r1, d0);
3331 break;
3332 default:
3333 break;
3334 }
3335 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003336 codegen_->GenerateFrameExit();
3337}
3338
Artem Serovcfbe9132016-10-14 15:58:56 +01003339void LocationsBuilderARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3340 // The trampoline uses the same calling convention as dex calling conventions,
3341 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
3342 // the method_idx.
3343 HandleInvoke(invoke);
3344}
3345
3346void InstructionCodeGeneratorARMVIXL::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
3347 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
Andreas Gampe3db70682018-12-26 15:12:03 -08003348 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 3);
Artem Serovcfbe9132016-10-14 15:58:56 +01003349}
3350
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003351void LocationsBuilderARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3352 // Explicit clinit checks triggered by static invokes must have been pruned by
3353 // art::PrepareForRegisterAllocation.
3354 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3355
Anton Kirilov5ec62182016-10-13 20:16:02 +01003356 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3357 if (intrinsic.TryDispatch(invoke)) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003358 return;
3359 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003360
Vladimir Marko86c87522020-05-11 16:55:55 +01003361 if (invoke->GetCodePtrLocation() == HInvokeStaticOrDirect::CodePtrLocation::kCallCriticalNative) {
3362 CriticalNativeCallingConventionVisitorARMVIXL calling_convention_visitor(
3363 /*for_register_allocation=*/ true);
3364 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3365 } else {
3366 HandleInvoke(invoke);
3367 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003368}
3369
Anton Kirilov5ec62182016-10-13 20:16:02 +01003370static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARMVIXL* codegen) {
3371 if (invoke->GetLocations()->Intrinsified()) {
3372 IntrinsicCodeGeneratorARMVIXL intrinsic(codegen);
3373 intrinsic.Dispatch(invoke);
3374 return true;
3375 }
3376 return false;
3377}
3378
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003379void InstructionCodeGeneratorARMVIXL::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
3380 // Explicit clinit checks triggered by static invokes must have been pruned by
3381 // art::PrepareForRegisterAllocation.
3382 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
3383
Anton Kirilov5ec62182016-10-13 20:16:02 +01003384 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Andreas Gampe3db70682018-12-26 15:12:03 -08003385 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 4);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003386 return;
3387 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003388
3389 LocationSummary* locations = invoke->GetLocations();
Artem Serovd4cc5b22016-11-04 11:19:09 +00003390 codegen_->GenerateStaticOrDirectCall(
3391 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
Roland Levillain5daa4952017-07-03 17:23:56 +01003392
Andreas Gampe3db70682018-12-26 15:12:03 -08003393 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 5);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003394}
3395
3396void LocationsBuilderARMVIXL::HandleInvoke(HInvoke* invoke) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00003397 InvokeDexCallingConventionVisitorARMVIXL calling_convention_visitor;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003398 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
3399}
3400
3401void LocationsBuilderARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003402 IntrinsicLocationsBuilderARMVIXL intrinsic(codegen_);
3403 if (intrinsic.TryDispatch(invoke)) {
3404 return;
3405 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003406
3407 HandleInvoke(invoke);
3408}
3409
3410void InstructionCodeGeneratorARMVIXL::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Anton Kirilov5ec62182016-10-13 20:16:02 +01003411 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Andreas Gampe3db70682018-12-26 15:12:03 -08003412 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 6);
Anton Kirilov5ec62182016-10-13 20:16:02 +01003413 return;
3414 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003415
3416 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
Alexandre Rames374ddf32016-11-04 10:40:49 +00003417 DCHECK(!codegen_->IsLeafMethod());
Roland Levillain5daa4952017-07-03 17:23:56 +01003418
Andreas Gampe3db70682018-12-26 15:12:03 -08003419 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 7);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003420}
3421
Artem Serovcfbe9132016-10-14 15:58:56 +01003422void LocationsBuilderARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3423 HandleInvoke(invoke);
3424 // Add the hidden argument.
3425 invoke->GetLocations()->AddTemp(LocationFrom(r12));
3426}
3427
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003428void CodeGeneratorARMVIXL::MaybeGenerateInlineCacheCheck(HInstruction* instruction,
3429 vixl32::Register klass) {
3430 DCHECK_EQ(r0.GetCode(), klass.GetCode());
Nicolas Geoffray20036d82019-11-28 16:15:00 +00003431 // We know the destination of an intrinsic, so no need to record inline
3432 // caches.
3433 if (!instruction->GetLocations()->Intrinsified() &&
Nicolas Geoffray9b5271e2019-12-04 14:39:46 +00003434 GetGraph()->IsCompilingBaseline() &&
Nicolas Geoffray20036d82019-11-28 16:15:00 +00003435 !Runtime::Current()->IsAotCompiler()) {
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003436 DCHECK(!instruction->GetEnvironment()->IsFromInlinedInvoke());
3437 ScopedObjectAccess soa(Thread::Current());
3438 ProfilingInfo* info = GetGraph()->GetArtMethod()->GetProfilingInfo(kRuntimePointerSize);
Nicolas Geoffray796aa2c2019-12-17 10:20:05 +00003439 if (info != nullptr) {
3440 InlineCache* cache = info->GetInlineCache(instruction->GetDexPc());
3441 uint32_t address = reinterpret_cast32<uint32_t>(cache);
3442 vixl32::Label done;
3443 UseScratchRegisterScope temps(GetVIXLAssembler());
3444 temps.Exclude(ip);
3445 __ Mov(r4, address);
3446 __ Ldr(ip, MemOperand(r4, InlineCache::ClassesOffset().Int32Value()));
3447 // Fast path for a monomorphic cache.
3448 __ Cmp(klass, ip);
3449 __ B(eq, &done, /* is_far_target= */ false);
3450 InvokeRuntime(kQuickUpdateInlineCache, instruction, instruction->GetDexPc());
3451 __ Bind(&done);
3452 }
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003453 }
3454}
3455
Artem Serovcfbe9132016-10-14 15:58:56 +01003456void InstructionCodeGeneratorARMVIXL::VisitInvokeInterface(HInvokeInterface* invoke) {
3457 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
3458 LocationSummary* locations = invoke->GetLocations();
3459 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
3460 vixl32::Register hidden_reg = RegisterFrom(locations->GetTemp(1));
3461 Location receiver = locations->InAt(0);
3462 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3463
3464 DCHECK(!receiver.IsStackSlot());
3465
Alexandre Rames374ddf32016-11-04 10:40:49 +00003466 // Ensure the pc position is recorded immediately after the `ldr` instruction.
3467 {
Artem Serov0fb37192016-12-06 18:13:40 +00003468 ExactAssemblyScope aas(GetVIXLAssembler(),
3469 vixl32::kMaxInstructionSizeInBytes,
3470 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00003471 // /* HeapReference<Class> */ temp = receiver->klass_
3472 __ ldr(temp, MemOperand(RegisterFrom(receiver), class_offset));
3473 codegen_->MaybeRecordImplicitNullCheck(invoke);
3474 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003475 // Instead of simply (possibly) unpoisoning `temp` here, we should
3476 // emit a read barrier for the previous class reference load.
3477 // However this is not required in practice, as this is an
3478 // intermediate/temporary reference and because the current
3479 // concurrent copying collector keeps the from-space memory
3480 // intact/accessible until the end of the marking phase (the
3481 // concurrent copying collector may not in the future).
3482 GetAssembler()->MaybeUnpoisonHeapReference(temp);
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003483
3484 // If we're compiling baseline, update the inline cache.
3485 codegen_->MaybeGenerateInlineCacheCheck(invoke, temp);
3486
Artem Serovcfbe9132016-10-14 15:58:56 +01003487 GetAssembler()->LoadFromOffset(kLoadWord,
3488 temp,
3489 temp,
3490 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00003491
Artem Serovcfbe9132016-10-14 15:58:56 +01003492 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
3493 invoke->GetImtIndex(), kArmPointerSize));
3494 // temp = temp->GetImtEntryAt(method_offset);
3495 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
3496 uint32_t entry_point =
3497 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize).Int32Value();
3498 // LR = temp->GetEntryPoint();
3499 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
3500
3501 // Set the hidden (in r12) argument. It is done here, right before a BLX to prevent other
3502 // instruction from clobbering it as they might use r12 as a scratch register.
3503 DCHECK(hidden_reg.Is(r12));
Scott Wakelingb77051e2016-11-21 19:46:00 +00003504
3505 {
3506 // The VIXL macro assembler may clobber any of the scratch registers that are available to it,
3507 // so it checks if the application is using them (by passing them to the macro assembler
3508 // methods). The following application of UseScratchRegisterScope corrects VIXL's notion of
3509 // what is available, and is the opposite of the standard usage: Instead of requesting a
3510 // temporary location, it imposes an external constraint (i.e. a specific register is reserved
3511 // for the hidden argument). Note that this works even if VIXL needs a scratch register itself
3512 // (to materialize the constant), since the destination register becomes available for such use
3513 // internally for the duration of the macro instruction.
3514 UseScratchRegisterScope temps(GetVIXLAssembler());
3515 temps.Exclude(hidden_reg);
3516 __ Mov(hidden_reg, invoke->GetDexMethodIndex());
3517 }
Artem Serovcfbe9132016-10-14 15:58:56 +01003518 {
Alexandre Rames374ddf32016-11-04 10:40:49 +00003519 // Ensure the pc position is recorded immediately after the `blx` instruction.
3520 // 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 +00003521 ExactAssemblyScope aas(GetVIXLAssembler(),
Alexandre Rames374ddf32016-11-04 10:40:49 +00003522 vixl32::k16BitT32InstructionSizeInBytes,
3523 CodeBufferCheckScope::kExactSize);
Artem Serovcfbe9132016-10-14 15:58:56 +01003524 // LR();
3525 __ blx(lr);
Artem Serovcfbe9132016-10-14 15:58:56 +01003526 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
Alexandre Rames374ddf32016-11-04 10:40:49 +00003527 DCHECK(!codegen_->IsLeafMethod());
Artem Serovcfbe9132016-10-14 15:58:56 +01003528 }
Roland Levillain5daa4952017-07-03 17:23:56 +01003529
Andreas Gampe3db70682018-12-26 15:12:03 -08003530 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 8);
Artem Serovcfbe9132016-10-14 15:58:56 +01003531}
3532
Orion Hodsonac141392017-01-13 11:53:47 +00003533void LocationsBuilderARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3534 HandleInvoke(invoke);
3535}
3536
3537void InstructionCodeGeneratorARMVIXL::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
3538 codegen_->GenerateInvokePolymorphicCall(invoke);
Andreas Gampe3db70682018-12-26 15:12:03 -08003539 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 9);
Orion Hodsonac141392017-01-13 11:53:47 +00003540}
3541
Orion Hodson4c8e12e2018-05-18 08:33:20 +01003542void LocationsBuilderARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3543 HandleInvoke(invoke);
3544}
3545
3546void InstructionCodeGeneratorARMVIXL::VisitInvokeCustom(HInvokeCustom* invoke) {
3547 codegen_->GenerateInvokeCustomCall(invoke);
Andreas Gampe3db70682018-12-26 15:12:03 -08003548 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 10);
Orion Hodson4c8e12e2018-05-18 08:33:20 +01003549}
3550
Artem Serov02109dd2016-09-23 17:17:54 +01003551void LocationsBuilderARMVIXL::VisitNeg(HNeg* neg) {
3552 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003553 new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01003554 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003555 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01003556 locations->SetInAt(0, Location::RequiresRegister());
3557 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3558 break;
3559 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003560 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01003561 locations->SetInAt(0, Location::RequiresRegister());
3562 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
3563 break;
3564 }
3565
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003566 case DataType::Type::kFloat32:
3567 case DataType::Type::kFloat64:
Artem Serov02109dd2016-09-23 17:17:54 +01003568 locations->SetInAt(0, Location::RequiresFpuRegister());
3569 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3570 break;
3571
3572 default:
3573 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3574 }
3575}
3576
3577void InstructionCodeGeneratorARMVIXL::VisitNeg(HNeg* neg) {
3578 LocationSummary* locations = neg->GetLocations();
3579 Location out = locations->Out();
3580 Location in = locations->InAt(0);
3581 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003582 case DataType::Type::kInt32:
Artem Serov02109dd2016-09-23 17:17:54 +01003583 __ Rsb(OutputRegister(neg), InputRegisterAt(neg, 0), 0);
3584 break;
3585
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003586 case DataType::Type::kInt64:
Artem Serov02109dd2016-09-23 17:17:54 +01003587 // out.lo = 0 - in.lo (and update the carry/borrow (C) flag)
3588 __ Rsbs(LowRegisterFrom(out), LowRegisterFrom(in), 0);
3589 // We cannot emit an RSC (Reverse Subtract with Carry)
3590 // instruction here, as it does not exist in the Thumb-2
3591 // instruction set. We use the following approach
3592 // using SBC and SUB instead.
3593 //
3594 // out.hi = -C
3595 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(out));
3596 // out.hi = out.hi - in.hi
3597 __ Sub(HighRegisterFrom(out), HighRegisterFrom(out), HighRegisterFrom(in));
3598 break;
3599
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003600 case DataType::Type::kFloat32:
3601 case DataType::Type::kFloat64:
Anton Kirilov644032c2016-12-06 17:51:43 +00003602 __ Vneg(OutputVRegister(neg), InputVRegister(neg));
Artem Serov02109dd2016-09-23 17:17:54 +01003603 break;
3604
3605 default:
3606 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
3607 }
3608}
3609
Scott Wakelingfe885462016-09-22 10:24:38 +01003610void LocationsBuilderARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003611 DataType::Type result_type = conversion->GetResultType();
3612 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003613 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3614 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003615
3616 // The float-to-long, double-to-long and long-to-float type conversions
3617 // rely on a call to the runtime.
3618 LocationSummary::CallKind call_kind =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003619 (((input_type == DataType::Type::kFloat32 || input_type == DataType::Type::kFloat64)
3620 && result_type == DataType::Type::kInt64)
3621 || (input_type == DataType::Type::kInt64 && result_type == DataType::Type::kFloat32))
Scott Wakelingfe885462016-09-22 10:24:38 +01003622 ? LocationSummary::kCallOnMainOnly
3623 : LocationSummary::kNoCall;
3624 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003625 new (GetGraph()->GetAllocator()) LocationSummary(conversion, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01003626
Scott Wakelingfe885462016-09-22 10:24:38 +01003627 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003628 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003629 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003630 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003631 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003632 DCHECK(DataType::IsIntegralType(input_type)) << input_type;
3633 locations->SetInAt(0, Location::RequiresRegister());
3634 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Scott Wakelingfe885462016-09-22 10:24:38 +01003635 break;
3636
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003637 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003638 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003639 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003640 locations->SetInAt(0, Location::Any());
3641 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3642 break;
3643
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003644 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003645 locations->SetInAt(0, Location::RequiresFpuRegister());
3646 locations->SetOut(Location::RequiresRegister());
3647 locations->AddTemp(Location::RequiresFpuRegister());
3648 break;
3649
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003650 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003651 locations->SetInAt(0, Location::RequiresFpuRegister());
3652 locations->SetOut(Location::RequiresRegister());
3653 locations->AddTemp(Location::RequiresFpuRegister());
3654 break;
3655
3656 default:
3657 LOG(FATAL) << "Unexpected type conversion from " << input_type
3658 << " to " << result_type;
3659 }
3660 break;
3661
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003662 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003663 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003664 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003665 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003666 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003667 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003668 case DataType::Type::kInt16:
3669 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003670 locations->SetInAt(0, Location::RequiresRegister());
3671 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3672 break;
3673
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003674 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003675 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3676 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
3677 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003678 break;
3679 }
3680
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003681 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003682 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3683 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0),
3684 calling_convention.GetFpuRegisterAt(1)));
3685 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003686 break;
3687 }
3688
3689 default:
3690 LOG(FATAL) << "Unexpected type conversion from " << input_type
3691 << " to " << result_type;
3692 }
3693 break;
3694
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003695 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003696 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003697 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003698 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003699 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003700 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003701 case DataType::Type::kInt16:
3702 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003703 locations->SetInAt(0, Location::RequiresRegister());
3704 locations->SetOut(Location::RequiresFpuRegister());
3705 break;
3706
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003707 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003708 InvokeRuntimeCallingConventionARMVIXL calling_convention;
3709 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0),
3710 calling_convention.GetRegisterAt(1)));
3711 locations->SetOut(LocationFrom(calling_convention.GetFpuRegisterAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01003712 break;
3713 }
3714
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003715 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003716 locations->SetInAt(0, Location::RequiresFpuRegister());
3717 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3718 break;
3719
3720 default:
3721 LOG(FATAL) << "Unexpected type conversion from " << input_type
3722 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003723 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003724 break;
3725
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003726 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003727 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003728 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003729 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003730 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003731 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003732 case DataType::Type::kInt16:
3733 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003734 locations->SetInAt(0, Location::RequiresRegister());
3735 locations->SetOut(Location::RequiresFpuRegister());
3736 break;
3737
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003738 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003739 locations->SetInAt(0, Location::RequiresRegister());
3740 locations->SetOut(Location::RequiresFpuRegister());
3741 locations->AddTemp(Location::RequiresFpuRegister());
3742 locations->AddTemp(Location::RequiresFpuRegister());
3743 break;
3744
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003745 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003746 locations->SetInAt(0, Location::RequiresFpuRegister());
3747 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3748 break;
3749
3750 default:
3751 LOG(FATAL) << "Unexpected type conversion from " << input_type
3752 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003753 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003754 break;
3755
3756 default:
3757 LOG(FATAL) << "Unexpected type conversion from " << input_type
3758 << " to " << result_type;
3759 }
3760}
3761
3762void InstructionCodeGeneratorARMVIXL::VisitTypeConversion(HTypeConversion* conversion) {
3763 LocationSummary* locations = conversion->GetLocations();
3764 Location out = locations->Out();
3765 Location in = locations->InAt(0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003766 DataType::Type result_type = conversion->GetResultType();
3767 DataType::Type input_type = conversion->GetInputType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003768 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
3769 << input_type << " -> " << result_type;
Scott Wakelingfe885462016-09-22 10:24:38 +01003770 switch (result_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003771 case DataType::Type::kUint8:
Scott Wakelingfe885462016-09-22 10:24:38 +01003772 switch (input_type) {
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003773 case DataType::Type::kInt8:
3774 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003775 case DataType::Type::kInt16:
3776 case DataType::Type::kInt32:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003777 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3778 break;
3779 case DataType::Type::kInt64:
3780 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3781 break;
3782
3783 default:
3784 LOG(FATAL) << "Unexpected type conversion from " << input_type
3785 << " to " << result_type;
3786 }
3787 break;
3788
3789 case DataType::Type::kInt8:
3790 switch (input_type) {
3791 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003792 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003793 case DataType::Type::kInt16:
3794 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003795 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 8);
3796 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003797 case DataType::Type::kInt64:
3798 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 8);
3799 break;
3800
3801 default:
3802 LOG(FATAL) << "Unexpected type conversion from " << input_type
3803 << " to " << result_type;
3804 }
3805 break;
3806
3807 case DataType::Type::kUint16:
3808 switch (input_type) {
3809 case DataType::Type::kInt8:
3810 case DataType::Type::kInt16:
3811 case DataType::Type::kInt32:
3812 __ Ubfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3813 break;
3814 case DataType::Type::kInt64:
3815 __ Ubfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
3816 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003817
3818 default:
3819 LOG(FATAL) << "Unexpected type conversion from " << input_type
3820 << " to " << result_type;
3821 }
3822 break;
3823
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003824 case DataType::Type::kInt16:
Scott Wakelingfe885462016-09-22 10:24:38 +01003825 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003826 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003827 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003828 __ Sbfx(OutputRegister(conversion), InputRegisterAt(conversion, 0), 0, 16);
3829 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003830 case DataType::Type::kInt64:
3831 __ Sbfx(OutputRegister(conversion), LowRegisterFrom(in), 0, 16);
3832 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003833
3834 default:
3835 LOG(FATAL) << "Unexpected type conversion from " << input_type
3836 << " to " << result_type;
3837 }
3838 break;
3839
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003840 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003841 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003842 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003843 DCHECK(out.IsRegister());
3844 if (in.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003845 __ Mov(OutputRegister(conversion), LowRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003846 } else if (in.IsDoubleStackSlot()) {
3847 GetAssembler()->LoadFromOffset(kLoadWord,
3848 OutputRegister(conversion),
3849 sp,
3850 in.GetStackIndex());
3851 } else {
3852 DCHECK(in.IsConstant());
3853 DCHECK(in.GetConstant()->IsLongConstant());
Vladimir Markoba1a48e2017-04-13 11:50:14 +01003854 int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
3855 __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01003856 }
3857 break;
3858
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003859 case DataType::Type::kFloat32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003860 vixl32::SRegister temp = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003861 __ Vcvt(S32, F32, temp, InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003862 __ Vmov(OutputRegister(conversion), temp);
3863 break;
3864 }
3865
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003866 case DataType::Type::kFloat64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003867 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003868 __ Vcvt(S32, F64, temp_s, DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003869 __ Vmov(OutputRegister(conversion), temp_s);
3870 break;
3871 }
3872
3873 default:
3874 LOG(FATAL) << "Unexpected type conversion from " << input_type
3875 << " to " << result_type;
3876 }
3877 break;
3878
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003879 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003880 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003881 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003882 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003883 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003884 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003885 case DataType::Type::kInt16:
3886 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003887 DCHECK(out.IsRegisterPair());
3888 DCHECK(in.IsRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003889 __ Mov(LowRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003890 // Sign extension.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003891 __ Asr(HighRegisterFrom(out), LowRegisterFrom(out), 31);
Scott Wakelingfe885462016-09-22 10:24:38 +01003892 break;
3893
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003894 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003895 codegen_->InvokeRuntime(kQuickF2l, conversion, conversion->GetDexPc());
3896 CheckEntrypointTypes<kQuickF2l, int64_t, float>();
3897 break;
3898
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003899 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003900 codegen_->InvokeRuntime(kQuickD2l, conversion, conversion->GetDexPc());
3901 CheckEntrypointTypes<kQuickD2l, int64_t, double>();
3902 break;
3903
3904 default:
3905 LOG(FATAL) << "Unexpected type conversion from " << input_type
3906 << " to " << result_type;
3907 }
3908 break;
3909
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003910 case DataType::Type::kFloat32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003911 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003912 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003913 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003914 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003915 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003916 case DataType::Type::kInt16:
3917 case DataType::Type::kInt32:
Scott Wakelingfe885462016-09-22 10:24:38 +01003918 __ Vmov(OutputSRegister(conversion), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003919 __ Vcvt(F32, S32, OutputSRegister(conversion), OutputSRegister(conversion));
Scott Wakelingfe885462016-09-22 10:24:38 +01003920 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003921
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003922 case DataType::Type::kInt64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003923 codegen_->InvokeRuntime(kQuickL2f, conversion, conversion->GetDexPc());
3924 CheckEntrypointTypes<kQuickL2f, float, int64_t>();
3925 break;
3926
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003927 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01003928 __ Vcvt(F32, F64, OutputSRegister(conversion), DRegisterFrom(in));
Scott Wakelingfe885462016-09-22 10:24:38 +01003929 break;
3930
3931 default:
3932 LOG(FATAL) << "Unexpected type conversion from " << input_type
3933 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003934 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003935 break;
3936
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003937 case DataType::Type::kFloat64:
Scott Wakelingfe885462016-09-22 10:24:38 +01003938 switch (input_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003939 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003940 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003941 case DataType::Type::kInt8:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003942 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003943 case DataType::Type::kInt16:
3944 case DataType::Type::kInt32:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003945 __ Vmov(LowSRegisterFrom(out), InputRegisterAt(conversion, 0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003946 __ Vcvt(F64, S32, DRegisterFrom(out), LowSRegisterFrom(out));
Scott Wakelingfe885462016-09-22 10:24:38 +01003947 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01003948
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003949 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003950 vixl32::Register low = LowRegisterFrom(in);
3951 vixl32::Register high = HighRegisterFrom(in);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003952 vixl32::SRegister out_s = LowSRegisterFrom(out);
Scott Wakelingc34dba72016-10-03 10:14:44 +01003953 vixl32::DRegister out_d = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003954 vixl32::SRegister temp_s = LowSRegisterFrom(locations->GetTemp(0));
Scott Wakelingc34dba72016-10-03 10:14:44 +01003955 vixl32::DRegister temp_d = DRegisterFrom(locations->GetTemp(0));
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003956 vixl32::DRegister constant_d = DRegisterFrom(locations->GetTemp(1));
Scott Wakelingfe885462016-09-22 10:24:38 +01003957
3958 // temp_d = int-to-double(high)
3959 __ Vmov(temp_s, high);
Scott Wakelingfb0b7d42016-10-28 16:11:08 +01003960 __ Vcvt(F64, S32, temp_d, temp_s);
Scott Wakelingfe885462016-09-22 10:24:38 +01003961 // constant_d = k2Pow32EncodingForDouble
Scott Wakelinga7812ae2016-10-17 10:03:36 +01003962 __ Vmov(constant_d, bit_cast<double, int64_t>(k2Pow32EncodingForDouble));
Scott Wakelingfe885462016-09-22 10:24:38 +01003963 // out_d = unsigned-to-double(low)
3964 __ Vmov(out_s, low);
3965 __ Vcvt(F64, U32, out_d, out_s);
3966 // out_d += temp_d * constant_d
3967 __ Vmla(F64, out_d, temp_d, constant_d);
3968 break;
3969 }
3970
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003971 case DataType::Type::kFloat32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01003972 __ Vcvt(F64, F32, DRegisterFrom(out), InputSRegisterAt(conversion, 0));
Scott Wakelingfe885462016-09-22 10:24:38 +01003973 break;
3974
3975 default:
3976 LOG(FATAL) << "Unexpected type conversion from " << input_type
3977 << " to " << result_type;
Igor Murashkin2ffb7032017-11-08 13:35:21 -08003978 }
Scott Wakelingfe885462016-09-22 10:24:38 +01003979 break;
3980
3981 default:
3982 LOG(FATAL) << "Unexpected type conversion from " << input_type
3983 << " to " << result_type;
3984 }
3985}
3986
3987void LocationsBuilderARMVIXL::VisitAdd(HAdd* add) {
3988 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003989 new (GetGraph()->GetAllocator()) LocationSummary(add, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01003990 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003991 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003992 locations->SetInAt(0, Location::RequiresRegister());
3993 locations->SetInAt(1, Location::RegisterOrConstant(add->InputAt(1)));
3994 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3995 break;
3996 }
3997
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003998 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01003999 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004000 locations->SetInAt(1, ArmEncodableConstantOrRegister(add->InputAt(1), ADD));
Scott Wakelingfe885462016-09-22 10:24:38 +01004001 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4002 break;
4003 }
4004
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004005 case DataType::Type::kFloat32:
4006 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004007 locations->SetInAt(0, Location::RequiresFpuRegister());
4008 locations->SetInAt(1, Location::RequiresFpuRegister());
4009 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4010 break;
4011 }
4012
4013 default:
4014 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4015 }
4016}
4017
4018void InstructionCodeGeneratorARMVIXL::VisitAdd(HAdd* add) {
4019 LocationSummary* locations = add->GetLocations();
4020 Location out = locations->Out();
4021 Location first = locations->InAt(0);
4022 Location second = locations->InAt(1);
4023
4024 switch (add->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004025 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004026 __ Add(OutputRegister(add), InputRegisterAt(add, 0), InputOperandAt(add, 1));
4027 }
4028 break;
4029
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004030 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004031 if (second.IsConstant()) {
4032 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4033 GenerateAddLongConst(out, first, value);
4034 } else {
4035 DCHECK(second.IsRegisterPair());
4036 __ Adds(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4037 __ Adc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4038 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004039 break;
4040 }
4041
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004042 case DataType::Type::kFloat32:
4043 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004044 __ Vadd(OutputVRegister(add), InputVRegisterAt(add, 0), InputVRegisterAt(add, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004045 break;
4046
4047 default:
4048 LOG(FATAL) << "Unexpected add type " << add->GetResultType();
4049 }
4050}
4051
4052void LocationsBuilderARMVIXL::VisitSub(HSub* sub) {
4053 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004054 new (GetGraph()->GetAllocator()) LocationSummary(sub, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004055 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004056 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004057 locations->SetInAt(0, Location::RequiresRegister());
4058 locations->SetInAt(1, Location::RegisterOrConstant(sub->InputAt(1)));
4059 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4060 break;
4061 }
4062
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004063 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004064 locations->SetInAt(0, Location::RequiresRegister());
Anton Kirilovdda43962016-11-21 19:55:20 +00004065 locations->SetInAt(1, ArmEncodableConstantOrRegister(sub->InputAt(1), SUB));
Scott Wakelingfe885462016-09-22 10:24:38 +01004066 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4067 break;
4068 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004069 case DataType::Type::kFloat32:
4070 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004071 locations->SetInAt(0, Location::RequiresFpuRegister());
4072 locations->SetInAt(1, Location::RequiresFpuRegister());
4073 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4074 break;
4075 }
4076 default:
4077 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4078 }
4079}
4080
4081void InstructionCodeGeneratorARMVIXL::VisitSub(HSub* sub) {
4082 LocationSummary* locations = sub->GetLocations();
4083 Location out = locations->Out();
4084 Location first = locations->InAt(0);
4085 Location second = locations->InAt(1);
4086 switch (sub->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004087 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004088 __ Sub(OutputRegister(sub), InputRegisterAt(sub, 0), InputOperandAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004089 break;
4090 }
4091
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004092 case DataType::Type::kInt64: {
Anton Kirilovdda43962016-11-21 19:55:20 +00004093 if (second.IsConstant()) {
4094 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
4095 GenerateAddLongConst(out, first, -value);
4096 } else {
4097 DCHECK(second.IsRegisterPair());
4098 __ Subs(LowRegisterFrom(out), LowRegisterFrom(first), LowRegisterFrom(second));
4099 __ Sbc(HighRegisterFrom(out), HighRegisterFrom(first), HighRegisterFrom(second));
4100 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004101 break;
4102 }
4103
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004104 case DataType::Type::kFloat32:
4105 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004106 __ Vsub(OutputVRegister(sub), InputVRegisterAt(sub, 0), InputVRegisterAt(sub, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004107 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004108
4109 default:
4110 LOG(FATAL) << "Unexpected sub type " << sub->GetResultType();
4111 }
4112}
4113
4114void LocationsBuilderARMVIXL::VisitMul(HMul* mul) {
4115 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004116 new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
Scott Wakelingfe885462016-09-22 10:24:38 +01004117 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004118 case DataType::Type::kInt32:
4119 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004120 locations->SetInAt(0, Location::RequiresRegister());
4121 locations->SetInAt(1, Location::RequiresRegister());
4122 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4123 break;
4124 }
4125
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004126 case DataType::Type::kFloat32:
4127 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004128 locations->SetInAt(0, Location::RequiresFpuRegister());
4129 locations->SetInAt(1, Location::RequiresFpuRegister());
4130 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4131 break;
4132 }
4133
4134 default:
4135 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4136 }
4137}
4138
4139void InstructionCodeGeneratorARMVIXL::VisitMul(HMul* mul) {
4140 LocationSummary* locations = mul->GetLocations();
4141 Location out = locations->Out();
4142 Location first = locations->InAt(0);
4143 Location second = locations->InAt(1);
4144 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004145 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004146 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
4147 break;
4148 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004149 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004150 vixl32::Register out_hi = HighRegisterFrom(out);
4151 vixl32::Register out_lo = LowRegisterFrom(out);
4152 vixl32::Register in1_hi = HighRegisterFrom(first);
4153 vixl32::Register in1_lo = LowRegisterFrom(first);
4154 vixl32::Register in2_hi = HighRegisterFrom(second);
4155 vixl32::Register in2_lo = LowRegisterFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004156
4157 // Extra checks to protect caused by the existence of R1_R2.
4158 // The algorithm is wrong if out.hi is either in1.lo or in2.lo:
4159 // (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
Anton Kirilov644032c2016-12-06 17:51:43 +00004160 DCHECK(!out_hi.Is(in1_lo));
4161 DCHECK(!out_hi.Is(in2_lo));
Scott Wakelingfe885462016-09-22 10:24:38 +01004162
4163 // input: in1 - 64 bits, in2 - 64 bits
4164 // output: out
4165 // formula: out.hi : out.lo = (in1.lo * in2.hi + in1.hi * in2.lo)* 2^32 + in1.lo * in2.lo
4166 // parts: out.hi = in1.lo * in2.hi + in1.hi * in2.lo + (in1.lo * in2.lo)[63:32]
4167 // parts: out.lo = (in1.lo * in2.lo)[31:0]
4168
4169 UseScratchRegisterScope temps(GetVIXLAssembler());
4170 vixl32::Register temp = temps.Acquire();
4171 // temp <- in1.lo * in2.hi
4172 __ Mul(temp, in1_lo, in2_hi);
4173 // out.hi <- in1.lo * in2.hi + in1.hi * in2.lo
4174 __ Mla(out_hi, in1_hi, in2_lo, temp);
4175 // out.lo <- (in1.lo * in2.lo)[31:0];
4176 __ Umull(out_lo, temp, in1_lo, in2_lo);
4177 // out.hi <- in2.hi * in1.lo + in2.lo * in1.hi + (in1.lo * in2.lo)[63:32]
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004178 __ Add(out_hi, out_hi, temp);
Scott Wakelingfe885462016-09-22 10:24:38 +01004179 break;
4180 }
4181
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004182 case DataType::Type::kFloat32:
4183 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004184 __ Vmul(OutputVRegister(mul), InputVRegisterAt(mul, 0), InputVRegisterAt(mul, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004185 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004186
4187 default:
4188 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
4189 }
4190}
4191
Scott Wakelingfe885462016-09-22 10:24:38 +01004192void InstructionCodeGeneratorARMVIXL::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
4193 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004194 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004195
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004196 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004197 DCHECK(second.IsConstant());
4198
4199 vixl32::Register out = OutputRegister(instruction);
4200 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00004201 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004202 DCHECK(imm == 1 || imm == -1);
4203
4204 if (instruction->IsRem()) {
4205 __ Mov(out, 0);
4206 } else {
4207 if (imm == 1) {
4208 __ Mov(out, dividend);
4209 } else {
4210 __ Rsb(out, dividend, 0);
4211 }
4212 }
4213}
4214
4215void InstructionCodeGeneratorARMVIXL::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
4216 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004217 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004218
4219 LocationSummary* locations = instruction->GetLocations();
4220 Location second = locations->InAt(1);
4221 DCHECK(second.IsConstant());
4222
4223 vixl32::Register out = OutputRegister(instruction);
4224 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Anton Kirilov644032c2016-12-06 17:51:43 +00004225 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004226 uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
4227 int ctz_imm = CTZ(abs_imm);
4228
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004229 auto generate_div_code = [this, imm, ctz_imm](vixl32::Register out, vixl32::Register in) {
4230 __ Asr(out, in, ctz_imm);
Scott Wakelingfe885462016-09-22 10:24:38 +01004231 if (imm < 0) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004232 __ Rsb(out, out, 0);
Scott Wakelingfe885462016-09-22 10:24:38 +01004233 }
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004234 };
4235
4236 if (HasNonNegativeResultOrMinInt(instruction->GetLeft())) {
4237 // No need to adjust the result for non-negative dividends or the INT32_MIN dividend.
4238 // NOTE: The generated code for HDiv/HRem correctly works for the INT32_MIN dividend:
4239 // imm == 2
4240 // HDiv
4241 // add out, dividend(0x80000000), dividend(0x80000000), lsr #31 => out = 0x80000001
4242 // asr out, out(0x80000001), #1 => out = 0xc0000000
4243 // This is the same as 'asr out, dividend(0x80000000), #1'
4244 //
4245 // imm > 2
4246 // HDiv
4247 // asr out, dividend(0x80000000), #31 => out = -1
4248 // add out, dividend(0x80000000), out(-1), lsr #(32 - ctz_imm) => out = 0b10..01..1,
4249 // where the number of the rightmost 1s is ctz_imm.
4250 // asr out, out(0b10..01..1), #ctz_imm => out = 0b1..10..0, where the number of the
4251 // leftmost 1s is ctz_imm + 1.
4252 // This is the same as 'asr out, dividend(0x80000000), #ctz_imm'.
4253 //
4254 // imm == INT32_MIN
4255 // HDiv
4256 // asr out, dividend(0x80000000), #31 => out = -1
4257 // add out, dividend(0x80000000), out(-1), lsr #1 => out = 0xc0000000
4258 // asr out, out(0xc0000000), #31 => out = -1
4259 // rsb out, out(-1), #0 => out = 1
4260 // This is the same as
4261 // asr out, dividend(0x80000000), #31
4262 // rsb out, out, #0
4263 //
4264 //
4265 // INT_MIN % imm must be 0 for any imm of power 2. 'and' and 'ubfx' work only with bits
4266 // 0..30 of a dividend. For INT32_MIN those bits are zeros. So 'and' and 'ubfx' always
4267 // produce zero.
4268 if (instruction->IsDiv()) {
4269 generate_div_code(out, dividend);
4270 } else {
4271 if (GetVIXLAssembler()->IsModifiedImmediate(abs_imm - 1)) {
4272 __ And(out, dividend, abs_imm - 1);
4273 } else {
4274 __ Ubfx(out, dividend, 0, ctz_imm);
4275 }
4276 return;
4277 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004278 } else {
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004279 vixl32::Register add_right_input = dividend;
4280 if (ctz_imm > 1) {
4281 __ Asr(out, dividend, 31);
4282 add_right_input = out;
4283 }
4284 __ Add(out, dividend, Operand(add_right_input, vixl32::LSR, 32 - ctz_imm));
4285
4286 if (instruction->IsDiv()) {
4287 generate_div_code(out, out);
4288 } else {
4289 __ Bfc(out, 0, ctz_imm);
4290 __ Sub(out, dividend, out);
4291 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004292 }
4293}
4294
4295void InstructionCodeGeneratorARMVIXL::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
4296 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004297 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004298
4299 LocationSummary* locations = instruction->GetLocations();
4300 Location second = locations->InAt(1);
4301 DCHECK(second.IsConstant());
4302
4303 vixl32::Register out = OutputRegister(instruction);
4304 vixl32::Register dividend = InputRegisterAt(instruction, 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004305 vixl32::Register temp1 = RegisterFrom(locations->GetTemp(0));
4306 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(1));
Scott Wakelingb77051e2016-11-21 19:46:00 +00004307 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004308
4309 int64_t magic;
4310 int shift;
Andreas Gampe3db70682018-12-26 15:12:03 -08004311 CalculateMagicAndShiftForDivRem(imm, /* is_long= */ false, &magic, &shift);
Scott Wakelingfe885462016-09-22 10:24:38 +01004312
Evgeny Astigeevichf9388412020-07-02 15:25:13 +01004313 auto generate_unsigned_div_code =[this, magic, shift](vixl32::Register out,
4314 vixl32::Register dividend,
4315 vixl32::Register temp1,
4316 vixl32::Register temp2) {
4317 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4318 __ Mov(temp1, static_cast<int32_t>(magic));
4319 if (magic > 0 && shift == 0) {
4320 __ Smull(temp2, out, dividend, temp1);
4321 } else {
4322 __ Smull(temp2, temp1, dividend, temp1);
4323 if (magic < 0) {
4324 // The negative magic M = static_cast<int>(m) means that the multiplier m is greater
4325 // than INT32_MAX. In such a case shift is never 0.
4326 // Proof:
4327 // m = (2^p + d - 2^p % d) / d, where p = 32 + shift, d > 2
4328 //
4329 // If shift == 0, m = (2^32 + d - 2^32 % d) / d =
4330 // = (2^32 + d - (2^32 - (2^32 / d) * d)) / d =
4331 // = (d + (2^32 / d) * d) / d = 1 + (2^32 / d), here '/' is the integer division.
4332 //
4333 // 1 + (2^32 / d) is decreasing when d is increasing.
4334 // The maximum is 1 431 655 766, when d == 3. This value is less than INT32_MAX.
4335 // the minimum is 3, when d = 2^31 -1.
4336 // So for all values of d in [3, INT32_MAX] m with p == 32 is in [3, INT32_MAX) and
4337 // is never less than 0.
4338 __ Add(temp1, temp1, dividend);
4339 }
4340 DCHECK_NE(shift, 0);
4341 __ Lsr(out, temp1, shift);
4342 }
4343 };
Scott Wakelingfe885462016-09-22 10:24:38 +01004344
Evgeny Astigeevichf9388412020-07-02 15:25:13 +01004345 if (imm > 0 && IsGEZero(instruction->GetLeft())) {
4346 // No need to adjust the result for a non-negative dividend and a positive divisor.
4347 if (instruction->IsDiv()) {
4348 generate_unsigned_div_code(out, dividend, temp1, temp2);
4349 } else {
4350 generate_unsigned_div_code(temp1, dividend, temp1, temp2);
4351 __ Mov(temp2, imm);
4352 __ Mls(out, temp1, temp2, dividend);
4353 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004354 } else {
Evgeny Astigeevichf9388412020-07-02 15:25:13 +01004355 // TODO(VIXL): Change the static cast to Operand::From() after VIXL is fixed.
4356 __ Mov(temp1, static_cast<int32_t>(magic));
4357 __ Smull(temp2, temp1, dividend, temp1);
4358
4359 if (imm > 0 && magic < 0) {
4360 __ Add(temp1, temp1, dividend);
4361 } else if (imm < 0 && magic > 0) {
4362 __ Sub(temp1, temp1, dividend);
4363 }
4364
4365 if (shift != 0) {
4366 __ Asr(temp1, temp1, shift);
4367 }
4368
4369 if (instruction->IsDiv()) {
4370 __ Sub(out, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4371 } else {
4372 __ Sub(temp1, temp1, Operand(temp1, vixl32::Shift(ASR), 31));
4373 // TODO: Strength reduction for mls.
4374 __ Mov(temp2, imm);
4375 __ Mls(out, temp1, temp2, dividend);
4376 }
Scott Wakelingfe885462016-09-22 10:24:38 +01004377 }
4378}
4379
4380void InstructionCodeGeneratorARMVIXL::GenerateDivRemConstantIntegral(
4381 HBinaryOperation* instruction) {
4382 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004383 DCHECK(instruction->GetResultType() == DataType::Type::kInt32);
Scott Wakelingfe885462016-09-22 10:24:38 +01004384
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004385 Location second = instruction->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004386 DCHECK(second.IsConstant());
4387
Anton Kirilov644032c2016-12-06 17:51:43 +00004388 int32_t imm = Int32ConstantFrom(second);
Scott Wakelingfe885462016-09-22 10:24:38 +01004389 if (imm == 0) {
4390 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
4391 } else if (imm == 1 || imm == -1) {
4392 DivRemOneOrMinusOne(instruction);
4393 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
4394 DivRemByPowerOfTwo(instruction);
4395 } else {
4396 DCHECK(imm <= -2 || imm >= 2);
4397 GenerateDivRemWithAnyConstant(instruction);
4398 }
4399}
4400
4401void LocationsBuilderARMVIXL::VisitDiv(HDiv* div) {
4402 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004403 if (div->GetResultType() == DataType::Type::kInt64) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004404 // pLdiv runtime call.
4405 call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004406 } else if (div->GetResultType() == DataType::Type::kInt32 && div->InputAt(1)->IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004407 // sdiv will be replaced by other instruction sequence.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004408 } else if (div->GetResultType() == DataType::Type::kInt32 &&
Scott Wakelingfe885462016-09-22 10:24:38 +01004409 !codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4410 // pIdivmod runtime call.
4411 call_kind = LocationSummary::kCallOnMainOnly;
4412 }
4413
Vladimir Markoca6fff82017-10-03 14:49:14 +01004414 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(div, call_kind);
Scott Wakelingfe885462016-09-22 10:24:38 +01004415
4416 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004417 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004418 if (div->InputAt(1)->IsConstant()) {
4419 locations->SetInAt(0, Location::RequiresRegister());
4420 locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
Anton Kirilov644032c2016-12-06 17:51:43 +00004421 int32_t value = Int32ConstantFrom(div->InputAt(1));
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004422 Location::OutputOverlap out_overlaps = Location::kNoOutputOverlap;
Scott Wakelingfe885462016-09-22 10:24:38 +01004423 if (value == 1 || value == 0 || value == -1) {
4424 // No temp register required.
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004425 } else if (IsPowerOfTwo(AbsOrMin(value)) &&
4426 value != 2 &&
4427 value != -2 &&
4428 !HasNonNegativeResultOrMinInt(div)) {
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004429 // The "out" register is used as a temporary, so it overlaps with the inputs.
4430 out_overlaps = Location::kOutputOverlap;
Scott Wakelingfe885462016-09-22 10:24:38 +01004431 } else {
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004432 locations->AddRegisterTemps(2);
Scott Wakelingfe885462016-09-22 10:24:38 +01004433 }
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004434 locations->SetOut(Location::RequiresRegister(), out_overlaps);
Scott Wakelingfe885462016-09-22 10:24:38 +01004435 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4436 locations->SetInAt(0, Location::RequiresRegister());
4437 locations->SetInAt(1, Location::RequiresRegister());
4438 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4439 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004440 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4441 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4442 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004443 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004444 // we only need the former.
4445 locations->SetOut(LocationFrom(r0));
Scott Wakelingfe885462016-09-22 10:24:38 +01004446 }
4447 break;
4448 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004449 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004450 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4451 locations->SetInAt(0, LocationFrom(
4452 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4453 locations->SetInAt(1, LocationFrom(
4454 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4455 locations->SetOut(LocationFrom(r0, r1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004456 break;
4457 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004458 case DataType::Type::kFloat32:
4459 case DataType::Type::kFloat64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004460 locations->SetInAt(0, Location::RequiresFpuRegister());
4461 locations->SetInAt(1, Location::RequiresFpuRegister());
4462 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4463 break;
4464 }
4465
4466 default:
4467 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4468 }
4469}
4470
4471void InstructionCodeGeneratorARMVIXL::VisitDiv(HDiv* div) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004472 Location lhs = div->GetLocations()->InAt(0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004473 Location rhs = div->GetLocations()->InAt(1);
Scott Wakelingfe885462016-09-22 10:24:38 +01004474
4475 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004476 case DataType::Type::kInt32: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004477 if (rhs.IsConstant()) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004478 GenerateDivRemConstantIntegral(div);
4479 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4480 __ Sdiv(OutputRegister(div), InputRegisterAt(div, 0), InputRegisterAt(div, 1));
4481 } else {
Artem Serov551b28f2016-10-18 19:11:30 +01004482 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4483 DCHECK(calling_convention.GetRegisterAt(0).Is(RegisterFrom(lhs)));
4484 DCHECK(calling_convention.GetRegisterAt(1).Is(RegisterFrom(rhs)));
4485 DCHECK(r0.Is(OutputRegister(div)));
4486
4487 codegen_->InvokeRuntime(kQuickIdivmod, div, div->GetDexPc());
4488 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004489 }
4490 break;
4491 }
4492
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004493 case DataType::Type::kInt64: {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01004494 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4495 DCHECK(calling_convention.GetRegisterAt(0).Is(LowRegisterFrom(lhs)));
4496 DCHECK(calling_convention.GetRegisterAt(1).Is(HighRegisterFrom(lhs)));
4497 DCHECK(calling_convention.GetRegisterAt(2).Is(LowRegisterFrom(rhs)));
4498 DCHECK(calling_convention.GetRegisterAt(3).Is(HighRegisterFrom(rhs)));
4499 DCHECK(LowRegisterFrom(div->GetLocations()->Out()).Is(r0));
4500 DCHECK(HighRegisterFrom(div->GetLocations()->Out()).Is(r1));
4501
4502 codegen_->InvokeRuntime(kQuickLdiv, div, div->GetDexPc());
4503 CheckEntrypointTypes<kQuickLdiv, int64_t, int64_t, int64_t>();
Scott Wakelingfe885462016-09-22 10:24:38 +01004504 break;
4505 }
4506
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004507 case DataType::Type::kFloat32:
4508 case DataType::Type::kFloat64:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004509 __ Vdiv(OutputVRegister(div), InputVRegisterAt(div, 0), InputVRegisterAt(div, 1));
Scott Wakelingfe885462016-09-22 10:24:38 +01004510 break;
Scott Wakelingfe885462016-09-22 10:24:38 +01004511
4512 default:
4513 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
4514 }
4515}
4516
Artem Serov551b28f2016-10-18 19:11:30 +01004517void LocationsBuilderARMVIXL::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004518 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004519
4520 // Most remainders are implemented in the runtime.
4521 LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004522 if (rem->GetResultType() == DataType::Type::kInt32 && rem->InputAt(1)->IsConstant()) {
Artem Serov551b28f2016-10-18 19:11:30 +01004523 // sdiv will be replaced by other instruction sequence.
4524 call_kind = LocationSummary::kNoCall;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004525 } else if ((rem->GetResultType() == DataType::Type::kInt32)
Artem Serov551b28f2016-10-18 19:11:30 +01004526 && codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4527 // Have hardware divide instruction for int, do it with three instructions.
4528 call_kind = LocationSummary::kNoCall;
4529 }
4530
Vladimir Markoca6fff82017-10-03 14:49:14 +01004531 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
Artem Serov551b28f2016-10-18 19:11:30 +01004532
4533 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004534 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004535 if (rem->InputAt(1)->IsConstant()) {
4536 locations->SetInAt(0, Location::RequiresRegister());
4537 locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
Anton Kirilov644032c2016-12-06 17:51:43 +00004538 int32_t value = Int32ConstantFrom(rem->InputAt(1));
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004539 Location::OutputOverlap out_overlaps = Location::kNoOutputOverlap;
Artem Serov551b28f2016-10-18 19:11:30 +01004540 if (value == 1 || value == 0 || value == -1) {
4541 // No temp register required.
Evgeny Astigeevichaf92a0f2020-06-26 13:28:33 +01004542 } else if (IsPowerOfTwo(AbsOrMin(value)) && !HasNonNegativeResultOrMinInt(rem)) {
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004543 // The "out" register is used as a temporary, so it overlaps with the inputs.
4544 out_overlaps = Location::kOutputOverlap;
Artem Serov551b28f2016-10-18 19:11:30 +01004545 } else {
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004546 locations->AddRegisterTemps(2);
Artem Serov551b28f2016-10-18 19:11:30 +01004547 }
Evgeny Astigeevich2d101172020-06-25 16:52:03 +01004548 locations->SetOut(Location::RequiresRegister(), out_overlaps);
Artem Serov551b28f2016-10-18 19:11:30 +01004549 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4550 locations->SetInAt(0, Location::RequiresRegister());
4551 locations->SetInAt(1, Location::RequiresRegister());
4552 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4553 locations->AddTemp(Location::RequiresRegister());
4554 } else {
4555 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4556 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
4557 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Roland Levillain5e8d5f02016-10-18 18:03:43 +01004558 // Note: divmod will compute both the quotient and the remainder as the pair R0 and R1, but
Artem Serov551b28f2016-10-18 19:11:30 +01004559 // we only need the latter.
4560 locations->SetOut(LocationFrom(r1));
4561 }
4562 break;
4563 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004564 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004565 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4566 locations->SetInAt(0, LocationFrom(
4567 calling_convention.GetRegisterAt(0), calling_convention.GetRegisterAt(1)));
4568 locations->SetInAt(1, LocationFrom(
4569 calling_convention.GetRegisterAt(2), calling_convention.GetRegisterAt(3)));
4570 // The runtime helper puts the output in R2,R3.
4571 locations->SetOut(LocationFrom(r2, r3));
4572 break;
4573 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004574 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004575 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4576 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
4577 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
4578 locations->SetOut(LocationFrom(s0));
4579 break;
4580 }
4581
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004582 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004583 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4584 locations->SetInAt(0, LocationFrom(
4585 calling_convention.GetFpuRegisterAt(0), calling_convention.GetFpuRegisterAt(1)));
4586 locations->SetInAt(1, LocationFrom(
4587 calling_convention.GetFpuRegisterAt(2), calling_convention.GetFpuRegisterAt(3)));
4588 locations->SetOut(LocationFrom(s0, s1));
4589 break;
4590 }
4591
4592 default:
4593 LOG(FATAL) << "Unexpected rem type " << type;
4594 }
4595}
4596
4597void InstructionCodeGeneratorARMVIXL::VisitRem(HRem* rem) {
4598 LocationSummary* locations = rem->GetLocations();
4599 Location second = locations->InAt(1);
4600
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004601 DataType::Type type = rem->GetResultType();
Artem Serov551b28f2016-10-18 19:11:30 +01004602 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004603 case DataType::Type::kInt32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004604 vixl32::Register reg1 = InputRegisterAt(rem, 0);
4605 vixl32::Register out_reg = OutputRegister(rem);
4606 if (second.IsConstant()) {
4607 GenerateDivRemConstantIntegral(rem);
4608 } else if (codegen_->GetInstructionSetFeatures().HasDivideInstruction()) {
4609 vixl32::Register reg2 = RegisterFrom(second);
4610 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
4611
4612 // temp = reg1 / reg2 (integer division)
4613 // dest = reg1 - temp * reg2
4614 __ Sdiv(temp, reg1, reg2);
4615 __ Mls(out_reg, temp, reg2, reg1);
4616 } else {
4617 InvokeRuntimeCallingConventionARMVIXL calling_convention;
4618 DCHECK(reg1.Is(calling_convention.GetRegisterAt(0)));
4619 DCHECK(RegisterFrom(second).Is(calling_convention.GetRegisterAt(1)));
4620 DCHECK(out_reg.Is(r1));
4621
4622 codegen_->InvokeRuntime(kQuickIdivmod, rem, rem->GetDexPc());
4623 CheckEntrypointTypes<kQuickIdivmod, int32_t, int32_t, int32_t>();
4624 }
4625 break;
4626 }
4627
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004628 case DataType::Type::kInt64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004629 codegen_->InvokeRuntime(kQuickLmod, rem, rem->GetDexPc());
4630 CheckEntrypointTypes<kQuickLmod, int64_t, int64_t, int64_t>();
4631 break;
4632 }
4633
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004634 case DataType::Type::kFloat32: {
Artem Serov551b28f2016-10-18 19:11:30 +01004635 codegen_->InvokeRuntime(kQuickFmodf, rem, rem->GetDexPc());
4636 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
4637 break;
4638 }
4639
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004640 case DataType::Type::kFloat64: {
Artem Serov551b28f2016-10-18 19:11:30 +01004641 codegen_->InvokeRuntime(kQuickFmod, rem, rem->GetDexPc());
4642 CheckEntrypointTypes<kQuickFmod, double, double, double>();
4643 break;
4644 }
4645
4646 default:
4647 LOG(FATAL) << "Unexpected rem type " << type;
4648 }
4649}
4650
Aart Bik1f8d51b2018-02-15 10:42:37 -08004651static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
4652 LocationSummary* locations = new (allocator) LocationSummary(minmax);
4653 switch (minmax->GetResultType()) {
4654 case DataType::Type::kInt32:
4655 locations->SetInAt(0, Location::RequiresRegister());
4656 locations->SetInAt(1, Location::RequiresRegister());
4657 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4658 break;
4659 case DataType::Type::kInt64:
4660 locations->SetInAt(0, Location::RequiresRegister());
4661 locations->SetInAt(1, Location::RequiresRegister());
4662 locations->SetOut(Location::SameAsFirstInput());
4663 break;
4664 case DataType::Type::kFloat32:
4665 locations->SetInAt(0, Location::RequiresFpuRegister());
4666 locations->SetInAt(1, Location::RequiresFpuRegister());
4667 locations->SetOut(Location::SameAsFirstInput());
4668 locations->AddTemp(Location::RequiresRegister());
4669 break;
4670 case DataType::Type::kFloat64:
4671 locations->SetInAt(0, Location::RequiresFpuRegister());
4672 locations->SetInAt(1, Location::RequiresFpuRegister());
4673 locations->SetOut(Location::SameAsFirstInput());
4674 break;
4675 default:
4676 LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
4677 }
4678}
4679
Aart Bik351df3e2018-03-07 11:54:57 -08004680void InstructionCodeGeneratorARMVIXL::GenerateMinMaxInt(LocationSummary* locations, bool is_min) {
Aart Bik1f8d51b2018-02-15 10:42:37 -08004681 Location op1_loc = locations->InAt(0);
4682 Location op2_loc = locations->InAt(1);
4683 Location out_loc = locations->Out();
4684
4685 vixl32::Register op1 = RegisterFrom(op1_loc);
4686 vixl32::Register op2 = RegisterFrom(op2_loc);
4687 vixl32::Register out = RegisterFrom(out_loc);
4688
4689 __ Cmp(op1, op2);
4690
4691 {
4692 ExactAssemblyScope aas(GetVIXLAssembler(),
4693 3 * kMaxInstructionSizeInBytes,
4694 CodeBufferCheckScope::kMaximumSize);
4695
4696 __ ite(is_min ? lt : gt);
4697 __ mov(is_min ? lt : gt, out, op1);
4698 __ mov(is_min ? ge : le, out, op2);
4699 }
4700}
4701
4702void InstructionCodeGeneratorARMVIXL::GenerateMinMaxLong(LocationSummary* locations, bool is_min) {
4703 Location op1_loc = locations->InAt(0);
4704 Location op2_loc = locations->InAt(1);
4705 Location out_loc = locations->Out();
4706
4707 // Optimization: don't generate any code if inputs are the same.
4708 if (op1_loc.Equals(op2_loc)) {
4709 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4710 return;
4711 }
4712
4713 vixl32::Register op1_lo = LowRegisterFrom(op1_loc);
4714 vixl32::Register op1_hi = HighRegisterFrom(op1_loc);
4715 vixl32::Register op2_lo = LowRegisterFrom(op2_loc);
4716 vixl32::Register op2_hi = HighRegisterFrom(op2_loc);
4717 vixl32::Register out_lo = LowRegisterFrom(out_loc);
4718 vixl32::Register out_hi = HighRegisterFrom(out_loc);
4719 UseScratchRegisterScope temps(GetVIXLAssembler());
4720 const vixl32::Register temp = temps.Acquire();
4721
4722 DCHECK(op1_lo.Is(out_lo));
4723 DCHECK(op1_hi.Is(out_hi));
4724
4725 // Compare op1 >= op2, or op1 < op2.
4726 __ Cmp(out_lo, op2_lo);
4727 __ Sbcs(temp, out_hi, op2_hi);
4728
4729 // Now GE/LT condition code is correct for the long comparison.
4730 {
4731 vixl32::ConditionType cond = is_min ? ge : lt;
4732 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4733 3 * kMaxInstructionSizeInBytes,
4734 CodeBufferCheckScope::kMaximumSize);
4735 __ itt(cond);
4736 __ mov(cond, out_lo, op2_lo);
4737 __ mov(cond, out_hi, op2_hi);
4738 }
4739}
4740
Aart Bik351df3e2018-03-07 11:54:57 -08004741void InstructionCodeGeneratorARMVIXL::GenerateMinMaxFloat(HInstruction* minmax, bool is_min) {
4742 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004743 Location op1_loc = locations->InAt(0);
4744 Location op2_loc = locations->InAt(1);
4745 Location out_loc = locations->Out();
4746
4747 // Optimization: don't generate any code if inputs are the same.
4748 if (op1_loc.Equals(op2_loc)) {
4749 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in location builder.
4750 return;
4751 }
4752
4753 vixl32::SRegister op1 = SRegisterFrom(op1_loc);
4754 vixl32::SRegister op2 = SRegisterFrom(op2_loc);
4755 vixl32::SRegister out = SRegisterFrom(out_loc);
4756
4757 UseScratchRegisterScope temps(GetVIXLAssembler());
4758 const vixl32::Register temp1 = temps.Acquire();
4759 vixl32::Register temp2 = RegisterFrom(locations->GetTemp(0));
4760 vixl32::Label nan, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004761 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004762
4763 DCHECK(op1.Is(out));
4764
4765 __ Vcmp(op1, op2);
4766 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
Andreas Gampe3db70682018-12-26 15:12:03 -08004767 __ B(vs, &nan, /* is_far_target= */ false); // if un-ordered, go to NaN handling.
Aart Bik1f8d51b2018-02-15 10:42:37 -08004768
4769 // op1 <> op2
4770 vixl32::ConditionType cond = is_min ? gt : lt;
4771 {
4772 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4773 2 * kMaxInstructionSizeInBytes,
4774 CodeBufferCheckScope::kMaximumSize);
4775 __ it(cond);
4776 __ vmov(cond, F32, out, op2);
4777 }
4778 // for <>(not equal), we've done min/max calculation.
Andreas Gampe3db70682018-12-26 15:12:03 -08004779 __ B(ne, final_label, /* is_far_target= */ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004780
4781 // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0).
4782 __ Vmov(temp1, op1);
4783 __ Vmov(temp2, op2);
4784 if (is_min) {
4785 __ Orr(temp1, temp1, temp2);
4786 } else {
4787 __ And(temp1, temp1, temp2);
4788 }
4789 __ Vmov(out, temp1);
4790 __ B(final_label);
4791
4792 // handle NaN input.
4793 __ Bind(&nan);
4794 __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN.
4795 __ Vmov(out, temp1);
4796
4797 if (done.IsReferenced()) {
4798 __ Bind(&done);
4799 }
4800}
4801
Aart Bik351df3e2018-03-07 11:54:57 -08004802void InstructionCodeGeneratorARMVIXL::GenerateMinMaxDouble(HInstruction* minmax, bool is_min) {
4803 LocationSummary* locations = minmax->GetLocations();
Aart Bik1f8d51b2018-02-15 10:42:37 -08004804 Location op1_loc = locations->InAt(0);
4805 Location op2_loc = locations->InAt(1);
4806 Location out_loc = locations->Out();
4807
4808 // Optimization: don't generate any code if inputs are the same.
4809 if (op1_loc.Equals(op2_loc)) {
4810 DCHECK(out_loc.Equals(op1_loc)); // out_loc is set as SameAsFirstInput() in.
4811 return;
4812 }
4813
4814 vixl32::DRegister op1 = DRegisterFrom(op1_loc);
4815 vixl32::DRegister op2 = DRegisterFrom(op2_loc);
4816 vixl32::DRegister out = DRegisterFrom(out_loc);
4817 vixl32::Label handle_nan_eq, done;
Aart Bik351df3e2018-03-07 11:54:57 -08004818 vixl32::Label* final_label = codegen_->GetFinalLabel(minmax, &done);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004819
4820 DCHECK(op1.Is(out));
4821
4822 __ Vcmp(op1, op2);
4823 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
Andreas Gampe3db70682018-12-26 15:12:03 -08004824 __ B(vs, &handle_nan_eq, /* is_far_target= */ false); // if un-ordered, go to NaN handling.
Aart Bik1f8d51b2018-02-15 10:42:37 -08004825
4826 // op1 <> op2
4827 vixl32::ConditionType cond = is_min ? gt : lt;
4828 {
4829 ExactAssemblyScope it_scope(GetVIXLAssembler(),
4830 2 * kMaxInstructionSizeInBytes,
4831 CodeBufferCheckScope::kMaximumSize);
4832 __ it(cond);
4833 __ vmov(cond, F64, out, op2);
4834 }
4835 // for <>(not equal), we've done min/max calculation.
Andreas Gampe3db70682018-12-26 15:12:03 -08004836 __ B(ne, final_label, /* is_far_target= */ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004837
4838 // handle op1 == op2, max(+0.0,-0.0).
4839 if (!is_min) {
4840 __ Vand(F64, out, op1, op2);
4841 __ B(final_label);
4842 }
4843
4844 // handle op1 == op2, min(+0.0,-0.0), NaN input.
4845 __ Bind(&handle_nan_eq);
4846 __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN.
4847
4848 if (done.IsReferenced()) {
4849 __ Bind(&done);
4850 }
4851}
4852
Aart Bik351df3e2018-03-07 11:54:57 -08004853void InstructionCodeGeneratorARMVIXL::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
4854 DataType::Type type = minmax->GetResultType();
4855 switch (type) {
4856 case DataType::Type::kInt32:
4857 GenerateMinMaxInt(minmax->GetLocations(), is_min);
4858 break;
4859 case DataType::Type::kInt64:
4860 GenerateMinMaxLong(minmax->GetLocations(), is_min);
4861 break;
4862 case DataType::Type::kFloat32:
4863 GenerateMinMaxFloat(minmax, is_min);
4864 break;
4865 case DataType::Type::kFloat64:
4866 GenerateMinMaxDouble(minmax, is_min);
4867 break;
4868 default:
4869 LOG(FATAL) << "Unexpected type for HMinMax " << type;
4870 }
4871}
4872
Aart Bik1f8d51b2018-02-15 10:42:37 -08004873void LocationsBuilderARMVIXL::VisitMin(HMin* min) {
4874 CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
4875}
4876
4877void InstructionCodeGeneratorARMVIXL::VisitMin(HMin* min) {
Aart Bik351df3e2018-03-07 11:54:57 -08004878 GenerateMinMax(min, /*is_min*/ true);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004879}
4880
4881void LocationsBuilderARMVIXL::VisitMax(HMax* max) {
4882 CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
4883}
4884
4885void InstructionCodeGeneratorARMVIXL::VisitMax(HMax* max) {
Aart Bik351df3e2018-03-07 11:54:57 -08004886 GenerateMinMax(max, /*is_min*/ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08004887}
4888
Aart Bik3dad3412018-02-28 12:01:46 -08004889void LocationsBuilderARMVIXL::VisitAbs(HAbs* abs) {
4890 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
4891 switch (abs->GetResultType()) {
4892 case DataType::Type::kInt32:
4893 case DataType::Type::kInt64:
4894 locations->SetInAt(0, Location::RequiresRegister());
4895 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
4896 locations->AddTemp(Location::RequiresRegister());
4897 break;
4898 case DataType::Type::kFloat32:
4899 case DataType::Type::kFloat64:
4900 locations->SetInAt(0, Location::RequiresFpuRegister());
4901 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
4902 break;
4903 default:
4904 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
4905 }
4906}
4907
4908void InstructionCodeGeneratorARMVIXL::VisitAbs(HAbs* abs) {
4909 LocationSummary* locations = abs->GetLocations();
4910 switch (abs->GetResultType()) {
4911 case DataType::Type::kInt32: {
4912 vixl32::Register in_reg = RegisterFrom(locations->InAt(0));
4913 vixl32::Register out_reg = RegisterFrom(locations->Out());
4914 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
4915 __ Asr(mask, in_reg, 31);
4916 __ Add(out_reg, in_reg, mask);
4917 __ Eor(out_reg, out_reg, mask);
4918 break;
4919 }
4920 case DataType::Type::kInt64: {
4921 Location in = locations->InAt(0);
4922 vixl32::Register in_reg_lo = LowRegisterFrom(in);
4923 vixl32::Register in_reg_hi = HighRegisterFrom(in);
4924 Location output = locations->Out();
4925 vixl32::Register out_reg_lo = LowRegisterFrom(output);
4926 vixl32::Register out_reg_hi = HighRegisterFrom(output);
4927 DCHECK(!out_reg_lo.Is(in_reg_hi)) << "Diagonal overlap unexpected.";
4928 vixl32::Register mask = RegisterFrom(locations->GetTemp(0));
4929 __ Asr(mask, in_reg_hi, 31);
4930 __ Adds(out_reg_lo, in_reg_lo, mask);
4931 __ Adc(out_reg_hi, in_reg_hi, mask);
4932 __ Eor(out_reg_lo, out_reg_lo, mask);
4933 __ Eor(out_reg_hi, out_reg_hi, mask);
4934 break;
4935 }
4936 case DataType::Type::kFloat32:
4937 case DataType::Type::kFloat64:
4938 __ Vabs(OutputVRegister(abs), InputVRegisterAt(abs, 0));
4939 break;
4940 default:
4941 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
4942 }
4943}
Artem Serov551b28f2016-10-18 19:11:30 +01004944
Scott Wakelingfe885462016-09-22 10:24:38 +01004945void LocationsBuilderARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00004946 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01004947 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Scott Wakelingfe885462016-09-22 10:24:38 +01004948}
4949
4950void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instruction) {
4951 DivZeroCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01004952 new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathARMVIXL(instruction);
Scott Wakelingfe885462016-09-22 10:24:38 +01004953 codegen_->AddSlowPath(slow_path);
4954
4955 LocationSummary* locations = instruction->GetLocations();
4956 Location value = locations->InAt(0);
4957
4958 switch (instruction->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004959 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01004960 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004961 case DataType::Type::kInt8:
4962 case DataType::Type::kUint16:
4963 case DataType::Type::kInt16:
4964 case DataType::Type::kInt32: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004965 if (value.IsRegister()) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00004966 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelingfe885462016-09-22 10:24:38 +01004967 } else {
4968 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00004969 if (Int32ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004970 __ B(slow_path->GetEntryLabel());
4971 }
4972 }
4973 break;
4974 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004975 case DataType::Type::kInt64: {
Scott Wakelingfe885462016-09-22 10:24:38 +01004976 if (value.IsRegisterPair()) {
4977 UseScratchRegisterScope temps(GetVIXLAssembler());
4978 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01004979 __ Orrs(temp, LowRegisterFrom(value), HighRegisterFrom(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01004980 __ B(eq, slow_path->GetEntryLabel());
4981 } else {
4982 DCHECK(value.IsConstant()) << value;
Anton Kirilov644032c2016-12-06 17:51:43 +00004983 if (Int64ConstantFrom(value) == 0) {
Scott Wakelingfe885462016-09-22 10:24:38 +01004984 __ B(slow_path->GetEntryLabel());
4985 }
4986 }
4987 break;
4988 }
4989 default:
4990 LOG(FATAL) << "Unexpected type for HDivZeroCheck " << instruction->GetType();
4991 }
4992}
4993
Artem Serov02109dd2016-09-23 17:17:54 +01004994void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) {
4995 LocationSummary* locations = ror->GetLocations();
4996 vixl32::Register in = InputRegisterAt(ror, 0);
4997 Location rhs = locations->InAt(1);
4998 vixl32::Register out = OutputRegister(ror);
4999
5000 if (rhs.IsConstant()) {
5001 // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31],
5002 // so map all rotations to a +ve. equivalent in that range.
5003 // (e.g. left *or* right by -2 bits == 30 bits in the same direction.)
5004 uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F;
5005 if (rot) {
5006 // Rotate, mapping left rotations to right equivalents if necessary.
5007 // (e.g. left by 2 bits == right by 30.)
5008 __ Ror(out, in, rot);
5009 } else if (!out.Is(in)) {
5010 __ Mov(out, in);
5011 }
5012 } else {
5013 __ Ror(out, in, RegisterFrom(rhs));
5014 }
5015}
5016
5017// Gain some speed by mapping all Long rotates onto equivalent pairs of Integer
5018// rotates by swapping input regs (effectively rotating by the first 32-bits of
5019// a larger rotation) or flipping direction (thus treating larger right/left
5020// rotations as sub-word sized rotations in the other direction) as appropriate.
5021void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) {
5022 LocationSummary* locations = ror->GetLocations();
5023 vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0));
5024 vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0));
5025 Location rhs = locations->InAt(1);
5026 vixl32::Register out_reg_lo = LowRegisterFrom(locations->Out());
5027 vixl32::Register out_reg_hi = HighRegisterFrom(locations->Out());
5028
5029 if (rhs.IsConstant()) {
5030 uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant());
5031 // Map all rotations to +ve. equivalents on the interval [0,63].
5032 rot &= kMaxLongShiftDistance;
5033 // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate
5034 // logic below to a simple pair of binary orr.
5035 // (e.g. 34 bits == in_reg swap + 2 bits right.)
5036 if (rot >= kArmBitsPerWord) {
5037 rot -= kArmBitsPerWord;
5038 std::swap(in_reg_hi, in_reg_lo);
5039 }
5040 // Rotate, or mov to out for zero or word size rotations.
5041 if (rot != 0u) {
Scott Wakelingb77051e2016-11-21 19:46:00 +00005042 __ Lsr(out_reg_hi, in_reg_hi, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01005043 __ Orr(out_reg_hi, out_reg_hi, Operand(in_reg_lo, ShiftType::LSL, kArmBitsPerWord - rot));
Scott Wakelingb77051e2016-11-21 19:46:00 +00005044 __ Lsr(out_reg_lo, in_reg_lo, Operand::From(rot));
Artem Serov02109dd2016-09-23 17:17:54 +01005045 __ Orr(out_reg_lo, out_reg_lo, Operand(in_reg_hi, ShiftType::LSL, kArmBitsPerWord - rot));
5046 } else {
5047 __ Mov(out_reg_lo, in_reg_lo);
5048 __ Mov(out_reg_hi, in_reg_hi);
5049 }
5050 } else {
5051 vixl32::Register shift_right = RegisterFrom(locations->GetTemp(0));
5052 vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1));
5053 vixl32::Label end;
5054 vixl32::Label shift_by_32_plus_shift_right;
Anton Kirilov6f644202017-02-27 18:29:45 +00005055 vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end);
Artem Serov02109dd2016-09-23 17:17:54 +01005056
5057 __ And(shift_right, RegisterFrom(rhs), 0x1F);
5058 __ Lsrs(shift_left, RegisterFrom(rhs), 6);
Scott Wakelingbffdc702016-12-07 17:46:03 +00005059 __ Rsb(LeaveFlags, shift_left, shift_right, Operand::From(kArmBitsPerWord));
Andreas Gampe3db70682018-12-26 15:12:03 -08005060 __ B(cc, &shift_by_32_plus_shift_right, /* is_far_target= */ false);
Artem Serov02109dd2016-09-23 17:17:54 +01005061
5062 // out_reg_hi = (reg_hi << shift_left) | (reg_lo >> shift_right).
5063 // out_reg_lo = (reg_lo << shift_left) | (reg_hi >> shift_right).
5064 __ Lsl(out_reg_hi, in_reg_hi, shift_left);
5065 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5066 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5067 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5068 __ Lsr(shift_left, in_reg_hi, shift_right);
5069 __ Add(out_reg_lo, out_reg_lo, shift_left);
Anton Kirilov6f644202017-02-27 18:29:45 +00005070 __ B(final_label);
Artem Serov02109dd2016-09-23 17:17:54 +01005071
5072 __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right.
5073 // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left).
5074 // out_reg_lo = (reg_lo >> shift_right) | (reg_hi << shift_left).
5075 __ Lsr(out_reg_hi, in_reg_hi, shift_right);
5076 __ Lsl(out_reg_lo, in_reg_lo, shift_left);
5077 __ Add(out_reg_hi, out_reg_hi, out_reg_lo);
5078 __ Lsr(out_reg_lo, in_reg_lo, shift_right);
5079 __ Lsl(shift_right, in_reg_hi, shift_left);
5080 __ Add(out_reg_lo, out_reg_lo, shift_right);
5081
Anton Kirilov6f644202017-02-27 18:29:45 +00005082 if (end.IsReferenced()) {
5083 __ Bind(&end);
5084 }
Artem Serov02109dd2016-09-23 17:17:54 +01005085 }
5086}
5087
5088void LocationsBuilderARMVIXL::VisitRor(HRor* ror) {
5089 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005090 new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall);
Artem Serov02109dd2016-09-23 17:17:54 +01005091 switch (ror->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005092 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01005093 locations->SetInAt(0, Location::RequiresRegister());
5094 locations->SetInAt(1, Location::RegisterOrConstant(ror->InputAt(1)));
5095 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5096 break;
5097 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005098 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01005099 locations->SetInAt(0, Location::RequiresRegister());
5100 if (ror->InputAt(1)->IsConstant()) {
5101 locations->SetInAt(1, Location::ConstantLocation(ror->InputAt(1)->AsConstant()));
5102 } else {
5103 locations->SetInAt(1, Location::RequiresRegister());
5104 locations->AddTemp(Location::RequiresRegister());
5105 locations->AddTemp(Location::RequiresRegister());
5106 }
5107 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5108 break;
5109 }
5110 default:
5111 LOG(FATAL) << "Unexpected operation type " << ror->GetResultType();
5112 }
5113}
5114
5115void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005116 DataType::Type type = ror->GetResultType();
Artem Serov02109dd2016-09-23 17:17:54 +01005117 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005118 case DataType::Type::kInt32: {
Artem Serov02109dd2016-09-23 17:17:54 +01005119 HandleIntegerRotate(ror);
5120 break;
5121 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005122 case DataType::Type::kInt64: {
Artem Serov02109dd2016-09-23 17:17:54 +01005123 HandleLongRotate(ror);
5124 break;
5125 }
5126 default:
5127 LOG(FATAL) << "Unexpected operation type " << type;
5128 UNREACHABLE();
5129 }
5130}
5131
Artem Serov02d37832016-10-25 15:25:33 +01005132void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) {
5133 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5134
5135 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005136 new (GetGraph()->GetAllocator()) LocationSummary(op, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005137
5138 switch (op->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005139 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005140 locations->SetInAt(0, Location::RequiresRegister());
5141 if (op->InputAt(1)->IsConstant()) {
5142 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5143 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5144 } else {
5145 locations->SetInAt(1, Location::RequiresRegister());
5146 // Make the output overlap, as it will be used to hold the masked
5147 // second input.
5148 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5149 }
5150 break;
5151 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005152 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005153 locations->SetInAt(0, Location::RequiresRegister());
5154 if (op->InputAt(1)->IsConstant()) {
5155 locations->SetInAt(1, Location::ConstantLocation(op->InputAt(1)->AsConstant()));
5156 // For simplicity, use kOutputOverlap even though we only require that low registers
5157 // don't clash with high registers which the register allocator currently guarantees.
5158 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5159 } else {
5160 locations->SetInAt(1, Location::RequiresRegister());
5161 locations->AddTemp(Location::RequiresRegister());
5162 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5163 }
5164 break;
5165 }
5166 default:
5167 LOG(FATAL) << "Unexpected operation type " << op->GetResultType();
5168 }
5169}
5170
5171void InstructionCodeGeneratorARMVIXL::HandleShift(HBinaryOperation* op) {
5172 DCHECK(op->IsShl() || op->IsShr() || op->IsUShr());
5173
5174 LocationSummary* locations = op->GetLocations();
5175 Location out = locations->Out();
5176 Location first = locations->InAt(0);
5177 Location second = locations->InAt(1);
5178
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005179 DataType::Type type = op->GetResultType();
Artem Serov02d37832016-10-25 15:25:33 +01005180 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005181 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005182 vixl32::Register out_reg = OutputRegister(op);
5183 vixl32::Register first_reg = InputRegisterAt(op, 0);
5184 if (second.IsRegister()) {
5185 vixl32::Register second_reg = RegisterFrom(second);
5186 // ARM doesn't mask the shift count so we need to do it ourselves.
5187 __ And(out_reg, second_reg, kMaxIntShiftDistance);
5188 if (op->IsShl()) {
5189 __ Lsl(out_reg, first_reg, out_reg);
5190 } else if (op->IsShr()) {
5191 __ Asr(out_reg, first_reg, out_reg);
5192 } else {
5193 __ Lsr(out_reg, first_reg, out_reg);
5194 }
5195 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00005196 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005197 uint32_t shift_value = cst & kMaxIntShiftDistance;
5198 if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
5199 __ Mov(out_reg, first_reg);
5200 } else if (op->IsShl()) {
5201 __ Lsl(out_reg, first_reg, shift_value);
5202 } else if (op->IsShr()) {
5203 __ Asr(out_reg, first_reg, shift_value);
5204 } else {
5205 __ Lsr(out_reg, first_reg, shift_value);
5206 }
5207 }
5208 break;
5209 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005210 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005211 vixl32::Register o_h = HighRegisterFrom(out);
5212 vixl32::Register o_l = LowRegisterFrom(out);
5213
5214 vixl32::Register high = HighRegisterFrom(first);
5215 vixl32::Register low = LowRegisterFrom(first);
5216
5217 if (second.IsRegister()) {
5218 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5219
5220 vixl32::Register second_reg = RegisterFrom(second);
5221
5222 if (op->IsShl()) {
5223 __ And(o_l, second_reg, kMaxLongShiftDistance);
5224 // Shift the high part
5225 __ Lsl(o_h, high, o_l);
5226 // Shift the low part and `or` what overflew on the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005227 __ Rsb(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005228 __ Lsr(temp, low, temp);
5229 __ Orr(o_h, o_h, temp);
5230 // If the shift is > 32 bits, override the high part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005231 __ Subs(temp, o_l, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005232 {
Artem Serov0fb37192016-12-06 18:13:40 +00005233 ExactAssemblyScope guard(GetVIXLAssembler(),
5234 2 * vixl32::kMaxInstructionSizeInBytes,
5235 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005236 __ it(pl);
5237 __ lsl(pl, o_h, low, temp);
5238 }
5239 // Shift the low part
5240 __ Lsl(o_l, low, o_l);
5241 } else if (op->IsShr()) {
5242 __ And(o_h, second_reg, kMaxLongShiftDistance);
5243 // Shift the low part
5244 __ Lsr(o_l, low, o_h);
5245 // Shift the high part and `or` what underflew on the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005246 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005247 __ Lsl(temp, high, temp);
5248 __ Orr(o_l, o_l, temp);
5249 // If the shift is > 32 bits, override the low part
Scott Wakelingb77051e2016-11-21 19:46:00 +00005250 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005251 {
Artem Serov0fb37192016-12-06 18:13:40 +00005252 ExactAssemblyScope guard(GetVIXLAssembler(),
5253 2 * vixl32::kMaxInstructionSizeInBytes,
5254 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005255 __ it(pl);
5256 __ asr(pl, o_l, high, temp);
5257 }
5258 // Shift the high part
5259 __ Asr(o_h, high, o_h);
5260 } else {
5261 __ And(o_h, second_reg, kMaxLongShiftDistance);
5262 // same as Shr except we use `Lsr`s and not `Asr`s
5263 __ Lsr(o_l, low, o_h);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005264 __ Rsb(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005265 __ Lsl(temp, high, temp);
5266 __ Orr(o_l, o_l, temp);
Scott Wakelingb77051e2016-11-21 19:46:00 +00005267 __ Subs(temp, o_h, Operand::From(kArmBitsPerWord));
Artem Serov02d37832016-10-25 15:25:33 +01005268 {
Artem Serov0fb37192016-12-06 18:13:40 +00005269 ExactAssemblyScope guard(GetVIXLAssembler(),
5270 2 * vixl32::kMaxInstructionSizeInBytes,
5271 CodeBufferCheckScope::kMaximumSize);
Artem Serov02d37832016-10-25 15:25:33 +01005272 __ it(pl);
5273 __ lsr(pl, o_l, high, temp);
5274 }
5275 __ Lsr(o_h, high, o_h);
5276 }
5277 } else {
5278 // Register allocator doesn't create partial overlap.
5279 DCHECK(!o_l.Is(high));
5280 DCHECK(!o_h.Is(low));
Anton Kirilov644032c2016-12-06 17:51:43 +00005281 int32_t cst = Int32ConstantFrom(second);
Artem Serov02d37832016-10-25 15:25:33 +01005282 uint32_t shift_value = cst & kMaxLongShiftDistance;
5283 if (shift_value > 32) {
5284 if (op->IsShl()) {
5285 __ Lsl(o_h, low, shift_value - 32);
5286 __ Mov(o_l, 0);
5287 } else if (op->IsShr()) {
5288 __ Asr(o_l, high, shift_value - 32);
5289 __ Asr(o_h, high, 31);
5290 } else {
5291 __ Lsr(o_l, high, shift_value - 32);
5292 __ Mov(o_h, 0);
5293 }
5294 } else if (shift_value == 32) {
5295 if (op->IsShl()) {
5296 __ Mov(o_h, low);
5297 __ Mov(o_l, 0);
5298 } else if (op->IsShr()) {
5299 __ Mov(o_l, high);
5300 __ Asr(o_h, high, 31);
5301 } else {
5302 __ Mov(o_l, high);
5303 __ Mov(o_h, 0);
5304 }
5305 } else if (shift_value == 1) {
5306 if (op->IsShl()) {
5307 __ Lsls(o_l, low, 1);
5308 __ Adc(o_h, high, high);
5309 } else if (op->IsShr()) {
5310 __ Asrs(o_h, high, 1);
5311 __ Rrx(o_l, low);
5312 } else {
5313 __ Lsrs(o_h, high, 1);
5314 __ Rrx(o_l, low);
5315 }
Nicolas Geoffray9b195cc2019-04-02 08:29:00 +01005316 } else if (shift_value == 0) {
5317 __ Mov(o_l, low);
5318 __ Mov(o_h, high);
Artem Serov02d37832016-10-25 15:25:33 +01005319 } else {
Nicolas Geoffray9b195cc2019-04-02 08:29:00 +01005320 DCHECK(0 < shift_value && shift_value < 32) << shift_value;
Artem Serov02d37832016-10-25 15:25:33 +01005321 if (op->IsShl()) {
5322 __ Lsl(o_h, high, shift_value);
5323 __ Orr(o_h, o_h, Operand(low, ShiftType::LSR, 32 - shift_value));
5324 __ Lsl(o_l, low, shift_value);
5325 } else if (op->IsShr()) {
5326 __ Lsr(o_l, low, shift_value);
5327 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5328 __ Asr(o_h, high, shift_value);
5329 } else {
5330 __ Lsr(o_l, low, shift_value);
5331 __ Orr(o_l, o_l, Operand(high, ShiftType::LSL, 32 - shift_value));
5332 __ Lsr(o_h, high, shift_value);
5333 }
5334 }
5335 }
5336 break;
5337 }
5338 default:
5339 LOG(FATAL) << "Unexpected operation type " << type;
5340 UNREACHABLE();
5341 }
5342}
5343
5344void LocationsBuilderARMVIXL::VisitShl(HShl* shl) {
5345 HandleShift(shl);
5346}
5347
5348void InstructionCodeGeneratorARMVIXL::VisitShl(HShl* shl) {
5349 HandleShift(shl);
5350}
5351
5352void LocationsBuilderARMVIXL::VisitShr(HShr* shr) {
5353 HandleShift(shr);
5354}
5355
5356void InstructionCodeGeneratorARMVIXL::VisitShr(HShr* shr) {
5357 HandleShift(shr);
5358}
5359
5360void LocationsBuilderARMVIXL::VisitUShr(HUShr* ushr) {
5361 HandleShift(ushr);
5362}
5363
5364void InstructionCodeGeneratorARMVIXL::VisitUShr(HUShr* ushr) {
5365 HandleShift(ushr);
5366}
5367
5368void LocationsBuilderARMVIXL::VisitNewInstance(HNewInstance* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005369 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5370 instruction, LocationSummary::kCallOnMainOnly);
Alex Lightd109e302018-06-27 10:25:41 -07005371 InvokeRuntimeCallingConventionARMVIXL calling_convention;
5372 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
Artem Serov02d37832016-10-25 15:25:33 +01005373 locations->SetOut(LocationFrom(r0));
5374}
5375
5376void InstructionCodeGeneratorARMVIXL::VisitNewInstance(HNewInstance* instruction) {
Alex Lightd109e302018-06-27 10:25:41 -07005377 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
5378 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
Andreas Gampe3db70682018-12-26 15:12:03 -08005379 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 11);
Artem Serov02d37832016-10-25 15:25:33 +01005380}
5381
5382void LocationsBuilderARMVIXL::VisitNewArray(HNewArray* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005383 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5384 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01005385 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Artem Serov02d37832016-10-25 15:25:33 +01005386 locations->SetOut(LocationFrom(r0));
Nicolas Geoffray8c7c4f12017-01-26 10:13:11 +00005387 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5388 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Artem Serov02d37832016-10-25 15:25:33 +01005389}
5390
5391void InstructionCodeGeneratorARMVIXL::VisitNewArray(HNewArray* instruction) {
Vladimir Markob5461632018-10-15 14:24:21 +01005392 // Note: if heap poisoning is enabled, the entry point takes care of poisoning the reference.
5393 QuickEntrypointEnum entrypoint = CodeGenerator::GetArrayAllocationEntrypoint(instruction);
Artem Serov7b3672e2017-02-03 17:30:34 +00005394 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00005395 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
Artem Serov7b3672e2017-02-03 17:30:34 +00005396 DCHECK(!codegen_->IsLeafMethod());
Andreas Gampe3db70682018-12-26 15:12:03 -08005397 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 12);
Artem Serov02d37832016-10-25 15:25:33 +01005398}
5399
5400void LocationsBuilderARMVIXL::VisitParameterValue(HParameterValue* instruction) {
5401 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005402 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005403 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
5404 if (location.IsStackSlot()) {
5405 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5406 } else if (location.IsDoubleStackSlot()) {
5407 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5408 }
5409 locations->SetOut(location);
5410}
5411
5412void InstructionCodeGeneratorARMVIXL::VisitParameterValue(
5413 HParameterValue* instruction ATTRIBUTE_UNUSED) {
5414 // Nothing to do, the parameter is already at its location.
5415}
5416
5417void LocationsBuilderARMVIXL::VisitCurrentMethod(HCurrentMethod* instruction) {
5418 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005419 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005420 locations->SetOut(LocationFrom(kMethodRegister));
5421}
5422
5423void InstructionCodeGeneratorARMVIXL::VisitCurrentMethod(
5424 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
5425 // Nothing to do, the method is already at its location.
5426}
5427
5428void LocationsBuilderARMVIXL::VisitNot(HNot* not_) {
5429 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005430 new (GetGraph()->GetAllocator()) LocationSummary(not_, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005431 locations->SetInAt(0, Location::RequiresRegister());
5432 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5433}
5434
5435void InstructionCodeGeneratorARMVIXL::VisitNot(HNot* not_) {
5436 LocationSummary* locations = not_->GetLocations();
5437 Location out = locations->Out();
5438 Location in = locations->InAt(0);
5439 switch (not_->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005440 case DataType::Type::kInt32:
Artem Serov02d37832016-10-25 15:25:33 +01005441 __ Mvn(OutputRegister(not_), InputRegisterAt(not_, 0));
5442 break;
5443
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005444 case DataType::Type::kInt64:
Artem Serov02d37832016-10-25 15:25:33 +01005445 __ Mvn(LowRegisterFrom(out), LowRegisterFrom(in));
5446 __ Mvn(HighRegisterFrom(out), HighRegisterFrom(in));
5447 break;
5448
5449 default:
5450 LOG(FATAL) << "Unimplemented type for not operation " << not_->GetResultType();
5451 }
5452}
5453
Scott Wakelingc34dba72016-10-03 10:14:44 +01005454void LocationsBuilderARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5455 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005456 new (GetGraph()->GetAllocator()) LocationSummary(bool_not, LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01005457 locations->SetInAt(0, Location::RequiresRegister());
5458 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5459}
5460
5461void InstructionCodeGeneratorARMVIXL::VisitBooleanNot(HBooleanNot* bool_not) {
5462 __ Eor(OutputRegister(bool_not), InputRegister(bool_not), 1);
5463}
5464
Artem Serov02d37832016-10-25 15:25:33 +01005465void LocationsBuilderARMVIXL::VisitCompare(HCompare* compare) {
5466 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005467 new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005468 switch (compare->InputAt(0)->GetType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005469 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005470 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005471 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005472 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005473 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005474 case DataType::Type::kInt32:
5475 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005476 locations->SetInAt(0, Location::RequiresRegister());
5477 locations->SetInAt(1, Location::RequiresRegister());
5478 // Output overlaps because it is written before doing the low comparison.
5479 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
5480 break;
5481 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005482 case DataType::Type::kFloat32:
5483 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005484 locations->SetInAt(0, Location::RequiresFpuRegister());
5485 locations->SetInAt(1, ArithmeticZeroOrFpuRegister(compare->InputAt(1)));
5486 locations->SetOut(Location::RequiresRegister());
5487 break;
5488 }
5489 default:
5490 LOG(FATAL) << "Unexpected type for compare operation " << compare->InputAt(0)->GetType();
5491 }
5492}
5493
5494void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) {
5495 LocationSummary* locations = compare->GetLocations();
5496 vixl32::Register out = OutputRegister(compare);
5497 Location left = locations->InAt(0);
5498 Location right = locations->InAt(1);
5499
5500 vixl32::Label less, greater, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00005501 vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005502 DataType::Type type = compare->InputAt(0)->GetType();
Vladimir Marko33bff252017-11-01 14:35:42 +00005503 vixl32::Condition less_cond = vixl32::Condition::None();
Artem Serov02d37832016-10-25 15:25:33 +01005504 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005505 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005506 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005507 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005508 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005509 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005510 case DataType::Type::kInt32: {
Artem Serov02d37832016-10-25 15:25:33 +01005511 // Emit move to `out` before the `Cmp`, as `Mov` might affect the status flags.
5512 __ Mov(out, 0);
5513 __ Cmp(RegisterFrom(left), RegisterFrom(right)); // Signed compare.
5514 less_cond = lt;
5515 break;
5516 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005517 case DataType::Type::kInt64: {
Artem Serov02d37832016-10-25 15:25:33 +01005518 __ Cmp(HighRegisterFrom(left), HighRegisterFrom(right)); // Signed compare.
Andreas Gampe3db70682018-12-26 15:12:03 -08005519 __ B(lt, &less, /* is_far_target= */ false);
5520 __ B(gt, &greater, /* is_far_target= */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005521 // Emit move to `out` before the last `Cmp`, as `Mov` might affect the status flags.
5522 __ Mov(out, 0);
5523 __ Cmp(LowRegisterFrom(left), LowRegisterFrom(right)); // Unsigned compare.
5524 less_cond = lo;
5525 break;
5526 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005527 case DataType::Type::kFloat32:
5528 case DataType::Type::kFloat64: {
Artem Serov02d37832016-10-25 15:25:33 +01005529 __ Mov(out, 0);
Donghui Bai426b49c2016-11-08 14:55:38 +08005530 GenerateVcmp(compare, codegen_);
Artem Serov02d37832016-10-25 15:25:33 +01005531 // To branch on the FP compare result we transfer FPSCR to APSR (encoded as PC in VMRS).
5532 __ Vmrs(RegisterOrAPSR_nzcv(kPcCode), FPSCR);
5533 less_cond = ARMFPCondition(kCondLT, compare->IsGtBias());
5534 break;
5535 }
5536 default:
5537 LOG(FATAL) << "Unexpected compare type " << type;
5538 UNREACHABLE();
5539 }
5540
Andreas Gampe3db70682018-12-26 15:12:03 -08005541 __ B(eq, final_label, /* is_far_target= */ false);
5542 __ B(less_cond, &less, /* is_far_target= */ false);
Artem Serov02d37832016-10-25 15:25:33 +01005543
5544 __ Bind(&greater);
5545 __ Mov(out, 1);
Anton Kirilov6f644202017-02-27 18:29:45 +00005546 __ B(final_label);
Artem Serov02d37832016-10-25 15:25:33 +01005547
5548 __ Bind(&less);
5549 __ Mov(out, -1);
5550
Anton Kirilov6f644202017-02-27 18:29:45 +00005551 if (done.IsReferenced()) {
5552 __ Bind(&done);
5553 }
Artem Serov02d37832016-10-25 15:25:33 +01005554}
5555
5556void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) {
5557 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005558 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005559 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
5560 locations->SetInAt(i, Location::Any());
5561 }
5562 locations->SetOut(Location::Any());
5563}
5564
5565void InstructionCodeGeneratorARMVIXL::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
5566 LOG(FATAL) << "Unreachable";
5567}
5568
5569void CodeGeneratorARMVIXL::GenerateMemoryBarrier(MemBarrierKind kind) {
5570 // TODO (ported from quick): revisit ARM barrier kinds.
5571 DmbOptions flavor = DmbOptions::ISH; // Quiet C++ warnings.
5572 switch (kind) {
5573 case MemBarrierKind::kAnyStore:
5574 case MemBarrierKind::kLoadAny:
5575 case MemBarrierKind::kAnyAny: {
5576 flavor = DmbOptions::ISH;
5577 break;
5578 }
5579 case MemBarrierKind::kStoreStore: {
5580 flavor = DmbOptions::ISHST;
5581 break;
5582 }
5583 default:
5584 LOG(FATAL) << "Unexpected memory barrier " << kind;
5585 }
5586 __ Dmb(flavor);
5587}
5588
5589void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicLoad(vixl32::Register addr,
5590 uint32_t offset,
5591 vixl32::Register out_lo,
5592 vixl32::Register out_hi) {
5593 UseScratchRegisterScope temps(GetVIXLAssembler());
5594 if (offset != 0) {
5595 vixl32::Register temp = temps.Acquire();
5596 __ Add(temp, addr, offset);
5597 addr = temp;
5598 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005599 __ Ldrexd(out_lo, out_hi, MemOperand(addr));
Artem Serov02d37832016-10-25 15:25:33 +01005600}
5601
5602void InstructionCodeGeneratorARMVIXL::GenerateWideAtomicStore(vixl32::Register addr,
5603 uint32_t offset,
5604 vixl32::Register value_lo,
5605 vixl32::Register value_hi,
5606 vixl32::Register temp1,
5607 vixl32::Register temp2,
5608 HInstruction* instruction) {
5609 UseScratchRegisterScope temps(GetVIXLAssembler());
5610 vixl32::Label fail;
5611 if (offset != 0) {
5612 vixl32::Register temp = temps.Acquire();
5613 __ Add(temp, addr, offset);
5614 addr = temp;
5615 }
5616 __ Bind(&fail);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005617 {
5618 // Ensure the pc position is recorded immediately after the `ldrexd` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00005619 ExactAssemblyScope aas(GetVIXLAssembler(),
5620 vixl32::kMaxInstructionSizeInBytes,
5621 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00005622 // We need a load followed by store. (The address used in a STREX instruction must
5623 // be the same as the address in the most recently executed LDREX instruction.)
5624 __ ldrexd(temp1, temp2, MemOperand(addr));
5625 codegen_->MaybeRecordImplicitNullCheck(instruction);
5626 }
Scott Wakelingb77051e2016-11-21 19:46:00 +00005627 __ Strexd(temp1, value_lo, value_hi, MemOperand(addr));
xueliang.zhongf51bc622016-11-04 09:23:32 +00005628 __ CompareAndBranchIfNonZero(temp1, &fail);
Artem Serov02d37832016-10-25 15:25:33 +01005629}
Artem Serov02109dd2016-09-23 17:17:54 +01005630
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005631void LocationsBuilderARMVIXL::HandleFieldSet(
5632 HInstruction* instruction, const FieldInfo& field_info) {
5633 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5634
5635 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005636 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005637 locations->SetInAt(0, Location::RequiresRegister());
5638
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005639 DataType::Type field_type = field_info.GetFieldType();
5640 if (DataType::IsFloatingPointType(field_type)) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005641 locations->SetInAt(1, Location::RequiresFpuRegister());
5642 } else {
5643 locations->SetInAt(1, Location::RequiresRegister());
5644 }
5645
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005646 bool is_wide = field_type == DataType::Type::kInt64 || field_type == DataType::Type::kFloat64;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005647 bool generate_volatile = field_info.IsVolatile()
5648 && is_wide
5649 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5650 bool needs_write_barrier =
5651 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5652 // Temporary registers for the write barrier.
5653 // TODO: consider renaming StoreNeedsWriteBarrier to StoreNeedsGCMark.
5654 if (needs_write_barrier) {
5655 locations->AddTemp(Location::RequiresRegister()); // Possibly used for reference poisoning too.
5656 locations->AddTemp(Location::RequiresRegister());
5657 } else if (generate_volatile) {
5658 // ARM encoding have some additional constraints for ldrexd/strexd:
5659 // - registers need to be consecutive
5660 // - the first register should be even but not R14.
5661 // We don't test for ARM yet, and the assertion makes sure that we
5662 // revisit this if we ever enable ARM encoding.
5663 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5664
5665 locations->AddTemp(Location::RequiresRegister());
5666 locations->AddTemp(Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005667 if (field_type == DataType::Type::kFloat64) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005668 // For doubles we need two more registers to copy the value.
5669 locations->AddTemp(LocationFrom(r2));
5670 locations->AddTemp(LocationFrom(r3));
5671 }
5672 }
5673}
5674
5675void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction,
5676 const FieldInfo& field_info,
5677 bool value_can_be_null) {
5678 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
5679
5680 LocationSummary* locations = instruction->GetLocations();
5681 vixl32::Register base = InputRegisterAt(instruction, 0);
5682 Location value = locations->InAt(1);
5683
5684 bool is_volatile = field_info.IsVolatile();
5685 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005686 DataType::Type field_type = field_info.GetFieldType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005687 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5688 bool needs_write_barrier =
5689 CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1));
5690
5691 if (is_volatile) {
5692 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore);
5693 }
5694
5695 switch (field_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005696 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005697 case DataType::Type::kUint8:
5698 case DataType::Type::kInt8:
5699 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005700 case DataType::Type::kInt16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005701 case DataType::Type::kInt32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005702 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5703 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005704 StoreOperandType operand_type = GetStoreOperandType(field_type);
5705 GetAssembler()->StoreToOffset(operand_type, RegisterFrom(value), base, offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005706 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005707 break;
5708 }
5709
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005710 case DataType::Type::kReference: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005711 vixl32::Register value_reg = RegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005712 if (kPoisonHeapReferences && needs_write_barrier) {
5713 // Note that in the case where `value` is a null reference,
5714 // we do not enter this block, as a null reference does not
5715 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005716 DCHECK_EQ(field_type, DataType::Type::kReference);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005717 value_reg = RegisterFrom(locations->GetTemp(0));
5718 __ Mov(value_reg, RegisterFrom(value));
5719 GetAssembler()->PoisonHeapReference(value_reg);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005720 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005721 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5722 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5723 GetAssembler()->StoreToOffset(kStoreWord, value_reg, base, offset);
5724 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005725 break;
5726 }
5727
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005728 case DataType::Type::kInt64: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005729 if (is_volatile && !atomic_ldrd_strd) {
5730 GenerateWideAtomicStore(base,
5731 offset,
5732 LowRegisterFrom(value),
5733 HighRegisterFrom(value),
5734 RegisterFrom(locations->GetTemp(0)),
5735 RegisterFrom(locations->GetTemp(1)),
5736 instruction);
5737 } else {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005738 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5739 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005740 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), base, offset);
5741 codegen_->MaybeRecordImplicitNullCheck(instruction);
5742 }
5743 break;
5744 }
5745
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005746 case DataType::Type::kFloat32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005747 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5748 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005749 GetAssembler()->StoreSToOffset(SRegisterFrom(value), base, offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005750 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005751 break;
5752 }
5753
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005754 case DataType::Type::kFloat64: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01005755 vixl32::DRegister value_reg = DRegisterFrom(value);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005756 if (is_volatile && !atomic_ldrd_strd) {
5757 vixl32::Register value_reg_lo = RegisterFrom(locations->GetTemp(0));
5758 vixl32::Register value_reg_hi = RegisterFrom(locations->GetTemp(1));
5759
5760 __ Vmov(value_reg_lo, value_reg_hi, value_reg);
5761
5762 GenerateWideAtomicStore(base,
5763 offset,
5764 value_reg_lo,
5765 value_reg_hi,
5766 RegisterFrom(locations->GetTemp(2)),
5767 RegisterFrom(locations->GetTemp(3)),
5768 instruction);
5769 } else {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005770 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
5771 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005772 GetAssembler()->StoreDToOffset(value_reg, base, offset);
5773 codegen_->MaybeRecordImplicitNullCheck(instruction);
5774 }
5775 break;
5776 }
5777
Aart Bik66c158e2018-01-31 12:55:04 -08005778 case DataType::Type::kUint32:
5779 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005780 case DataType::Type::kVoid:
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005781 LOG(FATAL) << "Unreachable type " << field_type;
5782 UNREACHABLE();
5783 }
5784
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005785 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
5786 vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
5787 vixl32::Register card = RegisterFrom(locations->GetTemp(1));
5788 codegen_->MarkGCCard(temp, card, base, RegisterFrom(value), value_can_be_null);
5789 }
5790
5791 if (is_volatile) {
5792 codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny);
5793 }
5794}
5795
Artem Serov02d37832016-10-25 15:25:33 +01005796void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction,
5797 const FieldInfo& field_info) {
5798 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5799
5800 bool object_field_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005801 kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference);
Artem Serov02d37832016-10-25 15:25:33 +01005802 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005803 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
5804 object_field_get_with_read_barrier
5805 ? LocationSummary::kCallOnSlowPath
5806 : LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01005807 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
5808 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
5809 }
5810 locations->SetInAt(0, Location::RequiresRegister());
5811
5812 bool volatile_for_double = field_info.IsVolatile()
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005813 && (field_info.GetFieldType() == DataType::Type::kFloat64)
Artem Serov02d37832016-10-25 15:25:33 +01005814 && !codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
5815 // The output overlaps in case of volatile long: we don't want the
5816 // code generated by GenerateWideAtomicLoad to overwrite the
5817 // object's location. Likewise, in the case of an object field get
5818 // with read barriers enabled, we do not want the load to overwrite
5819 // the object's location, as we need it to emit the read barrier.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005820 bool overlap =
5821 (field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kInt64)) ||
Artem Serov02d37832016-10-25 15:25:33 +01005822 object_field_get_with_read_barrier;
5823
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005824 if (DataType::IsFloatingPointType(instruction->GetType())) {
Artem Serov02d37832016-10-25 15:25:33 +01005825 locations->SetOut(Location::RequiresFpuRegister());
5826 } else {
5827 locations->SetOut(Location::RequiresRegister(),
5828 (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap));
5829 }
5830 if (volatile_for_double) {
5831 // ARM encoding have some additional constraints for ldrexd/strexd:
5832 // - registers need to be consecutive
5833 // - the first register should be even but not R14.
5834 // We don't test for ARM yet, and the assertion makes sure that we
5835 // revisit this if we ever enable ARM encoding.
5836 DCHECK_EQ(InstructionSet::kThumb2, codegen_->GetInstructionSet());
5837 locations->AddTemp(Location::RequiresRegister());
5838 locations->AddTemp(Location::RequiresRegister());
5839 } else if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Marko008e09f32018-08-06 15:42:43 +01005840 // We need a temporary register for the read barrier load in
5841 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
5842 // only if the offset is too big.
5843 if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01005844 locations->AddTemp(Location::RequiresRegister());
5845 }
Artem Serov02d37832016-10-25 15:25:33 +01005846 }
5847}
5848
5849Location LocationsBuilderARMVIXL::ArithmeticZeroOrFpuRegister(HInstruction* input) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005850 DCHECK(DataType::IsFloatingPointType(input->GetType())) << input->GetType();
Artem Serov02d37832016-10-25 15:25:33 +01005851 if ((input->IsFloatConstant() && (input->AsFloatConstant()->IsArithmeticZero())) ||
5852 (input->IsDoubleConstant() && (input->AsDoubleConstant()->IsArithmeticZero()))) {
5853 return Location::ConstantLocation(input->AsConstant());
5854 } else {
5855 return Location::RequiresFpuRegister();
5856 }
5857}
5858
Artem Serov02109dd2016-09-23 17:17:54 +01005859Location LocationsBuilderARMVIXL::ArmEncodableConstantOrRegister(HInstruction* constant,
5860 Opcode opcode) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005861 DCHECK(!DataType::IsFloatingPointType(constant->GetType()));
Artem Serov02109dd2016-09-23 17:17:54 +01005862 if (constant->IsConstant() &&
5863 CanEncodeConstantAsImmediate(constant->AsConstant(), opcode)) {
5864 return Location::ConstantLocation(constant->AsConstant());
5865 }
5866 return Location::RequiresRegister();
5867}
5868
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005869static bool CanEncode32BitConstantAsImmediate(
5870 CodeGeneratorARMVIXL* codegen,
5871 uint32_t value,
5872 Opcode opcode,
5873 vixl32::FlagsUpdate flags_update = vixl32::FlagsUpdate::DontCare) {
5874 ArmVIXLAssembler* assembler = codegen->GetAssembler();
5875 if (assembler->ShifterOperandCanHold(opcode, value, flags_update)) {
Artem Serov02109dd2016-09-23 17:17:54 +01005876 return true;
5877 }
5878 Opcode neg_opcode = kNoOperand;
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005879 uint32_t neg_value = 0;
Artem Serov02109dd2016-09-23 17:17:54 +01005880 switch (opcode) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005881 case AND: neg_opcode = BIC; neg_value = ~value; break;
5882 case ORR: neg_opcode = ORN; neg_value = ~value; break;
5883 case ADD: neg_opcode = SUB; neg_value = -value; break;
5884 case ADC: neg_opcode = SBC; neg_value = ~value; break;
5885 case SUB: neg_opcode = ADD; neg_value = -value; break;
5886 case SBC: neg_opcode = ADC; neg_value = ~value; break;
5887 case MOV: neg_opcode = MVN; neg_value = ~value; break;
Artem Serov02109dd2016-09-23 17:17:54 +01005888 default:
5889 return false;
5890 }
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005891
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005892 if (assembler->ShifterOperandCanHold(neg_opcode, neg_value, flags_update)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00005893 return true;
5894 }
5895
5896 return opcode == AND && IsPowerOfTwo(value + 1);
Artem Serov02109dd2016-09-23 17:17:54 +01005897}
5898
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00005899bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst, Opcode opcode) {
5900 uint64_t value = static_cast<uint64_t>(Int64FromConstant(input_cst));
5901 if (DataType::Is64BitType(input_cst->GetType())) {
5902 Opcode high_opcode = opcode;
5903 vixl32::FlagsUpdate low_flags_update = vixl32::FlagsUpdate::DontCare;
5904 switch (opcode) {
5905 case SUB:
5906 // Flip the operation to an ADD.
5907 value = -value;
5908 opcode = ADD;
5909 FALLTHROUGH_INTENDED;
5910 case ADD:
5911 if (Low32Bits(value) == 0u) {
5912 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), opcode);
5913 }
5914 high_opcode = ADC;
5915 low_flags_update = vixl32::FlagsUpdate::SetFlags;
5916 break;
5917 default:
5918 break;
5919 }
5920 return CanEncode32BitConstantAsImmediate(codegen_, High32Bits(value), high_opcode) &&
5921 CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode, low_flags_update);
5922 } else {
5923 return CanEncode32BitConstantAsImmediate(codegen_, Low32Bits(value), opcode);
5924 }
5925}
5926
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005927void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction,
5928 const FieldInfo& field_info) {
5929 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
5930
5931 LocationSummary* locations = instruction->GetLocations();
5932 vixl32::Register base = InputRegisterAt(instruction, 0);
5933 Location out = locations->Out();
5934 bool is_volatile = field_info.IsVolatile();
5935 bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd();
Vladimir Marko61b92282017-10-11 13:23:17 +01005936 DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
5937 DataType::Type load_type = instruction->GetType();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005938 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
5939
Vladimir Marko61b92282017-10-11 13:23:17 +01005940 switch (load_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005941 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005942 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005943 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005944 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005945 case DataType::Type::kInt16:
5946 case DataType::Type::kInt32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005947 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
5948 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Vladimir Marko61b92282017-10-11 13:23:17 +01005949 LoadOperandType operand_type = GetLoadOperandType(load_type);
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005950 GetAssembler()->LoadFromOffset(operand_type, RegisterFrom(out), base, offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005951 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005952 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005953 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005954
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005955 case DataType::Type::kReference: {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005956 // /* HeapReference<Object> */ out = *(base + offset)
5957 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Vladimir Markodcd117e2018-04-19 11:54:00 +01005958 Location maybe_temp = (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005959 // Note that a potential implicit null check is handled in this
5960 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier call.
5961 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08005962 instruction, out, base, offset, maybe_temp, /* needs_null_check= */ true);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00005963 if (is_volatile) {
5964 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5965 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005966 } else {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005967 {
5968 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
5969 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5970 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(out), base, offset);
5971 codegen_->MaybeRecordImplicitNullCheck(instruction);
5972 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005973 if (is_volatile) {
5974 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
5975 }
5976 // If read barriers are enabled, emit read barriers other than
5977 // Baker's using a slow path (and also unpoison the loaded
5978 // reference, if heap poisoning is enabled).
5979 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset);
5980 }
5981 break;
5982 }
5983
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005984 case DataType::Type::kInt64: {
5985 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
5986 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005987 if (is_volatile && !atomic_ldrd_strd) {
5988 GenerateWideAtomicLoad(base, offset, LowRegisterFrom(out), HighRegisterFrom(out));
5989 } else {
5990 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out), base, offset);
5991 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005992 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005993 break;
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005994 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005995
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01005996 case DataType::Type::kFloat32: {
5997 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
5998 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01005999 GetAssembler()->LoadSFromOffset(SRegisterFrom(out), base, offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006000 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006001 break;
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006002 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006003
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006004 case DataType::Type::kFloat64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006005 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6006 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006007 vixl32::DRegister out_dreg = DRegisterFrom(out);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006008 if (is_volatile && !atomic_ldrd_strd) {
6009 vixl32::Register lo = RegisterFrom(locations->GetTemp(0));
6010 vixl32::Register hi = RegisterFrom(locations->GetTemp(1));
6011 GenerateWideAtomicLoad(base, offset, lo, hi);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006012 codegen_->MaybeRecordImplicitNullCheck(instruction);
6013 __ Vmov(out_dreg, lo, hi);
6014 } else {
6015 GetAssembler()->LoadDFromOffset(out_dreg, base, offset);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006016 codegen_->MaybeRecordImplicitNullCheck(instruction);
6017 }
6018 break;
6019 }
6020
Aart Bik66c158e2018-01-31 12:55:04 -08006021 case DataType::Type::kUint32:
6022 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006023 case DataType::Type::kVoid:
Vladimir Marko61b92282017-10-11 13:23:17 +01006024 LOG(FATAL) << "Unreachable type " << load_type;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006025 UNREACHABLE();
6026 }
6027
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006028 if (is_volatile) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006029 if (load_type == DataType::Type::kReference) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006030 // Memory barriers, in the case of references, are also handled
6031 // in the previous switch statement.
6032 } else {
6033 codegen_->GenerateMemoryBarrier(MemBarrierKind::kLoadAny);
6034 }
6035 }
6036}
6037
6038void LocationsBuilderARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6039 HandleFieldSet(instruction, instruction->GetFieldInfo());
6040}
6041
6042void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
6043 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6044}
6045
6046void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6047 HandleFieldGet(instruction, instruction->GetFieldInfo());
6048}
6049
6050void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
6051 HandleFieldGet(instruction, instruction->GetFieldInfo());
6052}
6053
6054void LocationsBuilderARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6055 HandleFieldGet(instruction, instruction->GetFieldInfo());
6056}
6057
6058void InstructionCodeGeneratorARMVIXL::VisitStaticFieldGet(HStaticFieldGet* instruction) {
6059 HandleFieldGet(instruction, instruction->GetFieldInfo());
6060}
6061
Scott Wakelingc34dba72016-10-03 10:14:44 +01006062void LocationsBuilderARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6063 HandleFieldSet(instruction, instruction->GetFieldInfo());
6064}
6065
6066void InstructionCodeGeneratorARMVIXL::VisitStaticFieldSet(HStaticFieldSet* instruction) {
6067 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
6068}
6069
Vladimir Marko552a1342017-10-31 10:56:47 +00006070void LocationsBuilderARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
6071 codegen_->CreateStringBuilderAppendLocations(instruction, LocationFrom(r0));
6072}
6073
6074void InstructionCodeGeneratorARMVIXL::VisitStringBuilderAppend(HStringBuilderAppend* instruction) {
6075 __ Mov(r0, instruction->GetFormat()->GetValue());
6076 codegen_->InvokeRuntime(kQuickStringBuilderAppend, instruction, instruction->GetDexPc());
6077}
6078
Artem Serovcfbe9132016-10-14 15:58:56 +01006079void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldGet(
6080 HUnresolvedInstanceFieldGet* instruction) {
6081 FieldAccessCallingConventionARMVIXL calling_convention;
6082 codegen_->CreateUnresolvedFieldLocationSummary(
6083 instruction, instruction->GetFieldType(), calling_convention);
6084}
6085
6086void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldGet(
6087 HUnresolvedInstanceFieldGet* instruction) {
6088 FieldAccessCallingConventionARMVIXL calling_convention;
6089 codegen_->GenerateUnresolvedFieldAccess(instruction,
6090 instruction->GetFieldType(),
6091 instruction->GetFieldIndex(),
6092 instruction->GetDexPc(),
6093 calling_convention);
6094}
6095
6096void LocationsBuilderARMVIXL::VisitUnresolvedInstanceFieldSet(
6097 HUnresolvedInstanceFieldSet* instruction) {
6098 FieldAccessCallingConventionARMVIXL calling_convention;
6099 codegen_->CreateUnresolvedFieldLocationSummary(
6100 instruction, instruction->GetFieldType(), calling_convention);
6101}
6102
6103void InstructionCodeGeneratorARMVIXL::VisitUnresolvedInstanceFieldSet(
6104 HUnresolvedInstanceFieldSet* instruction) {
6105 FieldAccessCallingConventionARMVIXL calling_convention;
6106 codegen_->GenerateUnresolvedFieldAccess(instruction,
6107 instruction->GetFieldType(),
6108 instruction->GetFieldIndex(),
6109 instruction->GetDexPc(),
6110 calling_convention);
6111}
6112
6113void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldGet(
6114 HUnresolvedStaticFieldGet* instruction) {
6115 FieldAccessCallingConventionARMVIXL calling_convention;
6116 codegen_->CreateUnresolvedFieldLocationSummary(
6117 instruction, instruction->GetFieldType(), calling_convention);
6118}
6119
6120void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldGet(
6121 HUnresolvedStaticFieldGet* instruction) {
6122 FieldAccessCallingConventionARMVIXL calling_convention;
6123 codegen_->GenerateUnresolvedFieldAccess(instruction,
6124 instruction->GetFieldType(),
6125 instruction->GetFieldIndex(),
6126 instruction->GetDexPc(),
6127 calling_convention);
6128}
6129
6130void LocationsBuilderARMVIXL::VisitUnresolvedStaticFieldSet(
6131 HUnresolvedStaticFieldSet* instruction) {
6132 FieldAccessCallingConventionARMVIXL calling_convention;
6133 codegen_->CreateUnresolvedFieldLocationSummary(
6134 instruction, instruction->GetFieldType(), calling_convention);
6135}
6136
6137void InstructionCodeGeneratorARMVIXL::VisitUnresolvedStaticFieldSet(
6138 HUnresolvedStaticFieldSet* instruction) {
6139 FieldAccessCallingConventionARMVIXL calling_convention;
6140 codegen_->GenerateUnresolvedFieldAccess(instruction,
6141 instruction->GetFieldType(),
6142 instruction->GetFieldIndex(),
6143 instruction->GetDexPc(),
6144 calling_convention);
6145}
6146
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006147void LocationsBuilderARMVIXL::VisitNullCheck(HNullCheck* instruction) {
Artem Serov657022c2016-11-23 14:19:38 +00006148 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006149 locations->SetInAt(0, Location::RequiresRegister());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006150}
6151
6152void CodeGeneratorARMVIXL::GenerateImplicitNullCheck(HNullCheck* instruction) {
6153 if (CanMoveNullCheckToUser(instruction)) {
6154 return;
6155 }
6156
6157 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames374ddf32016-11-04 10:40:49 +00006158 // Ensure the pc position is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006159 ExactAssemblyScope aas(GetVIXLAssembler(),
6160 vixl32::kMaxInstructionSizeInBytes,
6161 CodeBufferCheckScope::kMaximumSize);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006162 __ ldr(temps.Acquire(), MemOperand(InputRegisterAt(instruction, 0)));
6163 RecordPcInfo(instruction, instruction->GetDexPc());
6164}
6165
6166void CodeGeneratorARMVIXL::GenerateExplicitNullCheck(HNullCheck* instruction) {
6167 NullCheckSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006168 new (GetScopedAllocator()) NullCheckSlowPathARMVIXL(instruction);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006169 AddSlowPath(slow_path);
xueliang.zhongf51bc622016-11-04 09:23:32 +00006170 __ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006171}
6172
6173void InstructionCodeGeneratorARMVIXL::VisitNullCheck(HNullCheck* instruction) {
6174 codegen_->GenerateNullCheck(instruction);
6175}
6176
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006177void CodeGeneratorARMVIXL::LoadFromShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006178 Location out_loc,
6179 vixl32::Register base,
6180 vixl32::Register reg_index,
6181 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006182 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006183 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6184
6185 switch (type) {
Vladimir Marko61b92282017-10-11 13:23:17 +01006186 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006187 case DataType::Type::kUint8:
Vladimir Marko61b92282017-10-11 13:23:17 +01006188 __ Ldrb(cond, RegisterFrom(out_loc), mem_address);
6189 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006190 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006191 __ Ldrsb(cond, RegisterFrom(out_loc), mem_address);
6192 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006193 case DataType::Type::kUint16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006194 __ Ldrh(cond, RegisterFrom(out_loc), mem_address);
6195 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006196 case DataType::Type::kInt16:
6197 __ Ldrsh(cond, RegisterFrom(out_loc), mem_address);
6198 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006199 case DataType::Type::kReference:
6200 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006201 __ Ldr(cond, RegisterFrom(out_loc), mem_address);
6202 break;
6203 // T32 doesn't support LoadFromShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006204 case DataType::Type::kInt64:
6205 case DataType::Type::kFloat32:
6206 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006207 default:
6208 LOG(FATAL) << "Unreachable type " << type;
6209 UNREACHABLE();
6210 }
6211}
6212
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006213void CodeGeneratorARMVIXL::StoreToShiftedRegOffset(DataType::Type type,
Scott Wakelingc34dba72016-10-03 10:14:44 +01006214 Location loc,
6215 vixl32::Register base,
6216 vixl32::Register reg_index,
6217 vixl32::Condition cond) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006218 uint32_t shift_count = DataType::SizeShift(type);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006219 MemOperand mem_address(base, reg_index, vixl32::LSL, shift_count);
6220
6221 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006222 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006223 case DataType::Type::kUint8:
6224 case DataType::Type::kInt8:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006225 __ Strb(cond, RegisterFrom(loc), mem_address);
6226 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006227 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006228 case DataType::Type::kInt16:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006229 __ Strh(cond, RegisterFrom(loc), mem_address);
6230 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006231 case DataType::Type::kReference:
6232 case DataType::Type::kInt32:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006233 __ Str(cond, RegisterFrom(loc), mem_address);
6234 break;
6235 // T32 doesn't support StoreToShiftedRegOffset mem address mode for these types.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006236 case DataType::Type::kInt64:
6237 case DataType::Type::kFloat32:
6238 case DataType::Type::kFloat64:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006239 default:
6240 LOG(FATAL) << "Unreachable type " << type;
6241 UNREACHABLE();
6242 }
6243}
6244
6245void LocationsBuilderARMVIXL::VisitArrayGet(HArrayGet* instruction) {
6246 bool object_array_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006247 kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006248 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006249 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
6250 object_array_get_with_read_barrier
6251 ? LocationSummary::kCallOnSlowPath
6252 : LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006253 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006254 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelingc34dba72016-10-03 10:14:44 +01006255 }
6256 locations->SetInAt(0, Location::RequiresRegister());
6257 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006258 if (DataType::IsFloatingPointType(instruction->GetType())) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006259 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
6260 } else {
6261 // The output overlaps in the case of an object array get with
6262 // read barriers enabled: we do not want the move to overwrite the
6263 // array's location, as we need it to emit the read barrier.
6264 locations->SetOut(
6265 Location::RequiresRegister(),
6266 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
6267 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006268 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Marko008e09f32018-08-06 15:42:43 +01006269 if (instruction->GetIndex()->IsConstant()) {
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006270 // Array loads with constant index are treated as field loads.
Vladimir Marko008e09f32018-08-06 15:42:43 +01006271 // We need a temporary register for the read barrier load in
6272 // CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier()
6273 // only if the offset is too big.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006274 uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
6275 uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006276 offset += index << DataType::SizeShift(DataType::Type::kReference);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006277 if (offset >= kReferenceLoadMinFarOffset) {
6278 locations->AddTemp(Location::RequiresRegister());
6279 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006280 } else {
Vladimir Marko008e09f32018-08-06 15:42:43 +01006281 // We need a non-scratch temporary for the array data pointer in
6282 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier().
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006283 locations->AddTemp(Location::RequiresRegister());
6284 }
6285 } else if (mirror::kUseStringCompression && instruction->IsStringCharAt()) {
6286 // Also need a temporary for String compression feature.
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006287 locations->AddTemp(Location::RequiresRegister());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006288 }
6289}
6290
6291void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006292 LocationSummary* locations = instruction->GetLocations();
6293 Location obj_loc = locations->InAt(0);
6294 vixl32::Register obj = InputRegisterAt(instruction, 0);
6295 Location index = locations->InAt(1);
6296 Location out_loc = locations->Out();
6297 uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006298 DataType::Type type = instruction->GetType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006299 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
6300 instruction->IsStringCharAt();
6301 HInstruction* array_instr = instruction->GetArray();
6302 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006303
6304 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006305 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006306 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006307 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006308 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006309 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006310 case DataType::Type::kInt32: {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006311 vixl32::Register length;
6312 if (maybe_compressed_char_at) {
6313 length = RegisterFrom(locations->GetTemp(0));
6314 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006315 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6316 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006317 GetAssembler()->LoadFromOffset(kLoadWord, length, obj, count_offset);
6318 codegen_->MaybeRecordImplicitNullCheck(instruction);
6319 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006320 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006321 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006322 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006323 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006324 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006325 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6326 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6327 "Expecting 0=compressed, 1=uncompressed");
Andreas Gampe3db70682018-12-26 15:12:03 -08006328 __ B(cs, &uncompressed_load, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006329 GetAssembler()->LoadFromOffset(kLoadUnsignedByte,
6330 RegisterFrom(out_loc),
6331 obj,
6332 data_offset + const_index);
Anton Kirilov6f644202017-02-27 18:29:45 +00006333 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006334 __ Bind(&uncompressed_load);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006335 GetAssembler()->LoadFromOffset(GetLoadOperandType(DataType::Type::kUint16),
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006336 RegisterFrom(out_loc),
6337 obj,
6338 data_offset + (const_index << 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006339 if (done.IsReferenced()) {
6340 __ Bind(&done);
6341 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006342 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006343 uint32_t full_offset = data_offset + (const_index << DataType::SizeShift(type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006344
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006345 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6346 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006347 LoadOperandType load_type = GetLoadOperandType(type);
6348 GetAssembler()->LoadFromOffset(load_type, RegisterFrom(out_loc), obj, full_offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006349 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006350 }
6351 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006352 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006353 vixl32::Register temp = temps.Acquire();
6354
6355 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006356 // We do not need to compute the intermediate address from the array: the
6357 // input instruction has done it already. See the comment in
6358 // `TryExtractArrayAccessAddress()`.
6359 if (kIsDebugBuild) {
6360 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006361 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006362 }
6363 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006364 } else {
6365 __ Add(temp, obj, data_offset);
6366 }
6367 if (maybe_compressed_char_at) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006368 vixl32::Label uncompressed_load, done;
Anton Kirilov6f644202017-02-27 18:29:45 +00006369 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006370 __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
6371 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
6372 "Expecting 0=compressed, 1=uncompressed");
Andreas Gampe3db70682018-12-26 15:12:03 -08006373 __ B(cs, &uncompressed_load, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006374 __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0));
Anton Kirilov6f644202017-02-27 18:29:45 +00006375 __ B(final_label);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006376 __ Bind(&uncompressed_load);
6377 __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1));
Anton Kirilov6f644202017-02-27 18:29:45 +00006378 if (done.IsReferenced()) {
6379 __ Bind(&done);
6380 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006381 } else {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006382 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6383 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006384 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006385 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006386 }
6387 }
6388 break;
6389 }
6390
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006391 case DataType::Type::kReference: {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006392 // The read barrier instrumentation of object ArrayGet
6393 // instructions does not support the HIntermediateAddress
6394 // instruction.
6395 DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier));
6396
Scott Wakelingc34dba72016-10-03 10:14:44 +01006397 static_assert(
6398 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6399 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
6400 // /* HeapReference<Object> */ out =
6401 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
6402 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006403 // Note that a potential implicit null check is handled in this
6404 // CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier call.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006405 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
6406 if (index.IsConstant()) {
6407 // Array load with a constant index can be treated as a field load.
Vladimir Markodcd117e2018-04-19 11:54:00 +01006408 Location maybe_temp =
6409 (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006410 data_offset += Int32ConstantFrom(index) << DataType::SizeShift(type);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006411 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6412 out_loc,
6413 obj,
6414 data_offset,
Vladimir Markodcd117e2018-04-19 11:54:00 +01006415 maybe_temp,
Andreas Gampe3db70682018-12-26 15:12:03 -08006416 /* needs_null_check= */ false);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006417 } else {
Vladimir Markodcd117e2018-04-19 11:54:00 +01006418 Location temp = locations->GetTemp(0);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006419 codegen_->GenerateArrayLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08006420 out_loc, obj, data_offset, index, temp, /* needs_null_check= */ false);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01006421 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006422 } else {
6423 vixl32::Register out = OutputRegister(instruction);
6424 if (index.IsConstant()) {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006425 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6426 {
6427 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6428 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6429 GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
6430 codegen_->MaybeRecordImplicitNullCheck(instruction);
6431 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006432 // If read barriers are enabled, emit read barriers other than
6433 // Baker's using a slow path (and also unpoison the loaded
6434 // reference, if heap poisoning is enabled).
6435 codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset);
6436 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006437 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006438 vixl32::Register temp = temps.Acquire();
6439
6440 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006441 // We do not need to compute the intermediate address from the array: the
6442 // input instruction has done it already. See the comment in
6443 // `TryExtractArrayAccessAddress()`.
6444 if (kIsDebugBuild) {
6445 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006446 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006447 }
6448 temp = obj;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006449 } else {
6450 __ Add(temp, obj, data_offset);
6451 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006452 {
6453 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6454 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6455 codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index));
6456 temps.Close();
6457 codegen_->MaybeRecordImplicitNullCheck(instruction);
6458 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006459 // If read barriers are enabled, emit read barriers other than
6460 // Baker's using a slow path (and also unpoison the loaded
6461 // reference, if heap poisoning is enabled).
6462 codegen_->MaybeGenerateReadBarrierSlow(
6463 instruction, out_loc, out_loc, obj_loc, data_offset, index);
6464 }
6465 }
6466 break;
6467 }
6468
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006469 case DataType::Type::kInt64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006470 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6471 // As two macro instructions can be emitted the max size is doubled.
6472 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006473 if (index.IsConstant()) {
6474 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006475 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006476 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
6477 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006478 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006479 vixl32::Register temp = temps.Acquire();
6480 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6481 GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), temp, data_offset);
6482 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006483 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006484 break;
6485 }
6486
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006487 case DataType::Type::kFloat32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006488 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6489 // As two macro instructions can be emitted the max size is doubled.
6490 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006491 vixl32::SRegister out = SRegisterFrom(out_loc);
6492 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006493 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006494 GetAssembler()->LoadSFromOffset(out, obj, offset);
6495 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006496 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006497 vixl32::Register temp = temps.Acquire();
6498 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6499 GetAssembler()->LoadSFromOffset(out, temp, data_offset);
6500 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006501 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006502 break;
6503 }
6504
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006505 case DataType::Type::kFloat64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006506 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
6507 // As two macro instructions can be emitted the max size is doubled.
6508 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006509 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006510 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006511 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
6512 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006513 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006514 vixl32::Register temp = temps.Acquire();
6515 __ Add(temp, obj, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6516 GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), temp, data_offset);
6517 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006518 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006519 break;
6520 }
6521
Aart Bik66c158e2018-01-31 12:55:04 -08006522 case DataType::Type::kUint32:
6523 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006524 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006525 LOG(FATAL) << "Unreachable type " << type;
6526 UNREACHABLE();
6527 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006528}
6529
6530void LocationsBuilderARMVIXL::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006531 DataType::Type value_type = instruction->GetComponentType();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006532
6533 bool needs_write_barrier =
6534 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006535 bool needs_type_check = instruction->NeedsTypeCheck();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006536
Vladimir Markoca6fff82017-10-03 14:49:14 +01006537 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Scott Wakelingc34dba72016-10-03 10:14:44 +01006538 instruction,
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006539 needs_type_check ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006540
6541 locations->SetInAt(0, Location::RequiresRegister());
6542 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006543 if (DataType::IsFloatingPointType(value_type)) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006544 locations->SetInAt(2, Location::RequiresFpuRegister());
6545 } else {
6546 locations->SetInAt(2, Location::RequiresRegister());
6547 }
6548 if (needs_write_barrier) {
6549 // Temporary registers for the write barrier.
6550 locations->AddTemp(Location::RequiresRegister()); // Possibly used for ref. poisoning too.
6551 locations->AddTemp(Location::RequiresRegister());
6552 }
6553}
6554
6555void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006556 LocationSummary* locations = instruction->GetLocations();
6557 vixl32::Register array = InputRegisterAt(instruction, 0);
6558 Location index = locations->InAt(1);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006559 DataType::Type value_type = instruction->GetComponentType();
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006560 bool needs_type_check = instruction->NeedsTypeCheck();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006561 bool needs_write_barrier =
6562 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
6563 uint32_t data_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006564 mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006565 Location value_loc = locations->InAt(2);
6566 HInstruction* array_instr = instruction->GetArray();
6567 bool has_intermediate_address = array_instr->IsIntermediateAddress();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006568
6569 switch (value_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006570 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006571 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006572 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006573 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01006574 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006575 case DataType::Type::kInt32: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006576 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006577 int32_t const_index = Int32ConstantFrom(index);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006578 uint32_t full_offset =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006579 data_offset + (const_index << DataType::SizeShift(value_type));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006580 StoreOperandType store_type = GetStoreOperandType(value_type);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006581 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6582 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006583 GetAssembler()->StoreToOffset(store_type, RegisterFrom(value_loc), array, full_offset);
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006584 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006585 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006586 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006587 vixl32::Register temp = temps.Acquire();
6588
6589 if (has_intermediate_address) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006590 // We do not need to compute the intermediate address from the array: the
6591 // input instruction has done it already. See the comment in
6592 // `TryExtractArrayAccessAddress()`.
6593 if (kIsDebugBuild) {
6594 HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
Anton Kirilov644032c2016-12-06 17:51:43 +00006595 DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
Artem Serov2bbc9532016-10-21 11:51:50 +01006596 }
6597 temp = array;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006598 } else {
6599 __ Add(temp, array, data_offset);
6600 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006601 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6602 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006603 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006604 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006605 }
6606 break;
6607 }
6608
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006609 case DataType::Type::kReference: {
Scott Wakelingc34dba72016-10-03 10:14:44 +01006610 vixl32::Register value = RegisterFrom(value_loc);
6611 // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet.
6612 // See the comment in instruction_simplifier_shared.cc.
6613 DCHECK(!has_intermediate_address);
6614
6615 if (instruction->InputAt(2)->IsNullConstant()) {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006616 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6617 // As two macro instructions can be emitted the max size is doubled.
6618 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006619 // Just setting null.
6620 if (index.IsConstant()) {
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006621 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006622 GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
6623 } else {
6624 DCHECK(index.IsRegister()) << index;
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006625 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006626 vixl32::Register temp = temps.Acquire();
6627 __ Add(temp, array, data_offset);
6628 codegen_->StoreToShiftedRegOffset(value_type, value_loc, temp, RegisterFrom(index));
6629 }
6630 codegen_->MaybeRecordImplicitNullCheck(instruction);
6631 DCHECK(!needs_write_barrier);
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006632 DCHECK(!needs_type_check);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006633 break;
6634 }
6635
6636 DCHECK(needs_write_barrier);
6637 Location temp1_loc = locations->GetTemp(0);
6638 vixl32::Register temp1 = RegisterFrom(temp1_loc);
6639 Location temp2_loc = locations->GetTemp(1);
6640 vixl32::Register temp2 = RegisterFrom(temp2_loc);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006641
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006642 bool can_value_be_null = instruction->GetValueCanBeNull();
6643 vixl32::Label do_store;
6644 if (can_value_be_null) {
6645 __ CompareAndBranchIfZero(value, &do_store, /* is_far_target= */ false);
6646 }
6647
6648 SlowPathCodeARMVIXL* slow_path = nullptr;
6649 if (needs_type_check) {
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006650 slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathARMVIXL(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006651 codegen_->AddSlowPath(slow_path);
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006652
6653 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
6654 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
6655 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
Scott Wakelingc34dba72016-10-03 10:14:44 +01006656
6657 // Note that when read barriers are enabled, the type checks
6658 // are performed without read barriers. This is fine, even in
6659 // the case where a class object is in the from-space after
6660 // the flip, as a comparison involving such a type would not
6661 // produce a false positive; it may of course produce a false
6662 // negative, in which case we would take the ArraySet slow
6663 // path.
6664
Alexandre Rames374ddf32016-11-04 10:40:49 +00006665 {
6666 // Ensure we record the pc position immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00006667 ExactAssemblyScope aas(GetVIXLAssembler(),
6668 vixl32::kMaxInstructionSizeInBytes,
6669 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006670 // /* HeapReference<Class> */ temp1 = array->klass_
6671 __ ldr(temp1, MemOperand(array, class_offset));
6672 codegen_->MaybeRecordImplicitNullCheck(instruction);
6673 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006674 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6675
6676 // /* HeapReference<Class> */ temp1 = temp1->component_type_
6677 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, component_offset);
6678 // /* HeapReference<Class> */ temp2 = value->klass_
6679 GetAssembler()->LoadFromOffset(kLoadWord, temp2, value, class_offset);
6680 // If heap poisoning is enabled, no need to unpoison `temp1`
6681 // nor `temp2`, as we are comparing two poisoned references.
6682 __ Cmp(temp1, temp2);
6683
6684 if (instruction->StaticTypeOfArrayIsObjectArray()) {
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006685 vixl32::Label do_put;
6686 __ B(eq, &do_put, /* is_far_target= */ false);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006687 // If heap poisoning is enabled, the `temp1` reference has
6688 // not been unpoisoned yet; unpoison it now.
6689 GetAssembler()->MaybeUnpoisonHeapReference(temp1);
6690
6691 // /* HeapReference<Class> */ temp1 = temp1->super_class_
6692 GetAssembler()->LoadFromOffset(kLoadWord, temp1, temp1, super_offset);
6693 // If heap poisoning is enabled, no need to unpoison
6694 // `temp1`, as we are comparing against null below.
xueliang.zhongf51bc622016-11-04 09:23:32 +00006695 __ CompareAndBranchIfNonZero(temp1, slow_path->GetEntryLabel());
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006696 __ Bind(&do_put);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006697 } else {
6698 __ B(ne, slow_path->GetEntryLabel());
6699 }
6700 }
6701
Vladimir Marko8fa839c2019-05-16 12:50:47 +00006702 codegen_->MarkGCCard(temp1, temp2, array, value, /* can_be_null= */ false);
6703
6704 if (can_value_be_null) {
6705 DCHECK(do_store.IsReferenced());
6706 __ Bind(&do_store);
6707 }
6708
Scott Wakelingc34dba72016-10-03 10:14:44 +01006709 vixl32::Register source = value;
6710 if (kPoisonHeapReferences) {
6711 // Note that in the case where `value` is a null reference,
6712 // we do not enter this block, as a null reference does not
6713 // need poisoning.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006714 DCHECK_EQ(value_type, DataType::Type::kReference);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006715 __ Mov(temp1, value);
6716 GetAssembler()->PoisonHeapReference(temp1);
6717 source = temp1;
6718 }
6719
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006720 {
6721 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6722 // As two macro instructions can be emitted the max size is doubled.
6723 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
6724 if (index.IsConstant()) {
6725 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
6726 GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
6727 } else {
6728 DCHECK(index.IsRegister()) << index;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006729
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006730 UseScratchRegisterScope temps(GetVIXLAssembler());
6731 vixl32::Register temp = temps.Acquire();
6732 __ Add(temp, array, data_offset);
6733 codegen_->StoreToShiftedRegOffset(value_type,
6734 LocationFrom(source),
6735 temp,
6736 RegisterFrom(index));
6737 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006738
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006739 if (can_value_be_null || !needs_type_check) {
6740 codegen_->MaybeRecordImplicitNullCheck(instruction);
6741 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006742 }
6743
Vladimir Marko0dda8c82019-05-16 12:47:40 +00006744 if (slow_path != nullptr) {
6745 __ Bind(slow_path->GetExitLabel());
6746 }
6747
Scott Wakelingc34dba72016-10-03 10:14:44 +01006748 break;
6749 }
6750
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006751 case DataType::Type::kInt64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006752 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6753 // As two macro instructions can be emitted the max size is doubled.
6754 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006755 Location value = locations->InAt(2);
6756 if (index.IsConstant()) {
6757 size_t offset =
Anton Kirilov644032c2016-12-06 17:51:43 +00006758 (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006759 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
6760 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006761 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006762 vixl32::Register temp = temps.Acquire();
6763 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6764 GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), temp, data_offset);
6765 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006766 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006767 break;
6768 }
6769
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006770 case DataType::Type::kFloat32: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006771 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6772 // As two macro instructions can be emitted the max size is doubled.
6773 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006774 Location value = locations->InAt(2);
6775 DCHECK(value.IsFpuRegister());
6776 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006777 size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006778 GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
6779 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006780 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006781 vixl32::Register temp = temps.Acquire();
6782 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_4));
6783 GetAssembler()->StoreSToOffset(SRegisterFrom(value), temp, data_offset);
6784 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006785 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006786 break;
6787 }
6788
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006789 case DataType::Type::kFloat64: {
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006790 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
6791 // As two macro instructions can be emitted the max size is doubled.
6792 EmissionCheckScope guard(GetVIXLAssembler(), 2 * kMaxMacroInstructionSizeInBytes);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006793 Location value = locations->InAt(2);
6794 DCHECK(value.IsFpuRegisterPair());
6795 if (index.IsConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00006796 size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
Scott Wakelingc34dba72016-10-03 10:14:44 +01006797 GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
6798 } else {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00006799 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelingc34dba72016-10-03 10:14:44 +01006800 vixl32::Register temp = temps.Acquire();
6801 __ Add(temp, array, Operand(RegisterFrom(index), vixl32::LSL, TIMES_8));
6802 GetAssembler()->StoreDToOffset(DRegisterFrom(value), temp, data_offset);
6803 }
Evgeny Astigeevich98416bf2019-09-09 14:52:12 +01006804 codegen_->MaybeRecordImplicitNullCheck(instruction);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006805 break;
6806 }
6807
Aart Bik66c158e2018-01-31 12:55:04 -08006808 case DataType::Type::kUint32:
6809 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006810 case DataType::Type::kVoid:
Scott Wakelingc34dba72016-10-03 10:14:44 +01006811 LOG(FATAL) << "Unreachable type " << value_type;
6812 UNREACHABLE();
6813 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006814}
6815
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006816void LocationsBuilderARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6817 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006818 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006819 locations->SetInAt(0, Location::RequiresRegister());
6820 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6821}
6822
6823void InstructionCodeGeneratorARMVIXL::VisitArrayLength(HArrayLength* instruction) {
6824 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
6825 vixl32::Register obj = InputRegisterAt(instruction, 0);
6826 vixl32::Register out = OutputRegister(instruction);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006827 {
Artem Serov0fb37192016-12-06 18:13:40 +00006828 ExactAssemblyScope aas(GetVIXLAssembler(),
6829 vixl32::kMaxInstructionSizeInBytes,
6830 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00006831 __ ldr(out, MemOperand(obj, offset));
6832 codegen_->MaybeRecordImplicitNullCheck(instruction);
6833 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006834 // Mask out compression flag from String's array length.
6835 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01006836 __ Lsr(out, out, 1u);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01006837 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006838}
6839
Artem Serov2bbc9532016-10-21 11:51:50 +01006840void LocationsBuilderARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Artem Serov2bbc9532016-10-21 11:51:50 +01006841 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006842 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01006843
6844 locations->SetInAt(0, Location::RequiresRegister());
6845 locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset()));
6846 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
6847}
6848
6849void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddress(HIntermediateAddress* instruction) {
6850 vixl32::Register out = OutputRegister(instruction);
6851 vixl32::Register first = InputRegisterAt(instruction, 0);
6852 Location second = instruction->GetLocations()->InAt(1);
6853
Artem Serov2bbc9532016-10-21 11:51:50 +01006854 if (second.IsRegister()) {
6855 __ Add(out, first, RegisterFrom(second));
6856 } else {
Anton Kirilov644032c2016-12-06 17:51:43 +00006857 __ Add(out, first, Int32ConstantFrom(second));
Artem Serov2bbc9532016-10-21 11:51:50 +01006858 }
6859}
6860
Artem Serove1811ed2017-04-27 16:50:47 +01006861void LocationsBuilderARMVIXL::VisitIntermediateAddressIndex(
6862 HIntermediateAddressIndex* instruction) {
6863 LOG(FATAL) << "Unreachable " << instruction->GetId();
6864}
6865
6866void InstructionCodeGeneratorARMVIXL::VisitIntermediateAddressIndex(
6867 HIntermediateAddressIndex* instruction) {
6868 LOG(FATAL) << "Unreachable " << instruction->GetId();
6869}
6870
Scott Wakelingc34dba72016-10-03 10:14:44 +01006871void LocationsBuilderARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
6872 RegisterSet caller_saves = RegisterSet::Empty();
6873 InvokeRuntimeCallingConventionARMVIXL calling_convention;
6874 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(0)));
6875 caller_saves.Add(LocationFrom(calling_convention.GetRegisterAt(1)));
6876 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
Artem Serov2dd053d2017-03-08 14:54:06 +00006877
6878 HInstruction* index = instruction->InputAt(0);
6879 HInstruction* length = instruction->InputAt(1);
6880 // If both index and length are constants we can statically check the bounds. But if at least one
6881 // of them is not encodable ArmEncodableConstantOrRegister will create
6882 // Location::RequiresRegister() which is not desired to happen. Instead we create constant
6883 // locations.
6884 bool both_const = index->IsConstant() && length->IsConstant();
6885 locations->SetInAt(0, both_const
6886 ? Location::ConstantLocation(index->AsConstant())
6887 : ArmEncodableConstantOrRegister(index, CMP));
6888 locations->SetInAt(1, both_const
6889 ? Location::ConstantLocation(length->AsConstant())
6890 : ArmEncodableConstantOrRegister(length, CMP));
Scott Wakelingc34dba72016-10-03 10:14:44 +01006891}
6892
6893void InstructionCodeGeneratorARMVIXL::VisitBoundsCheck(HBoundsCheck* instruction) {
Artem Serov2dd053d2017-03-08 14:54:06 +00006894 LocationSummary* locations = instruction->GetLocations();
6895 Location index_loc = locations->InAt(0);
6896 Location length_loc = locations->InAt(1);
Scott Wakelingc34dba72016-10-03 10:14:44 +01006897
Artem Serov2dd053d2017-03-08 14:54:06 +00006898 if (length_loc.IsConstant()) {
6899 int32_t length = Int32ConstantFrom(length_loc);
6900 if (index_loc.IsConstant()) {
6901 // BCE will remove the bounds check if we are guaranteed to pass.
6902 int32_t index = Int32ConstantFrom(index_loc);
6903 if (index < 0 || index >= length) {
6904 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006905 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006906 codegen_->AddSlowPath(slow_path);
6907 __ B(slow_path->GetEntryLabel());
6908 } else {
6909 // Some optimization after BCE may have generated this, and we should not
6910 // generate a bounds check if it is a valid range.
6911 }
6912 return;
6913 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006914
Artem Serov2dd053d2017-03-08 14:54:06 +00006915 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006916 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006917 __ Cmp(RegisterFrom(index_loc), length);
6918 codegen_->AddSlowPath(slow_path);
6919 __ B(hs, slow_path->GetEntryLabel());
6920 } else {
6921 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006922 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARMVIXL(instruction);
Artem Serov2dd053d2017-03-08 14:54:06 +00006923 __ Cmp(RegisterFrom(length_loc), InputOperandAt(instruction, 0));
6924 codegen_->AddSlowPath(slow_path);
6925 __ B(ls, slow_path->GetEntryLabel());
6926 }
Scott Wakelingc34dba72016-10-03 10:14:44 +01006927}
6928
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006929void CodeGeneratorARMVIXL::MarkGCCard(vixl32::Register temp,
6930 vixl32::Register card,
6931 vixl32::Register object,
6932 vixl32::Register value,
6933 bool can_be_null) {
6934 vixl32::Label is_null;
6935 if (can_be_null) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00006936 __ CompareAndBranchIfZero(value, &is_null);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006937 }
Roland Levillainc73f0522018-08-14 15:16:50 +01006938 // Load the address of the card table into `card`.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006939 GetAssembler()->LoadFromOffset(
6940 kLoadWord, card, tr, Thread::CardTableOffset<kArmPointerSize>().Int32Value());
Roland Levillainc73f0522018-08-14 15:16:50 +01006941 // Calculate the offset (in the card table) of the card corresponding to
6942 // `object`.
Scott Wakelingb77051e2016-11-21 19:46:00 +00006943 __ Lsr(temp, object, Operand::From(gc::accounting::CardTable::kCardShift));
Roland Levillainc73f0522018-08-14 15:16:50 +01006944 // Write the `art::gc::accounting::CardTable::kCardDirty` value into the
6945 // `object`'s card.
6946 //
6947 // Register `card` contains the address of the card table. Note that the card
6948 // table's base is biased during its creation so that it always starts at an
6949 // address whose least-significant byte is equal to `kCardDirty` (see
6950 // art::gc::accounting::CardTable::Create). Therefore the STRB instruction
6951 // below writes the `kCardDirty` (byte) value into the `object`'s card
6952 // (located at `card + object >> kCardShift`).
6953 //
6954 // This dual use of the value in register `card` (1. to calculate the location
6955 // of the card to mark; and 2. to load the `kCardDirty` value) saves a load
6956 // (no need to explicitly load `kCardDirty` as an immediate value).
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006957 __ Strb(card, MemOperand(card, temp));
6958 if (can_be_null) {
6959 __ Bind(&is_null);
6960 }
6961}
6962
Scott Wakelingfe885462016-09-22 10:24:38 +01006963void LocationsBuilderARMVIXL::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
6964 LOG(FATAL) << "Unreachable";
6965}
6966
6967void InstructionCodeGeneratorARMVIXL::VisitParallelMove(HParallelMove* instruction) {
Vladimir Markobea75ff2017-10-11 20:39:54 +01006968 if (instruction->GetNext()->IsSuspendCheck() &&
6969 instruction->GetBlock()->GetLoopInformation() != nullptr) {
6970 HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
6971 // The back edge will generate the suspend check.
6972 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
6973 }
6974
Scott Wakelingfe885462016-09-22 10:24:38 +01006975 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
6976}
6977
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006978void LocationsBuilderARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01006979 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
6980 instruction, LocationSummary::kCallOnSlowPath);
Artem Serov657022c2016-11-23 14:19:38 +00006981 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006982}
6983
6984void InstructionCodeGeneratorARMVIXL::VisitSuspendCheck(HSuspendCheck* instruction) {
6985 HBasicBlock* block = instruction->GetBlock();
6986 if (block->GetLoopInformation() != nullptr) {
6987 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
6988 // The back edge will generate the suspend check.
6989 return;
6990 }
6991 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
6992 // The goto will generate the suspend check.
6993 return;
6994 }
6995 GenerateSuspendCheck(instruction, nullptr);
Andreas Gampe3db70682018-12-26 15:12:03 -08006996 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 13);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01006997}
6998
6999void InstructionCodeGeneratorARMVIXL::GenerateSuspendCheck(HSuspendCheck* instruction,
7000 HBasicBlock* successor) {
7001 SuspendCheckSlowPathARMVIXL* slow_path =
7002 down_cast<SuspendCheckSlowPathARMVIXL*>(instruction->GetSlowPath());
7003 if (slow_path == nullptr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007004 slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007005 new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathARMVIXL(instruction, successor);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007006 instruction->SetSlowPath(slow_path);
7007 codegen_->AddSlowPath(slow_path);
7008 if (successor != nullptr) {
7009 DCHECK(successor->IsLoopHeader());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007010 }
7011 } else {
7012 DCHECK_EQ(slow_path->GetSuccessor(), successor);
7013 }
7014
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007015 UseScratchRegisterScope temps(GetVIXLAssembler());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007016 vixl32::Register temp = temps.Acquire();
7017 GetAssembler()->LoadFromOffset(
7018 kLoadUnsignedHalfword, temp, tr, Thread::ThreadFlagsOffset<kArmPointerSize>().Int32Value());
7019 if (successor == nullptr) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007020 __ CompareAndBranchIfNonZero(temp, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007021 __ Bind(slow_path->GetReturnLabel());
7022 } else {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007023 __ CompareAndBranchIfZero(temp, codegen_->GetLabelOf(successor));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007024 __ B(slow_path->GetEntryLabel());
7025 }
7026}
7027
Scott Wakelingfe885462016-09-22 10:24:38 +01007028ArmVIXLAssembler* ParallelMoveResolverARMVIXL::GetAssembler() const {
7029 return codegen_->GetAssembler();
7030}
7031
7032void ParallelMoveResolverARMVIXL::EmitMove(size_t index) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007033 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Scott Wakelingfe885462016-09-22 10:24:38 +01007034 MoveOperands* move = moves_[index];
7035 Location source = move->GetSource();
7036 Location destination = move->GetDestination();
7037
7038 if (source.IsRegister()) {
7039 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007040 __ Mov(RegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007041 } else if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007042 __ Vmov(SRegisterFrom(destination), RegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007043 } else {
7044 DCHECK(destination.IsStackSlot());
7045 GetAssembler()->StoreToOffset(kStoreWord,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007046 RegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01007047 sp,
7048 destination.GetStackIndex());
7049 }
7050 } else if (source.IsStackSlot()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007051 if (destination.IsRegister()) {
7052 GetAssembler()->LoadFromOffset(kLoadWord,
7053 RegisterFrom(destination),
7054 sp,
7055 source.GetStackIndex());
7056 } else if (destination.IsFpuRegister()) {
7057 GetAssembler()->LoadSFromOffset(SRegisterFrom(destination), sp, source.GetStackIndex());
7058 } else {
7059 DCHECK(destination.IsStackSlot());
7060 vixl32::Register temp = temps.Acquire();
7061 GetAssembler()->LoadFromOffset(kLoadWord, temp, sp, source.GetStackIndex());
7062 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7063 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007064 } else if (source.IsFpuRegister()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007065 if (destination.IsRegister()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007066 __ Vmov(RegisterFrom(destination), SRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007067 } else if (destination.IsFpuRegister()) {
7068 __ Vmov(SRegisterFrom(destination), SRegisterFrom(source));
7069 } else {
7070 DCHECK(destination.IsStackSlot());
7071 GetAssembler()->StoreSToOffset(SRegisterFrom(source), sp, destination.GetStackIndex());
7072 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007073 } else if (source.IsDoubleStackSlot()) {
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007074 if (destination.IsDoubleStackSlot()) {
7075 vixl32::DRegister temp = temps.AcquireD();
7076 GetAssembler()->LoadDFromOffset(temp, sp, source.GetStackIndex());
7077 GetAssembler()->StoreDToOffset(temp, sp, destination.GetStackIndex());
7078 } else if (destination.IsRegisterPair()) {
7079 DCHECK(ExpectedPairLayout(destination));
7080 GetAssembler()->LoadFromOffset(
7081 kLoadWordPair, LowRegisterFrom(destination), sp, source.GetStackIndex());
7082 } else {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007083 DCHECK(destination.IsFpuRegisterPair()) << destination;
7084 GetAssembler()->LoadDFromOffset(DRegisterFrom(destination), sp, source.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007085 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007086 } else if (source.IsRegisterPair()) {
7087 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007088 __ Mov(LowRegisterFrom(destination), LowRegisterFrom(source));
7089 __ Mov(HighRegisterFrom(destination), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007090 } else if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007091 __ Vmov(DRegisterFrom(destination), LowRegisterFrom(source), HighRegisterFrom(source));
Scott Wakelingfe885462016-09-22 10:24:38 +01007092 } else {
7093 DCHECK(destination.IsDoubleStackSlot()) << destination;
7094 DCHECK(ExpectedPairLayout(source));
7095 GetAssembler()->StoreToOffset(kStoreWordPair,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007096 LowRegisterFrom(source),
Scott Wakelingfe885462016-09-22 10:24:38 +01007097 sp,
7098 destination.GetStackIndex());
7099 }
7100 } else if (source.IsFpuRegisterPair()) {
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007101 if (destination.IsRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007102 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), DRegisterFrom(source));
Alexandre Ramesb45fbaa52016-10-17 14:57:13 +01007103 } else if (destination.IsFpuRegisterPair()) {
7104 __ Vmov(DRegisterFrom(destination), DRegisterFrom(source));
7105 } else {
7106 DCHECK(destination.IsDoubleStackSlot()) << destination;
7107 GetAssembler()->StoreDToOffset(DRegisterFrom(source), sp, destination.GetStackIndex());
7108 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007109 } else {
7110 DCHECK(source.IsConstant()) << source;
7111 HConstant* constant = source.GetConstant();
7112 if (constant->IsIntConstant() || constant->IsNullConstant()) {
7113 int32_t value = CodeGenerator::GetInt32ValueOf(constant);
7114 if (destination.IsRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007115 __ Mov(RegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007116 } else {
7117 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007118 vixl32::Register temp = temps.Acquire();
7119 __ Mov(temp, value);
7120 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7121 }
7122 } else if (constant->IsLongConstant()) {
Anton Kirilov644032c2016-12-06 17:51:43 +00007123 int64_t value = Int64ConstantFrom(source);
Scott Wakelingfe885462016-09-22 10:24:38 +01007124 if (destination.IsRegisterPair()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007125 __ Mov(LowRegisterFrom(destination), Low32Bits(value));
7126 __ Mov(HighRegisterFrom(destination), High32Bits(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007127 } else {
7128 DCHECK(destination.IsDoubleStackSlot()) << destination;
Scott Wakelingfe885462016-09-22 10:24:38 +01007129 vixl32::Register temp = temps.Acquire();
7130 __ Mov(temp, Low32Bits(value));
7131 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7132 __ Mov(temp, High32Bits(value));
7133 GetAssembler()->StoreToOffset(kStoreWord,
7134 temp,
7135 sp,
7136 destination.GetHighStackIndex(kArmWordSize));
7137 }
7138 } else if (constant->IsDoubleConstant()) {
7139 double value = constant->AsDoubleConstant()->GetValue();
7140 if (destination.IsFpuRegisterPair()) {
Scott Wakelingc34dba72016-10-03 10:14:44 +01007141 __ Vmov(DRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007142 } else {
7143 DCHECK(destination.IsDoubleStackSlot()) << destination;
7144 uint64_t int_value = bit_cast<uint64_t, double>(value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007145 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007146 __ Mov(temp, Low32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007147 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007148 __ Mov(temp, High32Bits(int_value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007149 GetAssembler()->StoreToOffset(kStoreWord,
7150 temp,
7151 sp,
7152 destination.GetHighStackIndex(kArmWordSize));
7153 }
7154 } else {
7155 DCHECK(constant->IsFloatConstant()) << constant->DebugName();
7156 float value = constant->AsFloatConstant()->GetValue();
7157 if (destination.IsFpuRegister()) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007158 __ Vmov(SRegisterFrom(destination), value);
Scott Wakelingfe885462016-09-22 10:24:38 +01007159 } else {
7160 DCHECK(destination.IsStackSlot());
Scott Wakelingfe885462016-09-22 10:24:38 +01007161 vixl32::Register temp = temps.Acquire();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007162 __ Mov(temp, bit_cast<int32_t, float>(value));
Scott Wakelingfe885462016-09-22 10:24:38 +01007163 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
7164 }
7165 }
7166 }
7167}
7168
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007169void ParallelMoveResolverARMVIXL::Exchange(vixl32::Register reg, int mem) {
7170 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7171 vixl32::Register temp = temps.Acquire();
7172 __ Mov(temp, reg);
7173 GetAssembler()->LoadFromOffset(kLoadWord, reg, sp, mem);
7174 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Scott Wakelingfe885462016-09-22 10:24:38 +01007175}
7176
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007177void ParallelMoveResolverARMVIXL::Exchange(int mem1, int mem2) {
7178 // TODO(VIXL32): Double check the performance of this implementation.
7179 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007180 vixl32::Register temp1 = temps.Acquire();
7181 ScratchRegisterScope ensure_scratch(
7182 this, temp1.GetCode(), r0.GetCode(), codegen_->GetNumberOfCoreRegisters());
7183 vixl32::Register temp2(ensure_scratch.GetRegister());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007184
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007185 int stack_offset = ensure_scratch.IsSpilled() ? kArmWordSize : 0;
7186 GetAssembler()->LoadFromOffset(kLoadWord, temp1, sp, mem1 + stack_offset);
7187 GetAssembler()->LoadFromOffset(kLoadWord, temp2, sp, mem2 + stack_offset);
7188 GetAssembler()->StoreToOffset(kStoreWord, temp1, sp, mem2 + stack_offset);
7189 GetAssembler()->StoreToOffset(kStoreWord, temp2, sp, mem1 + stack_offset);
Scott Wakelingfe885462016-09-22 10:24:38 +01007190}
7191
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007192void ParallelMoveResolverARMVIXL::EmitSwap(size_t index) {
7193 MoveOperands* move = moves_[index];
7194 Location source = move->GetSource();
7195 Location destination = move->GetDestination();
7196 UseScratchRegisterScope temps(GetAssembler()->GetVIXLAssembler());
7197
7198 if (source.IsRegister() && destination.IsRegister()) {
7199 vixl32::Register temp = temps.Acquire();
7200 DCHECK(!RegisterFrom(source).Is(temp));
7201 DCHECK(!RegisterFrom(destination).Is(temp));
7202 __ Mov(temp, RegisterFrom(destination));
7203 __ Mov(RegisterFrom(destination), RegisterFrom(source));
7204 __ Mov(RegisterFrom(source), temp);
7205 } else if (source.IsRegister() && destination.IsStackSlot()) {
7206 Exchange(RegisterFrom(source), destination.GetStackIndex());
7207 } else if (source.IsStackSlot() && destination.IsRegister()) {
7208 Exchange(RegisterFrom(destination), source.GetStackIndex());
7209 } else if (source.IsStackSlot() && destination.IsStackSlot()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007210 Exchange(source.GetStackIndex(), destination.GetStackIndex());
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007211 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007212 vixl32::Register temp = temps.Acquire();
Anton Kirilovdda43962016-11-21 19:55:20 +00007213 __ Vmov(temp, SRegisterFrom(source));
7214 __ Vmov(SRegisterFrom(source), SRegisterFrom(destination));
7215 __ Vmov(SRegisterFrom(destination), temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007216 } else if (source.IsRegisterPair() && destination.IsRegisterPair()) {
7217 vixl32::DRegister temp = temps.AcquireD();
7218 __ Vmov(temp, LowRegisterFrom(source), HighRegisterFrom(source));
7219 __ Mov(LowRegisterFrom(source), LowRegisterFrom(destination));
7220 __ Mov(HighRegisterFrom(source), HighRegisterFrom(destination));
7221 __ Vmov(LowRegisterFrom(destination), HighRegisterFrom(destination), temp);
7222 } else if (source.IsRegisterPair() || destination.IsRegisterPair()) {
7223 vixl32::Register low_reg = LowRegisterFrom(source.IsRegisterPair() ? source : destination);
7224 int mem = source.IsRegisterPair() ? destination.GetStackIndex() : source.GetStackIndex();
7225 DCHECK(ExpectedPairLayout(source.IsRegisterPair() ? source : destination));
7226 vixl32::DRegister temp = temps.AcquireD();
7227 __ Vmov(temp, low_reg, vixl32::Register(low_reg.GetCode() + 1));
7228 GetAssembler()->LoadFromOffset(kLoadWordPair, low_reg, sp, mem);
7229 GetAssembler()->StoreDToOffset(temp, sp, mem);
7230 } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007231 vixl32::DRegister first = DRegisterFrom(source);
7232 vixl32::DRegister second = DRegisterFrom(destination);
7233 vixl32::DRegister temp = temps.AcquireD();
7234 __ Vmov(temp, first);
7235 __ Vmov(first, second);
7236 __ Vmov(second, temp);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007237 } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007238 vixl32::DRegister reg = source.IsFpuRegisterPair()
7239 ? DRegisterFrom(source)
7240 : DRegisterFrom(destination);
7241 int mem = source.IsFpuRegisterPair()
7242 ? destination.GetStackIndex()
7243 : source.GetStackIndex();
7244 vixl32::DRegister temp = temps.AcquireD();
7245 __ Vmov(temp, reg);
7246 GetAssembler()->LoadDFromOffset(reg, sp, mem);
7247 GetAssembler()->StoreDToOffset(temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007248 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
Anton Kirilovdda43962016-11-21 19:55:20 +00007249 vixl32::SRegister reg = source.IsFpuRegister()
7250 ? SRegisterFrom(source)
7251 : SRegisterFrom(destination);
7252 int mem = source.IsFpuRegister()
7253 ? destination.GetStackIndex()
7254 : source.GetStackIndex();
7255 vixl32::Register temp = temps.Acquire();
7256 __ Vmov(temp, reg);
7257 GetAssembler()->LoadSFromOffset(reg, sp, mem);
7258 GetAssembler()->StoreToOffset(kStoreWord, temp, sp, mem);
Alexandre Rames9c19bd62016-10-24 11:50:32 +01007259 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
7260 vixl32::DRegister temp1 = temps.AcquireD();
7261 vixl32::DRegister temp2 = temps.AcquireD();
7262 __ Vldr(temp1, MemOperand(sp, source.GetStackIndex()));
7263 __ Vldr(temp2, MemOperand(sp, destination.GetStackIndex()));
7264 __ Vstr(temp1, MemOperand(sp, destination.GetStackIndex()));
7265 __ Vstr(temp2, MemOperand(sp, source.GetStackIndex()));
7266 } else {
7267 LOG(FATAL) << "Unimplemented" << source << " <-> " << destination;
7268 }
Scott Wakelingfe885462016-09-22 10:24:38 +01007269}
7270
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007271void ParallelMoveResolverARMVIXL::SpillScratch(int reg) {
7272 __ Push(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007273}
7274
Nicolas Geoffray13a797b2017-03-15 16:41:31 +00007275void ParallelMoveResolverARMVIXL::RestoreScratch(int reg) {
7276 __ Pop(vixl32::Register(reg));
Scott Wakelingfe885462016-09-22 10:24:38 +01007277}
7278
Artem Serov02d37832016-10-25 15:25:33 +01007279HLoadClass::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadClassKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007280 HLoadClass::LoadKind desired_class_load_kind) {
7281 switch (desired_class_load_kind) {
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007282 case HLoadClass::LoadKind::kInvalid:
7283 LOG(FATAL) << "UNREACHABLE";
7284 UNREACHABLE();
Artem Serovd4cc5b22016-11-04 11:19:09 +00007285 case HLoadClass::LoadKind::kReferrersClass:
7286 break;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007287 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007288 case HLoadClass::LoadKind::kBootImageRelRo:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007289 case HLoadClass::LoadKind::kBssEntry:
Vladimir Marko695348f2020-05-19 14:42:02 +01007290 DCHECK(!GetCompilerOptions().IsJitCompiler());
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007291 break;
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007292 case HLoadClass::LoadKind::kJitBootImageAddress:
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007293 case HLoadClass::LoadKind::kJitTableAddress:
Vladimir Marko695348f2020-05-19 14:42:02 +01007294 DCHECK(GetCompilerOptions().IsJitCompiler());
Artem Serovc5fcb442016-12-02 19:19:58 +00007295 break;
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007296 case HLoadClass::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007297 break;
7298 }
7299 return desired_class_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007300}
7301
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007302void LocationsBuilderARMVIXL::VisitLoadClass(HLoadClass* cls) {
Vladimir Marko41559982017-01-06 14:04:23 +00007303 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007304 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007305 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Vladimir Marko41559982017-01-06 14:04:23 +00007306 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007307 cls,
7308 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko41559982017-01-06 14:04:23 +00007309 LocationFrom(r0));
Vladimir Markoea4c1262017-02-06 19:59:33 +00007310 DCHECK(calling_convention.GetRegisterAt(0).Is(r0));
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007311 return;
7312 }
Vladimir Marko41559982017-01-06 14:04:23 +00007313 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelingfe885462016-09-22 10:24:38 +01007314
Artem Serovd4cc5b22016-11-04 11:19:09 +00007315 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
7316 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007317 ? LocationSummary::kCallOnSlowPath
7318 : LocationSummary::kNoCall;
Vladimir Markoca6fff82017-10-03 14:49:14 +01007319 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007320 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00007321 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Artem Serovd4cc5b22016-11-04 11:19:09 +00007322 }
7323
Vladimir Marko41559982017-01-06 14:04:23 +00007324 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007325 locations->SetInAt(0, Location::RequiresRegister());
7326 }
7327 locations->SetOut(Location::RequiresRegister());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007328 if (load_kind == HLoadClass::LoadKind::kBssEntry) {
7329 if (!kUseReadBarrier || kUseBakerReadBarrier) {
7330 // Rely on the type resolution or initialization and marking to save everything we need.
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007331 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Vladimir Markoea4c1262017-02-06 19:59:33 +00007332 } else {
7333 // For non-Baker read barrier we have a temp-clobbering call.
7334 }
7335 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007336}
7337
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007338// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7339// move.
7340void InstructionCodeGeneratorARMVIXL::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
Vladimir Marko41559982017-01-06 14:04:23 +00007341 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007342 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Marko41559982017-01-06 14:04:23 +00007343 codegen_->GenerateLoadClassRuntimeCall(cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007344 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 14);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007345 return;
7346 }
Vladimir Marko41559982017-01-06 14:04:23 +00007347 DCHECK(!cls->NeedsAccessCheck());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007348
Vladimir Marko41559982017-01-06 14:04:23 +00007349 LocationSummary* locations = cls->GetLocations();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007350 Location out_loc = locations->Out();
7351 vixl32::Register out = OutputRegister(cls);
7352
Artem Serovd4cc5b22016-11-04 11:19:09 +00007353 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
7354 ? kWithoutReadBarrier
7355 : kCompilerReadBarrierOption;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007356 bool generate_null_check = false;
Vladimir Marko41559982017-01-06 14:04:23 +00007357 switch (load_kind) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007358 case HLoadClass::LoadKind::kReferrersClass: {
7359 DCHECK(!cls->CanCallRuntime());
7360 DCHECK(!cls->MustGenerateClinitCheck());
7361 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
7362 vixl32::Register current_method = InputRegisterAt(cls, 0);
Vladimir Markoca1e0382018-04-11 09:58:41 +00007363 codegen_->GenerateGcRootFieldLoad(cls,
7364 out_loc,
7365 current_method,
7366 ArtMethod::DeclaringClassOffset().Int32Value(),
7367 read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007368 break;
7369 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007370 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01007371 DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
7372 codegen_->GetCompilerOptions().IsBootImageExtension());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007373 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7374 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007375 codegen_->NewBootImageTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007376 codegen_->EmitMovwMovtPlaceholder(labels, out);
7377 break;
7378 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007379 case HLoadClass::LoadKind::kBootImageRelRo: {
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007380 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7381 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007382 codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(cls));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007383 codegen_->EmitMovwMovtPlaceholder(labels, out);
Andreas Gampe3db70682018-12-26 15:12:03 -08007384 __ Ldr(out, MemOperand(out, /* offset= */ 0));
Vladimir Marko94ec2db2017-09-06 17:21:03 +01007385 break;
7386 }
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007387 case HLoadClass::LoadKind::kBssEntry: {
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007388 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko1998cd02017-01-13 13:02:58 +00007389 codegen_->NewTypeBssEntryPatch(cls->GetDexFile(), cls->GetTypeIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007390 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01007391 // All aligned loads are implicitly atomic consume operations on ARM.
Andreas Gampe3db70682018-12-26 15:12:03 -08007392 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset= */ 0, read_barrier_option);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00007393 generate_null_check = true;
7394 break;
7395 }
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007396 case HLoadClass::LoadKind::kJitBootImageAddress: {
7397 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
7398 uint32_t address = reinterpret_cast32<uint32_t>(cls->GetClass().Get());
7399 DCHECK_NE(address, 0u);
7400 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
7401 break;
7402 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00007403 case HLoadClass::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007404 __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
7405 cls->GetTypeIndex(),
Nicolas Geoffray5247c082017-01-13 14:17:29 +00007406 cls->GetClass()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007407 // /* GcRoot<mirror::Class> */ out = *out
Andreas Gampe3db70682018-12-26 15:12:03 -08007408 codegen_->GenerateGcRootFieldLoad(cls, out_loc, out, /* offset= */ 0, read_barrier_option);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007409 break;
7410 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007411 case HLoadClass::LoadKind::kRuntimeCall:
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00007412 case HLoadClass::LoadKind::kInvalid:
Vladimir Marko41559982017-01-06 14:04:23 +00007413 LOG(FATAL) << "UNREACHABLE";
7414 UNREACHABLE();
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007415 }
7416
7417 if (generate_null_check || cls->MustGenerateClinitCheck()) {
7418 DCHECK(cls->CanCallRuntime());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007419 LoadClassSlowPathARMVIXL* slow_path =
Vladimir Markoa9f303c2018-07-20 16:43:56 +01007420 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(cls, cls);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007421 codegen_->AddSlowPath(slow_path);
7422 if (generate_null_check) {
xueliang.zhongf51bc622016-11-04 09:23:32 +00007423 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007424 }
7425 if (cls->MustGenerateClinitCheck()) {
7426 GenerateClassInitializationCheck(slow_path, out);
7427 } else {
7428 __ Bind(slow_path->GetExitLabel());
7429 }
Andreas Gampe3db70682018-12-26 15:12:03 -08007430 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 15);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01007431 }
7432}
7433
Orion Hodsondbaa5c72018-05-10 08:22:46 +01007434void LocationsBuilderARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7435 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7436 Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7437 CodeGenerator::CreateLoadMethodHandleRuntimeCallLocationSummary(load, location, location);
7438}
7439
7440void InstructionCodeGeneratorARMVIXL::VisitLoadMethodHandle(HLoadMethodHandle* load) {
7441 codegen_->GenerateLoadMethodHandleRuntimeCall(load);
7442}
7443
Orion Hodson18259d72018-04-12 11:18:23 +01007444void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7445 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7446 Location location = LocationFrom(calling_convention.GetRegisterAt(0));
7447 CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
7448}
7449
7450void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
7451 codegen_->GenerateLoadMethodTypeRuntimeCall(load);
7452}
7453
Artem Serov02d37832016-10-25 15:25:33 +01007454void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7455 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007456 new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
Artem Serov02d37832016-10-25 15:25:33 +01007457 locations->SetInAt(0, Location::RequiresRegister());
7458 if (check->HasUses()) {
7459 locations->SetOut(Location::SameAsFirstInput());
7460 }
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007461 // Rely on the type initialization to save everything we need.
7462 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Artem Serov02d37832016-10-25 15:25:33 +01007463}
7464
7465void InstructionCodeGeneratorARMVIXL::VisitClinitCheck(HClinitCheck* check) {
7466 // We assume the class is not null.
7467 LoadClassSlowPathARMVIXL* slow_path =
Vladimir Markoa9f303c2018-07-20 16:43:56 +01007468 new (codegen_->GetScopedAllocator()) LoadClassSlowPathARMVIXL(check->GetLoadClass(), check);
Artem Serov02d37832016-10-25 15:25:33 +01007469 codegen_->AddSlowPath(slow_path);
7470 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
7471}
7472
7473void InstructionCodeGeneratorARMVIXL::GenerateClassInitializationCheck(
7474 LoadClassSlowPathARMVIXL* slow_path, vixl32::Register class_reg) {
7475 UseScratchRegisterScope temps(GetVIXLAssembler());
7476 vixl32::Register temp = temps.Acquire();
Vladimir Markodc682aa2018-01-04 18:42:57 +00007477 constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
Vladimir Markobf121912019-06-04 13:49:05 +01007478 constexpr uint32_t shifted_visibly_initialized_value =
7479 enum_cast<uint32_t>(ClassStatus::kVisiblyInitialized) << status_lsb_position;
Vladimir Markodc682aa2018-01-04 18:42:57 +00007480
Vladimir Markobf121912019-06-04 13:49:05 +01007481 const size_t status_offset = mirror::Class::StatusOffset().SizeValue();
7482 GetAssembler()->LoadFromOffset(kLoadWord, temp, class_reg, status_offset);
7483 __ Cmp(temp, shifted_visibly_initialized_value);
Vladimir Marko2c64a832018-01-04 11:31:56 +00007484 __ B(lo, slow_path->GetEntryLabel());
Artem Serov02d37832016-10-25 15:25:33 +01007485 __ Bind(slow_path->GetExitLabel());
7486}
7487
Vladimir Marko175e7862018-03-27 09:03:13 +00007488void InstructionCodeGeneratorARMVIXL::GenerateBitstringTypeCheckCompare(
7489 HTypeCheckInstruction* check,
7490 vixl32::Register temp,
7491 vixl32::FlagsUpdate flags_update) {
7492 uint32_t path_to_root = check->GetBitstringPathToRoot();
7493 uint32_t mask = check->GetBitstringMask();
7494 DCHECK(IsPowerOfTwo(mask + 1));
7495 size_t mask_bits = WhichPowerOf2(mask + 1);
7496
7497 // Note that HInstanceOf shall check for zero value in `temp` but HCheckCast needs
7498 // the Z flag for BNE. This is indicated by the `flags_update` parameter.
7499 if (mask_bits == 16u) {
7500 // Load only the bitstring part of the status word.
7501 __ Ldrh(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7502 // Check if the bitstring bits are equal to `path_to_root`.
7503 if (flags_update == SetFlags) {
7504 __ Cmp(temp, path_to_root);
7505 } else {
7506 __ Sub(temp, temp, path_to_root);
7507 }
7508 } else {
7509 // /* uint32_t */ temp = temp->status_
7510 __ Ldr(temp, MemOperand(temp, mirror::Class::StatusOffset().Int32Value()));
7511 if (GetAssembler()->ShifterOperandCanHold(SUB, path_to_root)) {
7512 // Compare the bitstring bits using SUB.
7513 __ Sub(temp, temp, path_to_root);
7514 // Shift out bits that do not contribute to the comparison.
7515 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7516 } else if (IsUint<16>(path_to_root)) {
7517 if (temp.IsLow()) {
7518 // Note: Optimized for size but contains one more dependent instruction than necessary.
7519 // MOVW+SUB(register) would be 8 bytes unless we find a low-reg temporary but the
7520 // macro assembler would use the high reg IP for the constant by default.
7521 // Compare the bitstring bits using SUB.
7522 __ Sub(temp, temp, path_to_root & 0x00ffu); // 16-bit SUB (immediate) T2
7523 __ Sub(temp, temp, path_to_root & 0xff00u); // 32-bit SUB (immediate) T3
7524 // Shift out bits that do not contribute to the comparison.
7525 __ Lsl(flags_update, temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7526 } else {
7527 // Extract the bitstring bits.
7528 __ Ubfx(temp, temp, 0, mask_bits);
7529 // Check if the bitstring bits are equal to `path_to_root`.
7530 if (flags_update == SetFlags) {
7531 __ Cmp(temp, path_to_root);
7532 } else {
7533 __ Sub(temp, temp, path_to_root);
7534 }
7535 }
7536 } else {
7537 // Shift out bits that do not contribute to the comparison.
7538 __ Lsl(temp, temp, dchecked_integral_cast<uint32_t>(32u - mask_bits));
7539 // Check if the shifted bitstring bits are equal to `path_to_root << (32u - mask_bits)`.
7540 if (flags_update == SetFlags) {
7541 __ Cmp(temp, path_to_root << (32u - mask_bits));
7542 } else {
7543 __ Sub(temp, temp, path_to_root << (32u - mask_bits));
7544 }
7545 }
7546 }
7547}
7548
Artem Serov02d37832016-10-25 15:25:33 +01007549HLoadString::LoadKind CodeGeneratorARMVIXL::GetSupportedLoadStringKind(
Artem Serovd4cc5b22016-11-04 11:19:09 +00007550 HLoadString::LoadKind desired_string_load_kind) {
7551 switch (desired_string_load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007552 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007553 case HLoadString::LoadKind::kBootImageRelRo:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007554 case HLoadString::LoadKind::kBssEntry:
Vladimir Marko695348f2020-05-19 14:42:02 +01007555 DCHECK(!GetCompilerOptions().IsJitCompiler());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007556 break;
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007557 case HLoadString::LoadKind::kJitBootImageAddress:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007558 case HLoadString::LoadKind::kJitTableAddress:
Vladimir Marko695348f2020-05-19 14:42:02 +01007559 DCHECK(GetCompilerOptions().IsJitCompiler());
Artem Serovc5fcb442016-12-02 19:19:58 +00007560 break;
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007561 case HLoadString::LoadKind::kRuntimeCall:
Artem Serovd4cc5b22016-11-04 11:19:09 +00007562 break;
7563 }
7564 return desired_string_load_kind;
Artem Serov02d37832016-10-25 15:25:33 +01007565}
7566
7567void LocationsBuilderARMVIXL::VisitLoadString(HLoadString* load) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007568 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Vladimir Markoca6fff82017-10-03 14:49:14 +01007569 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
Artem Serov02d37832016-10-25 15:25:33 +01007570 HLoadString::LoadKind load_kind = load->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007571 if (load_kind == HLoadString::LoadKind::kRuntimeCall) {
Artem Serov02d37832016-10-25 15:25:33 +01007572 locations->SetOut(LocationFrom(r0));
7573 } else {
7574 locations->SetOut(Location::RequiresRegister());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007575 if (load_kind == HLoadString::LoadKind::kBssEntry) {
7576 if (!kUseReadBarrier || kUseBakerReadBarrier) {
Vladimir Markoea4c1262017-02-06 19:59:33 +00007577 // Rely on the pResolveString and marking to save everything we need, including temps.
Vladimir Marko3232dbb2018-07-25 15:42:46 +01007578 locations->SetCustomSlowPathCallerSaves(OneRegInReferenceOutSaveEverythingCallerSaves());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007579 } else {
7580 // For non-Baker read barrier we have a temp-clobbering call.
7581 }
7582 }
Artem Serov02d37832016-10-25 15:25:33 +01007583 }
7584}
7585
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007586// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
7587// move.
7588void InstructionCodeGeneratorARMVIXL::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007589 LocationSummary* locations = load->GetLocations();
7590 Location out_loc = locations->Out();
7591 vixl32::Register out = OutputRegister(load);
7592 HLoadString::LoadKind load_kind = load->GetLoadKind();
7593
7594 switch (load_kind) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007595 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01007596 DCHECK(codegen_->GetCompilerOptions().IsBootImage() ||
7597 codegen_->GetCompilerOptions().IsBootImageExtension());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007598 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00007599 codegen_->NewBootImageStringPatch(load->GetDexFile(), load->GetStringIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00007600 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007601 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007602 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007603 case HLoadString::LoadKind::kBootImageRelRo: {
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007604 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
7605 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00007606 codegen_->NewBootImageRelRoPatch(codegen_->GetBootImageOffset(load));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007607 codegen_->EmitMovwMovtPlaceholder(labels, out);
Andreas Gampe3db70682018-12-26 15:12:03 -08007608 __ Ldr(out, MemOperand(out, /* offset= */ 0));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007609 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007610 }
7611 case HLoadString::LoadKind::kBssEntry: {
Artem Serovd4cc5b22016-11-04 11:19:09 +00007612 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01007613 codegen_->NewStringBssEntryPatch(load->GetDexFile(), load->GetStringIndex());
Vladimir Markof3c52b42017-11-17 17:32:12 +00007614 codegen_->EmitMovwMovtPlaceholder(labels, out);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01007615 // All aligned loads are implicitly atomic consume operations on ARM.
Vladimir Markoca1e0382018-04-11 09:58:41 +00007616 codegen_->GenerateGcRootFieldLoad(
Andreas Gampe3db70682018-12-26 15:12:03 -08007617 load, out_loc, out, /* offset= */ 0, kCompilerReadBarrierOption);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007618 LoadStringSlowPathARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01007619 new (codegen_->GetScopedAllocator()) LoadStringSlowPathARMVIXL(load);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007620 codegen_->AddSlowPath(slow_path);
7621 __ CompareAndBranchIfZero(out, slow_path->GetEntryLabel());
7622 __ Bind(slow_path->GetExitLabel());
Andreas Gampe3db70682018-12-26 15:12:03 -08007623 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 16);
Artem Serovd4cc5b22016-11-04 11:19:09 +00007624 return;
7625 }
Vladimir Marko8e524ad2018-07-13 10:27:43 +01007626 case HLoadString::LoadKind::kJitBootImageAddress: {
7627 uint32_t address = reinterpret_cast32<uint32_t>(load->GetString().Get());
7628 DCHECK_NE(address, 0u);
7629 __ Ldr(out, codegen_->DeduplicateBootImageAddressLiteral(address));
7630 return;
7631 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00007632 case HLoadString::LoadKind::kJitTableAddress: {
Artem Serovc5fcb442016-12-02 19:19:58 +00007633 __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00007634 load->GetStringIndex(),
7635 load->GetString()));
Artem Serovc5fcb442016-12-02 19:19:58 +00007636 // /* GcRoot<mirror::String> */ out = *out
Vladimir Markoca1e0382018-04-11 09:58:41 +00007637 codegen_->GenerateGcRootFieldLoad(
Andreas Gampe3db70682018-12-26 15:12:03 -08007638 load, out_loc, out, /* offset= */ 0, kCompilerReadBarrierOption);
Artem Serovc5fcb442016-12-02 19:19:58 +00007639 return;
Artem Serovd4cc5b22016-11-04 11:19:09 +00007640 }
7641 default:
7642 break;
7643 }
Artem Serov02d37832016-10-25 15:25:33 +01007644
7645 // TODO: Re-add the compiler code to do string dex cache lookup again.
Vladimir Marko847e6ce2017-06-02 13:55:07 +01007646 DCHECK_EQ(load->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall);
Artem Serov02d37832016-10-25 15:25:33 +01007647 InvokeRuntimeCallingConventionARMVIXL calling_convention;
Andreas Gampe8a0128a2016-11-28 07:38:35 -08007648 __ Mov(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
Artem Serov02d37832016-10-25 15:25:33 +01007649 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
7650 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Andreas Gampe3db70682018-12-26 15:12:03 -08007651 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 17);
Artem Serov02d37832016-10-25 15:25:33 +01007652}
7653
7654static int32_t GetExceptionTlsOffset() {
7655 return Thread::ExceptionOffset<kArmPointerSize>().Int32Value();
7656}
7657
7658void LocationsBuilderARMVIXL::VisitLoadException(HLoadException* load) {
7659 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01007660 new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007661 locations->SetOut(Location::RequiresRegister());
7662}
7663
7664void InstructionCodeGeneratorARMVIXL::VisitLoadException(HLoadException* load) {
7665 vixl32::Register out = OutputRegister(load);
7666 GetAssembler()->LoadFromOffset(kLoadWord, out, tr, GetExceptionTlsOffset());
7667}
7668
7669
7670void LocationsBuilderARMVIXL::VisitClearException(HClearException* clear) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007671 new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
Artem Serov02d37832016-10-25 15:25:33 +01007672}
7673
7674void InstructionCodeGeneratorARMVIXL::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
7675 UseScratchRegisterScope temps(GetVIXLAssembler());
7676 vixl32::Register temp = temps.Acquire();
7677 __ Mov(temp, 0);
7678 GetAssembler()->StoreToOffset(kStoreWord, temp, tr, GetExceptionTlsOffset());
7679}
7680
7681void LocationsBuilderARMVIXL::VisitThrow(HThrow* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01007682 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
7683 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov02d37832016-10-25 15:25:33 +01007684 InvokeRuntimeCallingConventionARMVIXL calling_convention;
7685 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
7686}
7687
7688void InstructionCodeGeneratorARMVIXL::VisitThrow(HThrow* instruction) {
7689 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
7690 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
7691}
7692
Artem Serov657022c2016-11-23 14:19:38 +00007693// Temp is used for read barrier.
7694static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
7695 if (kEmitCompilerReadBarrier &&
7696 (kUseBakerReadBarrier ||
7697 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
7698 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
7699 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
7700 return 1;
7701 }
7702 return 0;
Anton Kirilove28d9ae2016-10-25 18:17:23 +01007703}
7704
Artem Serov657022c2016-11-23 14:19:38 +00007705// Interface case has 3 temps, one for holding the number of interfaces, one for the current
7706// interface pointer, one for loading the current interface.
7707// The other checks have one temp for loading the object's class.
7708static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
7709 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
7710 return 3;
7711 }
7712 return 1 + NumberOfInstanceOfTemps(type_check_kind);
7713}
Artem Serovcfbe9132016-10-14 15:58:56 +01007714
7715void LocationsBuilderARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7716 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
7717 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7718 bool baker_read_barrier_slow_path = false;
7719 switch (type_check_kind) {
7720 case TypeCheckKind::kExactCheck:
7721 case TypeCheckKind::kAbstractClassCheck:
7722 case TypeCheckKind::kClassHierarchyCheck:
Vladimir Marko87584542017-12-12 17:47:52 +00007723 case TypeCheckKind::kArrayObjectCheck: {
7724 bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
7725 call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
7726 baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
Artem Serovcfbe9132016-10-14 15:58:56 +01007727 break;
Vladimir Marko87584542017-12-12 17:47:52 +00007728 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007729 case TypeCheckKind::kArrayCheck:
7730 case TypeCheckKind::kUnresolvedCheck:
7731 case TypeCheckKind::kInterfaceCheck:
7732 call_kind = LocationSummary::kCallOnSlowPath;
7733 break;
Vladimir Marko175e7862018-03-27 09:03:13 +00007734 case TypeCheckKind::kBitstringCheck:
7735 break;
Artem Serovcfbe9132016-10-14 15:58:56 +01007736 }
7737
Vladimir Markoca6fff82017-10-03 14:49:14 +01007738 LocationSummary* locations =
7739 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Artem Serovcfbe9132016-10-14 15:58:56 +01007740 if (baker_read_barrier_slow_path) {
7741 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
7742 }
7743 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00007744 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
7745 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
7746 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
7747 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
7748 } else {
7749 locations->SetInAt(1, Location::RequiresRegister());
7750 }
Artem Serovcfbe9132016-10-14 15:58:56 +01007751 // The "out" register is used as a temporary, so it overlaps with the inputs.
7752 // Note that TypeCheckSlowPathARM uses this register too.
7753 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Artem Serov657022c2016-11-23 14:19:38 +00007754 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Artem Serovcfbe9132016-10-14 15:58:56 +01007755}
7756
7757void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) {
7758 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
7759 LocationSummary* locations = instruction->GetLocations();
7760 Location obj_loc = locations->InAt(0);
7761 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00007762 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
7763 ? vixl32::Register()
7764 : InputRegisterAt(instruction, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007765 Location out_loc = locations->Out();
7766 vixl32::Register out = OutputRegister(instruction);
Artem Serov657022c2016-11-23 14:19:38 +00007767 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
7768 DCHECK_LE(num_temps, 1u);
7769 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Artem Serovcfbe9132016-10-14 15:58:56 +01007770 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
7771 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
7772 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
7773 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007774 vixl32::Label done;
7775 vixl32::Label* const final_label = codegen_->GetFinalLabel(instruction, &done);
Artem Serovcfbe9132016-10-14 15:58:56 +01007776 SlowPathCodeARMVIXL* slow_path = nullptr;
7777
7778 // Return 0 if `obj` is null.
7779 // avoid null check if we know obj is not null.
7780 if (instruction->MustDoNullCheck()) {
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007781 DCHECK(!out.Is(obj));
7782 __ Mov(out, 0);
Andreas Gampe3db70682018-12-26 15:12:03 -08007783 __ CompareAndBranchIfZero(obj, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007784 }
7785
Artem Serovcfbe9132016-10-14 15:58:56 +01007786 switch (type_check_kind) {
7787 case TypeCheckKind::kExactCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007788 ReadBarrierOption read_barrier_option =
7789 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007790 // /* HeapReference<Class> */ out = obj->klass_
7791 GenerateReferenceLoadTwoRegisters(instruction,
7792 out_loc,
7793 obj_loc,
7794 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007795 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007796 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007797 // Classes must be equal for the instanceof to succeed.
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007798 __ Cmp(out, cls);
7799 // We speculatively set the result to false without changing the condition
7800 // flags, which allows us to avoid some branching later.
7801 __ Mov(LeaveFlags, out, 0);
7802
7803 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7804 // we check that the output is in a low register, so that a 16-bit MOV
7805 // encoding can be used.
7806 if (out.IsLow()) {
7807 // We use the scope because of the IT block that follows.
7808 ExactAssemblyScope guard(GetVIXLAssembler(),
7809 2 * vixl32::k16BitT32InstructionSizeInBytes,
7810 CodeBufferCheckScope::kExactSize);
7811
7812 __ it(eq);
7813 __ mov(eq, out, 1);
7814 } else {
Andreas Gampe3db70682018-12-26 15:12:03 -08007815 __ B(ne, final_label, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007816 __ Mov(out, 1);
7817 }
7818
Artem Serovcfbe9132016-10-14 15:58:56 +01007819 break;
7820 }
7821
7822 case TypeCheckKind::kAbstractClassCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007823 ReadBarrierOption read_barrier_option =
7824 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007825 // /* HeapReference<Class> */ out = obj->klass_
7826 GenerateReferenceLoadTwoRegisters(instruction,
7827 out_loc,
7828 obj_loc,
7829 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007830 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007831 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007832 // If the class is abstract, we eagerly fetch the super class of the
7833 // object to avoid doing a comparison we know will fail.
7834 vixl32::Label loop;
7835 __ Bind(&loop);
7836 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007837 GenerateReferenceLoadOneRegister(instruction,
7838 out_loc,
7839 super_offset,
7840 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007841 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007842 // If `out` is null, we use it for the result, and jump to the final label.
Andreas Gampe3db70682018-12-26 15:12:03 -08007843 __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007844 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007845 __ B(ne, &loop, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007846 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007847 break;
7848 }
7849
7850 case TypeCheckKind::kClassHierarchyCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007851 ReadBarrierOption read_barrier_option =
7852 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007853 // /* HeapReference<Class> */ out = obj->klass_
7854 GenerateReferenceLoadTwoRegisters(instruction,
7855 out_loc,
7856 obj_loc,
7857 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007858 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007859 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007860 // Walk over the class hierarchy to find a match.
7861 vixl32::Label loop, success;
7862 __ Bind(&loop);
7863 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007864 __ B(eq, &success, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007865 // /* HeapReference<Class> */ out = out->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00007866 GenerateReferenceLoadOneRegister(instruction,
7867 out_loc,
7868 super_offset,
7869 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007870 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007871 // This is essentially a null check, but it sets the condition flags to the
7872 // proper value for the code that follows the loop, i.e. not `eq`.
7873 __ Cmp(out, 1);
Andreas Gampe3db70682018-12-26 15:12:03 -08007874 __ B(hs, &loop, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007875
7876 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7877 // we check that the output is in a low register, so that a 16-bit MOV
7878 // encoding can be used.
7879 if (out.IsLow()) {
7880 // If `out` is null, we use it for the result, and the condition flags
7881 // have already been set to `ne`, so the IT block that comes afterwards
7882 // (and which handles the successful case) turns into a NOP (instead of
7883 // overwriting `out`).
7884 __ Bind(&success);
7885
7886 // We use the scope because of the IT block that follows.
7887 ExactAssemblyScope guard(GetVIXLAssembler(),
7888 2 * vixl32::k16BitT32InstructionSizeInBytes,
7889 CodeBufferCheckScope::kExactSize);
7890
7891 // There is only one branch to the `success` label (which is bound to this
7892 // IT block), and it has the same condition, `eq`, so in that case the MOV
7893 // is executed.
7894 __ it(eq);
7895 __ mov(eq, out, 1);
7896 } else {
7897 // If `out` is null, we use it for the result, and jump to the final label.
Anton Kirilov6f644202017-02-27 18:29:45 +00007898 __ B(final_label);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007899 __ Bind(&success);
7900 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007901 }
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007902
Artem Serovcfbe9132016-10-14 15:58:56 +01007903 break;
7904 }
7905
7906 case TypeCheckKind::kArrayObjectCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00007907 ReadBarrierOption read_barrier_option =
7908 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier6beced42016-11-15 15:51:31 -08007909 // /* HeapReference<Class> */ out = obj->klass_
7910 GenerateReferenceLoadTwoRegisters(instruction,
7911 out_loc,
7912 obj_loc,
7913 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007914 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007915 read_barrier_option);
Artem Serovcfbe9132016-10-14 15:58:56 +01007916 // Do an exact check.
7917 vixl32::Label exact_check;
7918 __ Cmp(out, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08007919 __ B(eq, &exact_check, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007920 // Otherwise, we need to check that the object's class is a non-primitive array.
7921 // /* HeapReference<Class> */ out = out->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00007922 GenerateReferenceLoadOneRegister(instruction,
7923 out_loc,
7924 component_offset,
7925 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00007926 read_barrier_option);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007927 // If `out` is null, we use it for the result, and jump to the final label.
Andreas Gampe3db70682018-12-26 15:12:03 -08007928 __ CompareAndBranchIfZero(out, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007929 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset);
7930 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007931 __ Cmp(out, 0);
7932 // We speculatively set the result to false without changing the condition
7933 // flags, which allows us to avoid some branching later.
7934 __ Mov(LeaveFlags, out, 0);
7935
7936 // Since IT blocks longer than a 16-bit instruction are deprecated by ARMv8,
7937 // we check that the output is in a low register, so that a 16-bit MOV
7938 // encoding can be used.
7939 if (out.IsLow()) {
7940 __ Bind(&exact_check);
7941
7942 // We use the scope because of the IT block that follows.
7943 ExactAssemblyScope guard(GetVIXLAssembler(),
7944 2 * vixl32::k16BitT32InstructionSizeInBytes,
7945 CodeBufferCheckScope::kExactSize);
7946
7947 __ it(eq);
7948 __ mov(eq, out, 1);
7949 } else {
Andreas Gampe3db70682018-12-26 15:12:03 -08007950 __ B(ne, final_label, /* is_far_target= */ false);
Anton Kirilov1e7bb5a2017-03-17 12:30:44 +00007951 __ Bind(&exact_check);
7952 __ Mov(out, 1);
7953 }
7954
Artem Serovcfbe9132016-10-14 15:58:56 +01007955 break;
7956 }
7957
7958 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00007959 // No read barrier since the slow path will retry upon failure.
Mathieu Chartier6beced42016-11-15 15:51:31 -08007960 // /* HeapReference<Class> */ out = obj->klass_
7961 GenerateReferenceLoadTwoRegisters(instruction,
7962 out_loc,
7963 obj_loc,
7964 class_offset,
Artem Serov657022c2016-11-23 14:19:38 +00007965 maybe_temp_loc,
7966 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01007967 __ Cmp(out, cls);
7968 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007969 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
Andreas Gampe3db70682018-12-26 15:12:03 -08007970 instruction, /* is_fatal= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01007971 codegen_->AddSlowPath(slow_path);
7972 __ B(ne, slow_path->GetEntryLabel());
7973 __ Mov(out, 1);
Artem Serovcfbe9132016-10-14 15:58:56 +01007974 break;
7975 }
7976
7977 case TypeCheckKind::kUnresolvedCheck:
7978 case TypeCheckKind::kInterfaceCheck: {
7979 // Note that we indeed only call on slow path, but we always go
7980 // into the slow path for the unresolved and interface check
7981 // cases.
7982 //
7983 // We cannot directly call the InstanceofNonTrivial runtime
7984 // entry point without resorting to a type checking slow path
7985 // here (i.e. by calling InvokeRuntime directly), as it would
7986 // require to assign fixed registers for the inputs of this
7987 // HInstanceOf instruction (following the runtime calling
7988 // convention), which might be cluttered by the potential first
7989 // read barrier emission at the beginning of this method.
7990 //
7991 // TODO: Introduce a new runtime entry point taking the object
7992 // to test (instead of its class) as argument, and let it deal
7993 // with the read barrier issues. This will let us refactor this
7994 // case of the `switch` code as it was previously (with a direct
7995 // call to the runtime not using a type checking slow path).
7996 // This should also be beneficial for the other cases above.
7997 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01007998 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
Andreas Gampe3db70682018-12-26 15:12:03 -08007999 instruction, /* is_fatal= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008000 codegen_->AddSlowPath(slow_path);
8001 __ B(slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008002 break;
8003 }
Vladimir Marko175e7862018-03-27 09:03:13 +00008004
8005 case TypeCheckKind::kBitstringCheck: {
8006 // /* HeapReference<Class> */ temp = obj->klass_
8007 GenerateReferenceLoadTwoRegisters(instruction,
8008 out_loc,
8009 obj_loc,
8010 class_offset,
8011 maybe_temp_loc,
8012 kWithoutReadBarrier);
8013
8014 GenerateBitstringTypeCheckCompare(instruction, out, DontCare);
8015 // If `out` is a low reg and we would have another low reg temp, we could
8016 // optimize this as RSBS+ADC, see GenerateConditionWithZero().
8017 //
8018 // Also, in some cases when `out` is a low reg and we're loading a constant to IP
8019 // it would make sense to use CMP+MOV+IT+MOV instead of SUB+CLZ+LSR as the code size
8020 // would be the same and we would have fewer direct data dependencies.
8021 codegen_->GenerateConditionWithZero(kCondEQ, out, out); // CLZ+LSR
8022 break;
8023 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008024 }
8025
Artem Serovcfbe9132016-10-14 15:58:56 +01008026 if (done.IsReferenced()) {
8027 __ Bind(&done);
8028 }
8029
8030 if (slow_path != nullptr) {
8031 __ Bind(slow_path->GetExitLabel());
8032 }
8033}
8034
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008035void LocationsBuilderARMVIXL::VisitCheckCast(HCheckCast* instruction) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008036 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Vladimir Marko87584542017-12-12 17:47:52 +00008037 LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
Vladimir Markoca6fff82017-10-03 14:49:14 +01008038 LocationSummary* locations =
8039 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008040 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko175e7862018-03-27 09:03:13 +00008041 if (type_check_kind == TypeCheckKind::kBitstringCheck) {
8042 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
8043 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
8044 locations->SetInAt(3, Location::ConstantLocation(instruction->InputAt(3)->AsConstant()));
8045 } else {
8046 locations->SetInAt(1, Location::RequiresRegister());
8047 }
Artem Serov657022c2016-11-23 14:19:38 +00008048 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008049}
8050
8051void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) {
8052 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
8053 LocationSummary* locations = instruction->GetLocations();
8054 Location obj_loc = locations->InAt(0);
8055 vixl32::Register obj = InputRegisterAt(instruction, 0);
Vladimir Marko175e7862018-03-27 09:03:13 +00008056 vixl32::Register cls = (type_check_kind == TypeCheckKind::kBitstringCheck)
8057 ? vixl32::Register()
8058 : InputRegisterAt(instruction, 1);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008059 Location temp_loc = locations->GetTemp(0);
8060 vixl32::Register temp = RegisterFrom(temp_loc);
Artem Serov657022c2016-11-23 14:19:38 +00008061 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
8062 DCHECK_LE(num_temps, 3u);
8063 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
8064 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
8065 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
8066 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
8067 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
8068 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
8069 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
8070 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
8071 const uint32_t object_array_data_offset =
8072 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008073
Vladimir Marko87584542017-12-12 17:47:52 +00008074 bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008075 SlowPathCodeARMVIXL* type_check_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01008076 new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARMVIXL(
8077 instruction, is_type_check_slow_path_fatal);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008078 codegen_->AddSlowPath(type_check_slow_path);
8079
8080 vixl32::Label done;
Anton Kirilov6f644202017-02-27 18:29:45 +00008081 vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008082 // Avoid null check if we know obj is not null.
8083 if (instruction->MustDoNullCheck()) {
Andreas Gampe3db70682018-12-26 15:12:03 -08008084 __ CompareAndBranchIfZero(obj, final_label, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008085 }
8086
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008087 switch (type_check_kind) {
8088 case TypeCheckKind::kExactCheck:
8089 case TypeCheckKind::kArrayCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008090 // /* HeapReference<Class> */ temp = obj->klass_
8091 GenerateReferenceLoadTwoRegisters(instruction,
8092 temp_loc,
8093 obj_loc,
8094 class_offset,
8095 maybe_temp2_loc,
8096 kWithoutReadBarrier);
8097
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008098 __ Cmp(temp, cls);
8099 // Jump to slow path for throwing the exception or doing a
8100 // more involved array check.
8101 __ B(ne, type_check_slow_path->GetEntryLabel());
8102 break;
8103 }
8104
8105 case TypeCheckKind::kAbstractClassCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008106 // /* HeapReference<Class> */ temp = obj->klass_
8107 GenerateReferenceLoadTwoRegisters(instruction,
8108 temp_loc,
8109 obj_loc,
8110 class_offset,
8111 maybe_temp2_loc,
8112 kWithoutReadBarrier);
8113
Artem Serovcfbe9132016-10-14 15:58:56 +01008114 // If the class is abstract, we eagerly fetch the super class of the
8115 // object to avoid doing a comparison we know will fail.
8116 vixl32::Label loop;
8117 __ Bind(&loop);
8118 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00008119 GenerateReferenceLoadOneRegister(instruction,
8120 temp_loc,
8121 super_offset,
8122 maybe_temp2_loc,
8123 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008124
8125 // If the class reference currently in `temp` is null, jump to the slow path to throw the
8126 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008127 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008128
8129 // Otherwise, compare the classes.
8130 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08008131 __ B(ne, &loop, /* is_far_target= */ false);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008132 break;
8133 }
8134
8135 case TypeCheckKind::kClassHierarchyCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008136 // /* HeapReference<Class> */ temp = obj->klass_
8137 GenerateReferenceLoadTwoRegisters(instruction,
8138 temp_loc,
8139 obj_loc,
8140 class_offset,
8141 maybe_temp2_loc,
8142 kWithoutReadBarrier);
8143
Artem Serovcfbe9132016-10-14 15:58:56 +01008144 // Walk over the class hierarchy to find a match.
8145 vixl32::Label loop;
8146 __ Bind(&loop);
8147 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08008148 __ B(eq, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008149
8150 // /* HeapReference<Class> */ temp = temp->super_class_
Artem Serov657022c2016-11-23 14:19:38 +00008151 GenerateReferenceLoadOneRegister(instruction,
8152 temp_loc,
8153 super_offset,
8154 maybe_temp2_loc,
8155 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008156
8157 // If the class reference currently in `temp` is null, jump to the slow path to throw the
8158 // exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008159 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008160 // Otherwise, jump to the beginning of the loop.
8161 __ B(&loop);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008162 break;
8163 }
8164
Artem Serovcfbe9132016-10-14 15:58:56 +01008165 case TypeCheckKind::kArrayObjectCheck: {
Artem Serov657022c2016-11-23 14:19:38 +00008166 // /* HeapReference<Class> */ temp = obj->klass_
8167 GenerateReferenceLoadTwoRegisters(instruction,
8168 temp_loc,
8169 obj_loc,
8170 class_offset,
8171 maybe_temp2_loc,
8172 kWithoutReadBarrier);
8173
Artem Serovcfbe9132016-10-14 15:58:56 +01008174 // Do an exact check.
8175 __ Cmp(temp, cls);
Andreas Gampe3db70682018-12-26 15:12:03 -08008176 __ B(eq, final_label, /* is_far_target= */ false);
Artem Serovcfbe9132016-10-14 15:58:56 +01008177
8178 // Otherwise, we need to check that the object's class is a non-primitive array.
8179 // /* HeapReference<Class> */ temp = temp->component_type_
Artem Serov657022c2016-11-23 14:19:38 +00008180 GenerateReferenceLoadOneRegister(instruction,
8181 temp_loc,
8182 component_offset,
8183 maybe_temp2_loc,
8184 kWithoutReadBarrier);
Artem Serovcfbe9132016-10-14 15:58:56 +01008185 // If the component type is null, jump to the slow path to throw the exception.
xueliang.zhongf51bc622016-11-04 09:23:32 +00008186 __ CompareAndBranchIfZero(temp, type_check_slow_path->GetEntryLabel());
Artem Serovcfbe9132016-10-14 15:58:56 +01008187 // Otherwise,the object is indeed an array, jump to label `check_non_primitive_component_type`
8188 // to further check that this component type is not a primitive type.
8189 GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, temp, temp, primitive_offset);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008190 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
xueliang.zhongf51bc622016-11-04 09:23:32 +00008191 __ CompareAndBranchIfNonZero(temp, type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008192 break;
8193 }
8194
8195 case TypeCheckKind::kUnresolvedCheck:
Artem Serov657022c2016-11-23 14:19:38 +00008196 // We always go into the type check slow path for the unresolved check case.
Artem Serovcfbe9132016-10-14 15:58:56 +01008197 // We cannot directly call the CheckCast runtime entry point
8198 // without resorting to a type checking slow path here (i.e. by
8199 // calling InvokeRuntime directly), as it would require to
8200 // assign fixed registers for the inputs of this HInstanceOf
8201 // instruction (following the runtime calling convention), which
8202 // might be cluttered by the potential first read barrier
8203 // emission at the beginning of this method.
Artem Serov657022c2016-11-23 14:19:38 +00008204
Artem Serovcfbe9132016-10-14 15:58:56 +01008205 __ B(type_check_slow_path->GetEntryLabel());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008206 break;
Artem Serov657022c2016-11-23 14:19:38 +00008207
8208 case TypeCheckKind::kInterfaceCheck: {
8209 // Avoid read barriers to improve performance of the fast path. We can not get false
8210 // positives by doing this.
8211 // /* HeapReference<Class> */ temp = obj->klass_
8212 GenerateReferenceLoadTwoRegisters(instruction,
8213 temp_loc,
8214 obj_loc,
8215 class_offset,
8216 maybe_temp2_loc,
8217 kWithoutReadBarrier);
8218
8219 // /* HeapReference<Class> */ temp = temp->iftable_
8220 GenerateReferenceLoadTwoRegisters(instruction,
8221 temp_loc,
8222 temp_loc,
8223 iftable_offset,
8224 maybe_temp2_loc,
8225 kWithoutReadBarrier);
8226 // Iftable is never null.
8227 __ Ldr(RegisterFrom(maybe_temp2_loc), MemOperand(temp, array_length_offset));
8228 // Loop through the iftable and check if any class matches.
8229 vixl32::Label start_loop;
8230 __ Bind(&start_loop);
8231 __ CompareAndBranchIfZero(RegisterFrom(maybe_temp2_loc),
8232 type_check_slow_path->GetEntryLabel());
8233 __ Ldr(RegisterFrom(maybe_temp3_loc), MemOperand(temp, object_array_data_offset));
8234 GetAssembler()->MaybeUnpoisonHeapReference(RegisterFrom(maybe_temp3_loc));
8235 // Go to next interface.
8236 __ Add(temp, temp, Operand::From(2 * kHeapReferenceSize));
8237 __ Sub(RegisterFrom(maybe_temp2_loc), RegisterFrom(maybe_temp2_loc), 2);
8238 // Compare the classes and continue the loop if they do not match.
8239 __ Cmp(cls, RegisterFrom(maybe_temp3_loc));
Andreas Gampe3db70682018-12-26 15:12:03 -08008240 __ B(ne, &start_loop, /* is_far_target= */ false);
Artem Serov657022c2016-11-23 14:19:38 +00008241 break;
8242 }
Vladimir Marko175e7862018-03-27 09:03:13 +00008243
8244 case TypeCheckKind::kBitstringCheck: {
8245 // /* HeapReference<Class> */ temp = obj->klass_
8246 GenerateReferenceLoadTwoRegisters(instruction,
8247 temp_loc,
8248 obj_loc,
8249 class_offset,
8250 maybe_temp2_loc,
8251 kWithoutReadBarrier);
8252
8253 GenerateBitstringTypeCheckCompare(instruction, temp, SetFlags);
8254 __ B(ne, type_check_slow_path->GetEntryLabel());
8255 break;
8256 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008257 }
Anton Kirilov6f644202017-02-27 18:29:45 +00008258 if (done.IsReferenced()) {
8259 __ Bind(&done);
8260 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008261
8262 __ Bind(type_check_slow_path->GetExitLabel());
8263}
8264
Artem Serov551b28f2016-10-18 19:11:30 +01008265void LocationsBuilderARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01008266 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
8267 instruction, LocationSummary::kCallOnMainOnly);
Artem Serov551b28f2016-10-18 19:11:30 +01008268 InvokeRuntimeCallingConventionARMVIXL calling_convention;
8269 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
8270}
8271
8272void InstructionCodeGeneratorARMVIXL::VisitMonitorOperation(HMonitorOperation* instruction) {
8273 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
8274 instruction,
8275 instruction->GetDexPc());
8276 if (instruction->IsEnter()) {
8277 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
8278 } else {
8279 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
8280 }
Andreas Gampe3db70682018-12-26 15:12:03 -08008281 codegen_->MaybeGenerateMarkingRegisterCheck(/* code= */ 18);
Artem Serov551b28f2016-10-18 19:11:30 +01008282}
8283
Artem Serov02109dd2016-09-23 17:17:54 +01008284void LocationsBuilderARMVIXL::VisitAnd(HAnd* instruction) {
8285 HandleBitwiseOperation(instruction, AND);
8286}
8287
8288void LocationsBuilderARMVIXL::VisitOr(HOr* instruction) {
8289 HandleBitwiseOperation(instruction, ORR);
8290}
8291
8292void LocationsBuilderARMVIXL::VisitXor(HXor* instruction) {
8293 HandleBitwiseOperation(instruction, EOR);
8294}
8295
8296void LocationsBuilderARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction, Opcode opcode) {
8297 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008298 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008299 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8300 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008301 // Note: GVN reorders commutative operations to have the constant on the right hand side.
8302 locations->SetInAt(0, Location::RequiresRegister());
8303 locations->SetInAt(1, ArmEncodableConstantOrRegister(instruction->InputAt(1), opcode));
8304 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8305}
8306
8307void InstructionCodeGeneratorARMVIXL::VisitAnd(HAnd* instruction) {
8308 HandleBitwiseOperation(instruction);
8309}
8310
8311void InstructionCodeGeneratorARMVIXL::VisitOr(HOr* instruction) {
8312 HandleBitwiseOperation(instruction);
8313}
8314
8315void InstructionCodeGeneratorARMVIXL::VisitXor(HXor* instruction) {
8316 HandleBitwiseOperation(instruction);
8317}
8318
Artem Serov2bbc9532016-10-21 11:51:50 +01008319void LocationsBuilderARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8320 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008321 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008322 DCHECK(instruction->GetResultType() == DataType::Type::kInt32
8323 || instruction->GetResultType() == DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008324
8325 locations->SetInAt(0, Location::RequiresRegister());
8326 locations->SetInAt(1, Location::RequiresRegister());
8327 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
8328}
8329
8330void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) {
8331 LocationSummary* locations = instruction->GetLocations();
8332 Location first = locations->InAt(0);
8333 Location second = locations->InAt(1);
8334 Location out = locations->Out();
8335
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008336 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov2bbc9532016-10-21 11:51:50 +01008337 vixl32::Register first_reg = RegisterFrom(first);
8338 vixl32::Register second_reg = RegisterFrom(second);
8339 vixl32::Register out_reg = RegisterFrom(out);
8340
8341 switch (instruction->GetOpKind()) {
8342 case HInstruction::kAnd:
8343 __ Bic(out_reg, first_reg, second_reg);
8344 break;
8345 case HInstruction::kOr:
8346 __ Orn(out_reg, first_reg, second_reg);
8347 break;
8348 // There is no EON on arm.
8349 case HInstruction::kXor:
8350 default:
8351 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8352 UNREACHABLE();
8353 }
8354 return;
8355
8356 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008357 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov2bbc9532016-10-21 11:51:50 +01008358 vixl32::Register first_low = LowRegisterFrom(first);
8359 vixl32::Register first_high = HighRegisterFrom(first);
8360 vixl32::Register second_low = LowRegisterFrom(second);
8361 vixl32::Register second_high = HighRegisterFrom(second);
8362 vixl32::Register out_low = LowRegisterFrom(out);
8363 vixl32::Register out_high = HighRegisterFrom(out);
8364
8365 switch (instruction->GetOpKind()) {
8366 case HInstruction::kAnd:
8367 __ Bic(out_low, first_low, second_low);
8368 __ Bic(out_high, first_high, second_high);
8369 break;
8370 case HInstruction::kOr:
8371 __ Orn(out_low, first_low, second_low);
8372 __ Orn(out_high, first_high, second_high);
8373 break;
8374 // There is no EON on arm.
8375 case HInstruction::kXor:
8376 default:
8377 LOG(FATAL) << "Unexpected instruction " << instruction->DebugName();
8378 UNREACHABLE();
8379 }
8380 }
8381}
8382
Anton Kirilov74234da2017-01-13 14:42:47 +00008383void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp(
8384 HDataProcWithShifterOp* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008385 DCHECK(instruction->GetType() == DataType::Type::kInt32 ||
8386 instruction->GetType() == DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008387 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01008388 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008389 const bool overlap = instruction->GetType() == DataType::Type::kInt64 &&
Anton Kirilov74234da2017-01-13 14:42:47 +00008390 HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind());
8391
8392 locations->SetInAt(0, Location::RequiresRegister());
8393 locations->SetInAt(1, Location::RequiresRegister());
8394 locations->SetOut(Location::RequiresRegister(),
8395 overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap);
8396}
8397
8398void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp(
8399 HDataProcWithShifterOp* instruction) {
8400 const LocationSummary* const locations = instruction->GetLocations();
8401 const HInstruction::InstructionKind kind = instruction->GetInstrKind();
8402 const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
8403
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008404 if (instruction->GetType() == DataType::Type::kInt32) {
Anton Kirilov420ee302017-02-21 18:10:26 +00008405 const vixl32::Register first = InputRegisterAt(instruction, 0);
8406 const vixl32::Register output = OutputRegister(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008407 const vixl32::Register second = instruction->InputAt(1)->GetType() == DataType::Type::kInt64
Anton Kirilov74234da2017-01-13 14:42:47 +00008408 ? LowRegisterFrom(locations->InAt(1))
8409 : InputRegisterAt(instruction, 1);
8410
Anton Kirilov420ee302017-02-21 18:10:26 +00008411 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8412 DCHECK_EQ(kind, HInstruction::kAdd);
8413
8414 switch (op_kind) {
8415 case HDataProcWithShifterOp::kUXTB:
8416 __ Uxtab(output, first, second);
8417 break;
8418 case HDataProcWithShifterOp::kUXTH:
8419 __ Uxtah(output, first, second);
8420 break;
8421 case HDataProcWithShifterOp::kSXTB:
8422 __ Sxtab(output, first, second);
8423 break;
8424 case HDataProcWithShifterOp::kSXTH:
8425 __ Sxtah(output, first, second);
8426 break;
8427 default:
8428 LOG(FATAL) << "Unexpected operation kind: " << op_kind;
8429 UNREACHABLE();
8430 }
8431 } else {
8432 GenerateDataProcInstruction(kind,
8433 output,
8434 first,
8435 Operand(second,
8436 ShiftFromOpKind(op_kind),
8437 instruction->GetShiftAmount()),
8438 codegen_);
8439 }
Anton Kirilov74234da2017-01-13 14:42:47 +00008440 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008441 DCHECK_EQ(instruction->GetType(), DataType::Type::kInt64);
Anton Kirilov74234da2017-01-13 14:42:47 +00008442
8443 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
8444 const vixl32::Register second = InputRegisterAt(instruction, 1);
8445
8446 DCHECK(!LowRegisterFrom(locations->Out()).Is(second));
8447 GenerateDataProc(kind,
8448 locations->Out(),
8449 locations->InAt(0),
8450 second,
8451 Operand(second, ShiftType::ASR, 31),
8452 codegen_);
8453 } else {
8454 GenerateLongDataProc(instruction, codegen_);
8455 }
8456 }
8457}
8458
Artem Serov02109dd2016-09-23 17:17:54 +01008459// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8460void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out,
8461 vixl32::Register first,
8462 uint32_t value) {
8463 // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier).
8464 if (value == 0xffffffffu) {
8465 if (!out.Is(first)) {
8466 __ Mov(out, first);
8467 }
8468 return;
8469 }
8470 if (value == 0u) {
8471 __ Mov(out, 0);
8472 return;
8473 }
8474 if (GetAssembler()->ShifterOperandCanHold(AND, value)) {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008475 __ And(out, first, value);
8476 } else if (GetAssembler()->ShifterOperandCanHold(BIC, ~value)) {
8477 __ Bic(out, first, ~value);
Artem Serov02109dd2016-09-23 17:17:54 +01008478 } else {
Anton Kiriloveffd5bf2017-02-28 16:59:15 +00008479 DCHECK(IsPowerOfTwo(value + 1));
8480 __ Ubfx(out, first, 0, WhichPowerOf2(value + 1));
Artem Serov02109dd2016-09-23 17:17:54 +01008481 }
8482}
8483
8484// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8485void InstructionCodeGeneratorARMVIXL::GenerateOrrConst(vixl32::Register out,
8486 vixl32::Register first,
8487 uint32_t value) {
8488 // Optimize special cases for individual halfs of `or-long` (`or` is simplified earlier).
8489 if (value == 0u) {
8490 if (!out.Is(first)) {
8491 __ Mov(out, first);
8492 }
8493 return;
8494 }
8495 if (value == 0xffffffffu) {
8496 __ Mvn(out, 0);
8497 return;
8498 }
8499 if (GetAssembler()->ShifterOperandCanHold(ORR, value)) {
8500 __ Orr(out, first, value);
8501 } else {
8502 DCHECK(GetAssembler()->ShifterOperandCanHold(ORN, ~value));
8503 __ Orn(out, first, ~value);
8504 }
8505}
8506
8507// TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl.
8508void InstructionCodeGeneratorARMVIXL::GenerateEorConst(vixl32::Register out,
8509 vixl32::Register first,
8510 uint32_t value) {
8511 // Optimize special case for individual halfs of `xor-long` (`xor` is simplified earlier).
8512 if (value == 0u) {
8513 if (!out.Is(first)) {
8514 __ Mov(out, first);
8515 }
8516 return;
8517 }
8518 __ Eor(out, first, value);
8519}
8520
Anton Kirilovdda43962016-11-21 19:55:20 +00008521void InstructionCodeGeneratorARMVIXL::GenerateAddLongConst(Location out,
8522 Location first,
8523 uint64_t value) {
8524 vixl32::Register out_low = LowRegisterFrom(out);
8525 vixl32::Register out_high = HighRegisterFrom(out);
8526 vixl32::Register first_low = LowRegisterFrom(first);
8527 vixl32::Register first_high = HighRegisterFrom(first);
8528 uint32_t value_low = Low32Bits(value);
8529 uint32_t value_high = High32Bits(value);
8530 if (value_low == 0u) {
8531 if (!out_low.Is(first_low)) {
8532 __ Mov(out_low, first_low);
8533 }
8534 __ Add(out_high, first_high, value_high);
8535 return;
8536 }
8537 __ Adds(out_low, first_low, value_low);
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008538 if (GetAssembler()->ShifterOperandCanHold(ADC, value_high)) {
Anton Kirilovdda43962016-11-21 19:55:20 +00008539 __ Adc(out_high, first_high, value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008540 } else {
Vladimir Markof0a6a1d2018-01-08 14:23:56 +00008541 DCHECK(GetAssembler()->ShifterOperandCanHold(SBC, ~value_high));
8542 __ Sbc(out_high, first_high, ~value_high);
Anton Kirilovdda43962016-11-21 19:55:20 +00008543 }
8544}
8545
Artem Serov02109dd2016-09-23 17:17:54 +01008546void InstructionCodeGeneratorARMVIXL::HandleBitwiseOperation(HBinaryOperation* instruction) {
8547 LocationSummary* locations = instruction->GetLocations();
8548 Location first = locations->InAt(0);
8549 Location second = locations->InAt(1);
8550 Location out = locations->Out();
8551
8552 if (second.IsConstant()) {
8553 uint64_t value = static_cast<uint64_t>(Int64FromConstant(second.GetConstant()));
8554 uint32_t value_low = Low32Bits(value);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008555 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008556 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8557 vixl32::Register out_reg = OutputRegister(instruction);
8558 if (instruction->IsAnd()) {
8559 GenerateAndConst(out_reg, first_reg, value_low);
8560 } else if (instruction->IsOr()) {
8561 GenerateOrrConst(out_reg, first_reg, value_low);
8562 } else {
8563 DCHECK(instruction->IsXor());
8564 GenerateEorConst(out_reg, first_reg, value_low);
8565 }
8566 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008567 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008568 uint32_t value_high = High32Bits(value);
8569 vixl32::Register first_low = LowRegisterFrom(first);
8570 vixl32::Register first_high = HighRegisterFrom(first);
8571 vixl32::Register out_low = LowRegisterFrom(out);
8572 vixl32::Register out_high = HighRegisterFrom(out);
8573 if (instruction->IsAnd()) {
8574 GenerateAndConst(out_low, first_low, value_low);
8575 GenerateAndConst(out_high, first_high, value_high);
8576 } else if (instruction->IsOr()) {
8577 GenerateOrrConst(out_low, first_low, value_low);
8578 GenerateOrrConst(out_high, first_high, value_high);
8579 } else {
8580 DCHECK(instruction->IsXor());
8581 GenerateEorConst(out_low, first_low, value_low);
8582 GenerateEorConst(out_high, first_high, value_high);
8583 }
8584 }
8585 return;
8586 }
8587
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008588 if (instruction->GetResultType() == DataType::Type::kInt32) {
Artem Serov02109dd2016-09-23 17:17:54 +01008589 vixl32::Register first_reg = InputRegisterAt(instruction, 0);
8590 vixl32::Register second_reg = InputRegisterAt(instruction, 1);
8591 vixl32::Register out_reg = OutputRegister(instruction);
8592 if (instruction->IsAnd()) {
8593 __ And(out_reg, first_reg, second_reg);
8594 } else if (instruction->IsOr()) {
8595 __ Orr(out_reg, first_reg, second_reg);
8596 } else {
8597 DCHECK(instruction->IsXor());
8598 __ Eor(out_reg, first_reg, second_reg);
8599 }
8600 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01008601 DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
Artem Serov02109dd2016-09-23 17:17:54 +01008602 vixl32::Register first_low = LowRegisterFrom(first);
8603 vixl32::Register first_high = HighRegisterFrom(first);
8604 vixl32::Register second_low = LowRegisterFrom(second);
8605 vixl32::Register second_high = HighRegisterFrom(second);
8606 vixl32::Register out_low = LowRegisterFrom(out);
8607 vixl32::Register out_high = HighRegisterFrom(out);
8608 if (instruction->IsAnd()) {
8609 __ And(out_low, first_low, second_low);
8610 __ And(out_high, first_high, second_high);
8611 } else if (instruction->IsOr()) {
8612 __ Orr(out_low, first_low, second_low);
8613 __ Orr(out_high, first_high, second_high);
8614 } else {
8615 DCHECK(instruction->IsXor());
8616 __ Eor(out_low, first_low, second_low);
8617 __ Eor(out_high, first_high, second_high);
8618 }
8619 }
8620}
8621
Artem Serovcfbe9132016-10-14 15:58:56 +01008622void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadOneRegister(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008623 HInstruction* instruction,
Artem Serovcfbe9132016-10-14 15:58:56 +01008624 Location out,
8625 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008626 Location maybe_temp,
8627 ReadBarrierOption read_barrier_option) {
Artem Serovcfbe9132016-10-14 15:58:56 +01008628 vixl32::Register out_reg = RegisterFrom(out);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008629 if (read_barrier_option == kWithReadBarrier) {
8630 CHECK(kEmitCompilerReadBarrier);
8631 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8632 if (kUseBakerReadBarrier) {
8633 // Load with fast path based Baker's read barrier.
8634 // /* HeapReference<Object> */ out = *(out + offset)
8635 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08008636 instruction, out, out_reg, offset, maybe_temp, /* needs_null_check= */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008637 } else {
8638 // Load with slow path based read barrier.
8639 // Save the value of `out` into `maybe_temp` before overwriting it
8640 // in the following move operation, as we will need it for the
8641 // read barrier below.
8642 __ Mov(RegisterFrom(maybe_temp), out_reg);
8643 // /* HeapReference<Object> */ out = *(out + offset)
8644 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8645 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
8646 }
Artem Serovcfbe9132016-10-14 15:58:56 +01008647 } else {
8648 // Plain load with no read barrier.
8649 // /* HeapReference<Object> */ out = *(out + offset)
8650 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, out_reg, offset);
8651 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8652 }
8653}
8654
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008655void InstructionCodeGeneratorARMVIXL::GenerateReferenceLoadTwoRegisters(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008656 HInstruction* instruction,
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008657 Location out,
8658 Location obj,
8659 uint32_t offset,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008660 Location maybe_temp,
8661 ReadBarrierOption read_barrier_option) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008662 vixl32::Register out_reg = RegisterFrom(out);
8663 vixl32::Register obj_reg = RegisterFrom(obj);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008664 if (read_barrier_option == kWithReadBarrier) {
8665 CHECK(kEmitCompilerReadBarrier);
8666 if (kUseBakerReadBarrier) {
8667 DCHECK(maybe_temp.IsRegister()) << maybe_temp;
8668 // Load with fast path based Baker's read barrier.
8669 // /* HeapReference<Object> */ out = *(obj + offset)
8670 codegen_->GenerateFieldLoadWithBakerReadBarrier(
Andreas Gampe3db70682018-12-26 15:12:03 -08008671 instruction, out, obj_reg, offset, maybe_temp, /* needs_null_check= */ false);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008672 } else {
8673 // Load with slow path based read barrier.
8674 // /* HeapReference<Object> */ out = *(obj + offset)
8675 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8676 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
8677 }
Anton Kirilove28d9ae2016-10-25 18:17:23 +01008678 } else {
8679 // Plain load with no read barrier.
8680 // /* HeapReference<Object> */ out = *(obj + offset)
8681 GetAssembler()->LoadFromOffset(kLoadWord, out_reg, obj_reg, offset);
8682 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
8683 }
8684}
8685
Vladimir Markoca1e0382018-04-11 09:58:41 +00008686void CodeGeneratorARMVIXL::GenerateGcRootFieldLoad(
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008687 HInstruction* instruction,
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008688 Location root,
8689 vixl32::Register obj,
8690 uint32_t offset,
Artem Serovd4cc5b22016-11-04 11:19:09 +00008691 ReadBarrierOption read_barrier_option) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008692 vixl32::Register root_reg = RegisterFrom(root);
Artem Serovd4cc5b22016-11-04 11:19:09 +00008693 if (read_barrier_option == kWithReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008694 DCHECK(kEmitCompilerReadBarrier);
8695 if (kUseBakerReadBarrier) {
8696 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
Roland Levillainba650a42017-03-06 13:52:32 +00008697 // Baker's read barrier are used.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008698
Vladimir Marko008e09f32018-08-06 15:42:43 +01008699 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
8700 // the Marking Register) to decide whether we need to enter
8701 // the slow path to mark the GC root.
8702 //
8703 // We use shared thunks for the slow path; shared within the method
8704 // for JIT, across methods for AOT. That thunk checks the reference
8705 // and jumps to the entrypoint if needed.
8706 //
8707 // lr = &return_address;
8708 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
8709 // if (mr) { // Thread::Current()->GetIsGcMarking()
8710 // goto gc_root_thunk<root_reg>(lr)
8711 // }
8712 // return_address:
Roland Levillainba650a42017-03-06 13:52:32 +00008713
Vladimir Marko008e09f32018-08-06 15:42:43 +01008714 UseScratchRegisterScope temps(GetVIXLAssembler());
8715 temps.Exclude(ip);
8716 bool narrow = CanEmitNarrowLdr(root_reg, obj, offset);
8717 uint32_t custom_data = EncodeBakerReadBarrierGcRootData(root_reg.GetCode(), narrow);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008718
Vladimir Markod887ed82018-08-14 13:52:12 +00008719 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u) + /* LDR */ (narrow ? 1u : 0u);
8720 size_t wide_instructions = /* ADR+CMP+LDR+BNE */ 4u - narrow_instructions;
8721 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8722 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8723 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008724 vixl32::Label return_address;
8725 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8726 __ cmp(mr, Operand(0));
8727 // Currently the offset is always within range. If that changes,
8728 // we shall have to split the load the same way as for fields.
8729 DCHECK_LT(offset, kReferenceLoadMinFarOffset);
8730 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8731 __ ldr(EncodingSize(narrow ? Narrow : Wide), root_reg, MemOperand(obj, offset));
8732 EmitBakerReadBarrierBne(custom_data);
Vladimir Markod887ed82018-08-14 13:52:12 +00008733 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008734 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8735 narrow ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_OFFSET
8736 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_OFFSET);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008737 } else {
8738 // GC root loaded through a slow path for read barriers other
8739 // than Baker's.
8740 // /* GcRoot<mirror::Object>* */ root = obj + offset
8741 __ Add(root_reg, obj, offset);
8742 // /* mirror::Object* */ root = root->Read()
Vladimir Markoca1e0382018-04-11 09:58:41 +00008743 GenerateReadBarrierForRootSlow(instruction, root, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008744 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008745 } else {
8746 // Plain GC root load with no read barrier.
8747 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
8748 GetAssembler()->LoadFromOffset(kLoadWord, root_reg, obj, offset);
8749 // Note that GC roots are not affected by heap poisoning, thus we
8750 // do not have to unpoison `root_reg` here.
8751 }
Andreas Gampe3db70682018-12-26 15:12:03 -08008752 MaybeGenerateMarkingRegisterCheck(/* code= */ 19);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01008753}
8754
Vladimir Markod887ed82018-08-14 13:52:12 +00008755void CodeGeneratorARMVIXL::GenerateUnsafeCasOldValueAddWithBakerReadBarrier(
8756 vixl::aarch32::Register old_value,
8757 vixl::aarch32::Register adjusted_old_value,
8758 vixl::aarch32::Register expected) {
8759 DCHECK(kEmitCompilerReadBarrier);
8760 DCHECK(kUseBakerReadBarrier);
8761
8762 // Similar to the Baker RB path in GenerateGcRootFieldLoad(), with an ADD instead of LDR.
8763 uint32_t custom_data = EncodeBakerReadBarrierUnsafeCasData(old_value.GetCode());
8764
8765 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
8766 size_t wide_instructions = /* ADR+CMP+ADD+BNE */ 4u - narrow_instructions;
8767 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8768 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8769 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
8770 vixl32::Label return_address;
8771 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8772 __ cmp(mr, Operand(0));
8773 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8774 __ add(EncodingSize(Wide), old_value, adjusted_old_value, Operand(expected)); // Preserves flags.
8775 EmitBakerReadBarrierBne(custom_data);
8776 __ bind(&return_address);
8777 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8778 BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ADD_OFFSET);
8779}
8780
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008781void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8782 Location ref,
8783 vixl32::Register obj,
Vladimir Marko248141f2018-08-10 10:40:07 +01008784 const vixl32::MemOperand& src,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008785 bool needs_null_check) {
8786 DCHECK(kEmitCompilerReadBarrier);
8787 DCHECK(kUseBakerReadBarrier);
8788
Vladimir Marko008e09f32018-08-06 15:42:43 +01008789 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8790 // Marking Register) to decide whether we need to enter the slow
8791 // path to mark the reference. Then, in the slow path, check the
8792 // gray bit in the lock word of the reference's holder (`obj`) to
8793 // decide whether to mark `ref` or not.
8794 //
8795 // We use shared thunks for the slow path; shared within the method
8796 // for JIT, across methods for AOT. That thunk checks the holder
8797 // and jumps to the entrypoint if needed. If the holder is not gray,
8798 // it creates a fake dependency and returns to the LDR instruction.
8799 //
8800 // lr = &gray_return_address;
8801 // if (mr) { // Thread::Current()->GetIsGcMarking()
8802 // goto field_thunk<holder_reg, base_reg>(lr)
8803 // }
8804 // not_gray_return_address:
8805 // // Original reference load. If the offset is too large to fit
8806 // // into LDR, we use an adjusted base register here.
8807 // HeapReference<mirror::Object> reference = *(obj+offset);
8808 // gray_return_address:
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008809
Vladimir Marko248141f2018-08-10 10:40:07 +01008810 DCHECK(src.GetAddrMode() == vixl32::Offset);
8811 DCHECK_ALIGNED(src.GetOffsetImmediate(), sizeof(mirror::HeapReference<mirror::Object>));
Vladimir Marko008e09f32018-08-06 15:42:43 +01008812 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
Vladimir Marko248141f2018-08-10 10:40:07 +01008813 bool narrow = CanEmitNarrowLdr(ref_reg, src.GetBaseRegister(), src.GetOffsetImmediate());
8814
Vladimir Marko008e09f32018-08-06 15:42:43 +01008815 UseScratchRegisterScope temps(GetVIXLAssembler());
8816 temps.Exclude(ip);
Vladimir Marko248141f2018-08-10 10:40:07 +01008817 uint32_t custom_data =
8818 EncodeBakerReadBarrierFieldData(src.GetBaseRegister().GetCode(), obj.GetCode(), narrow);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008819
Vladimir Marko008e09f32018-08-06 15:42:43 +01008820 {
Vladimir Markod887ed82018-08-14 13:52:12 +00008821 size_t narrow_instructions =
8822 /* CMP */ (mr.IsLow() ? 1u : 0u) +
8823 /* LDR+unpoison? */ (narrow ? (kPoisonHeapReferences ? 2u : 1u) : 0u);
8824 size_t wide_instructions =
8825 /* ADR+CMP+LDR+BNE+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
8826 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8827 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8828 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008829 vixl32::Label return_address;
8830 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8831 __ cmp(mr, Operand(0));
8832 EmitBakerReadBarrierBne(custom_data);
8833 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
Vladimir Marko248141f2018-08-10 10:40:07 +01008834 __ ldr(EncodingSize(narrow ? Narrow : Wide), ref_reg, src);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008835 if (needs_null_check) {
8836 MaybeRecordImplicitNullCheck(instruction);
8837 }
8838 // Note: We need a specific width for the unpoisoning NEG.
8839 if (kPoisonHeapReferences) {
8840 if (narrow) {
8841 // The only 16-bit encoding is T1 which sets flags outside IT block (i.e. RSBS, not RSB).
8842 __ rsbs(EncodingSize(Narrow), ref_reg, ref_reg, Operand(0));
8843 } else {
8844 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
8845 }
8846 }
Vladimir Markod887ed82018-08-14 13:52:12 +00008847 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008848 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8849 narrow ? BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET
8850 : BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET);
8851 }
Andreas Gampe3db70682018-12-26 15:12:03 -08008852 MaybeGenerateMarkingRegisterCheck(/* code= */ 20, /* temp_loc= */ LocationFrom(ip));
Roland Levillain6070e882016-11-03 17:51:58 +00008853}
8854
Vladimir Marko248141f2018-08-10 10:40:07 +01008855void CodeGeneratorARMVIXL::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
8856 Location ref,
8857 vixl32::Register obj,
8858 uint32_t offset,
8859 Location temp,
8860 bool needs_null_check) {
8861 DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
8862 vixl32::Register base = obj;
8863 if (offset >= kReferenceLoadMinFarOffset) {
8864 base = RegisterFrom(temp);
8865 static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
8866 __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
8867 offset &= (kReferenceLoadMinFarOffset - 1u);
8868 }
8869 GenerateFieldLoadWithBakerReadBarrier(
8870 instruction, ref, obj, MemOperand(base, offset), needs_null_check);
8871}
8872
Vladimir Marko008e09f32018-08-06 15:42:43 +01008873void CodeGeneratorARMVIXL::GenerateArrayLoadWithBakerReadBarrier(Location ref,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008874 vixl32::Register obj,
8875 uint32_t data_offset,
8876 Location index,
8877 Location temp,
8878 bool needs_null_check) {
8879 DCHECK(kEmitCompilerReadBarrier);
8880 DCHECK(kUseBakerReadBarrier);
8881
8882 static_assert(
8883 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
8884 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008885 ScaleFactor scale_factor = TIMES_4;
8886
Vladimir Marko008e09f32018-08-06 15:42:43 +01008887 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
8888 // Marking Register) to decide whether we need to enter the slow
8889 // path to mark the reference. Then, in the slow path, check the
8890 // gray bit in the lock word of the reference's holder (`obj`) to
8891 // decide whether to mark `ref` or not.
8892 //
8893 // We use shared thunks for the slow path; shared within the method
8894 // for JIT, across methods for AOT. That thunk checks the holder
8895 // and jumps to the entrypoint if needed. If the holder is not gray,
8896 // it creates a fake dependency and returns to the LDR instruction.
8897 //
8898 // lr = &gray_return_address;
8899 // if (mr) { // Thread::Current()->GetIsGcMarking()
8900 // goto array_thunk<base_reg>(lr)
8901 // }
8902 // not_gray_return_address:
8903 // // Original reference load. If the offset is too large to fit
8904 // // into LDR, we use an adjusted base register here.
8905 // HeapReference<mirror::Object> reference = data[index];
8906 // gray_return_address:
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008907
Vladimir Marko008e09f32018-08-06 15:42:43 +01008908 DCHECK(index.IsValid());
8909 vixl32::Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
8910 vixl32::Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
8911 vixl32::Register data_reg = RegisterFrom(temp, DataType::Type::kInt32); // Raw pointer.
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008912
Vladimir Marko008e09f32018-08-06 15:42:43 +01008913 UseScratchRegisterScope temps(GetVIXLAssembler());
8914 temps.Exclude(ip);
8915 uint32_t custom_data = EncodeBakerReadBarrierArrayData(data_reg.GetCode());
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008916
Vladimir Marko008e09f32018-08-06 15:42:43 +01008917 __ Add(data_reg, obj, Operand(data_offset));
8918 {
Vladimir Markod887ed82018-08-14 13:52:12 +00008919 size_t narrow_instructions = /* CMP */ (mr.IsLow() ? 1u : 0u);
8920 size_t wide_instructions =
8921 /* ADR+CMP+BNE+LDR+unpoison? */ (kPoisonHeapReferences ? 5u : 4u) - narrow_instructions;
8922 size_t exact_size = wide_instructions * vixl32::k32BitT32InstructionSizeInBytes +
8923 narrow_instructions * vixl32::k16BitT32InstructionSizeInBytes;
8924 ExactAssemblyScope guard(GetVIXLAssembler(), exact_size);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008925 vixl32::Label return_address;
8926 EmitAdrCode adr(GetVIXLAssembler(), lr, &return_address);
8927 __ cmp(mr, Operand(0));
8928 EmitBakerReadBarrierBne(custom_data);
8929 ptrdiff_t old_offset = GetVIXLAssembler()->GetBuffer()->GetCursorOffset();
8930 __ ldr(ref_reg, MemOperand(data_reg, index_reg, vixl32::LSL, scale_factor));
8931 DCHECK(!needs_null_check); // The thunk cannot handle the null check.
8932 // Note: We need a Wide NEG for the unpoisoning.
8933 if (kPoisonHeapReferences) {
8934 __ rsb(EncodingSize(Wide), ref_reg, ref_reg, Operand(0));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008935 }
Vladimir Markod887ed82018-08-14 13:52:12 +00008936 __ bind(&return_address);
Vladimir Marko008e09f32018-08-06 15:42:43 +01008937 DCHECK_EQ(old_offset - GetVIXLAssembler()->GetBuffer()->GetCursorOffset(),
8938 BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET);
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01008939 }
Andreas Gampe3db70682018-12-26 15:12:03 -08008940 MaybeGenerateMarkingRegisterCheck(/* code= */ 21, /* temp_loc= */ LocationFrom(ip));
Roland Levillain6070e882016-11-03 17:51:58 +00008941}
8942
Roland Levillain5daa4952017-07-03 17:23:56 +01008943void CodeGeneratorARMVIXL::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
8944 // The following condition is a compile-time one, so it does not have a run-time cost.
8945 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
8946 // The following condition is a run-time one; it is executed after the
8947 // previous compile-time test, to avoid penalizing non-debug builds.
8948 if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
8949 UseScratchRegisterScope temps(GetVIXLAssembler());
8950 vixl32::Register temp = temp_loc.IsValid() ? RegisterFrom(temp_loc) : temps.Acquire();
8951 GetAssembler()->GenerateMarkingRegisterCheck(temp,
8952 kMarkingRegisterCheckBreakCodeBaseCode + code);
8953 }
8954 }
8955}
8956
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008957void CodeGeneratorARMVIXL::GenerateReadBarrierSlow(HInstruction* instruction,
8958 Location out,
8959 Location ref,
8960 Location obj,
8961 uint32_t offset,
8962 Location index) {
8963 DCHECK(kEmitCompilerReadBarrier);
8964
8965 // Insert a slow path based read barrier *after* the reference load.
8966 //
8967 // If heap poisoning is enabled, the unpoisoning of the loaded
8968 // reference will be carried out by the runtime within the slow
8969 // path.
8970 //
8971 // Note that `ref` currently does not get unpoisoned (when heap
8972 // poisoning is enabled), which is alright as the `ref` argument is
8973 // not used by the artReadBarrierSlow entry point.
8974 //
8975 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
Vladimir Marko174b2e22017-10-12 13:34:49 +01008976 SlowPathCodeARMVIXL* slow_path = new (GetScopedAllocator())
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008977 ReadBarrierForHeapReferenceSlowPathARMVIXL(instruction, out, ref, obj, offset, index);
8978 AddSlowPath(slow_path);
8979
8980 __ B(slow_path->GetEntryLabel());
8981 __ Bind(slow_path->GetExitLabel());
8982}
8983
8984void CodeGeneratorARMVIXL::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
Artem Serov02d37832016-10-25 15:25:33 +01008985 Location out,
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008986 Location ref,
8987 Location obj,
8988 uint32_t offset,
8989 Location index) {
Artem Serov02d37832016-10-25 15:25:33 +01008990 if (kEmitCompilerReadBarrier) {
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008991 // Baker's read barriers shall be handled by the fast path
Roland Levillain9983e302017-07-14 14:34:22 +01008992 // (CodeGeneratorARMVIXL::GenerateReferenceLoadWithBakerReadBarrier).
Artem Serov02d37832016-10-25 15:25:33 +01008993 DCHECK(!kUseBakerReadBarrier);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00008994 // If heap poisoning is enabled, unpoisoning will be taken care of
8995 // by the runtime within the slow path.
8996 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Artem Serov02d37832016-10-25 15:25:33 +01008997 } else if (kPoisonHeapReferences) {
8998 GetAssembler()->UnpoisonHeapReference(RegisterFrom(out));
8999 }
9000}
9001
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009002void CodeGeneratorARMVIXL::GenerateReadBarrierForRootSlow(HInstruction* instruction,
9003 Location out,
9004 Location root) {
9005 DCHECK(kEmitCompilerReadBarrier);
9006
9007 // Insert a slow path based read barrier *after* the GC root load.
9008 //
9009 // Note that GC roots are not affected by heap poisoning, so we do
9010 // not need to do anything special for this here.
9011 SlowPathCodeARMVIXL* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01009012 new (GetScopedAllocator()) ReadBarrierForRootSlowPathARMVIXL(instruction, out, root);
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009013 AddSlowPath(slow_path);
9014
9015 __ B(slow_path->GetEntryLabel());
9016 __ Bind(slow_path->GetExitLabel());
9017}
9018
Artem Serov02d37832016-10-25 15:25:33 +01009019// Check if the desired_dispatch_info is supported. If it is, return it,
9020// otherwise return a fall-back info that should be used instead.
9021HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARMVIXL::GetSupportedInvokeStaticOrDirectDispatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00009022 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Vladimir Marko86c87522020-05-11 16:55:55 +01009023 ArtMethod* method) {
9024 if (desired_dispatch_info.code_ptr_location ==
9025 HInvokeStaticOrDirect::CodePtrLocation::kCallCriticalNative) {
9026 // TODO: Work around CheckTypeConsistency() in code_generator.cc that does not allow
9027 // putting FP values in core registers as we need to do for the soft-float native ABI.
9028 ScopedObjectAccess soa(Thread::Current());
9029 uint32_t shorty_len;
9030 const char* shorty = method->GetShorty(&shorty_len);
9031 size_t reg = 0u;
9032 for (uint32_t i = 1; i != shorty_len; ++i) {
9033 size_t next_reg = reg + 1u;
9034 if (shorty[i] == 'D' || shorty[i] == 'J') {
9035 reg = RoundUp(reg, 2u);
9036 next_reg = reg + 2u;
9037 }
9038 if (reg == 4u) {
9039 break;
9040 }
9041 if (shorty[i] == 'D' || shorty[i] == 'F') {
9042 HInvokeStaticOrDirect::DispatchInfo dispatch_info = desired_dispatch_info;
9043 dispatch_info.code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod;
9044 return dispatch_info;
9045 }
9046 reg = next_reg;
9047 }
9048 }
Nicolas Geoffraye807ff72017-01-23 09:03:12 +00009049 return desired_dispatch_info;
Artem Serov02d37832016-10-25 15:25:33 +01009050}
9051
Vladimir Markod254f5c2017-06-02 15:18:36 +00009052void CodeGeneratorARMVIXL::GenerateStaticOrDirectCall(
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009053 HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009054 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009055 switch (invoke->GetMethodLoadKind()) {
9056 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
9057 uint32_t offset =
9058 GetThreadOffset<kArmPointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
9059 // temp = thread->string_init_entrypoint
Artem Serovd4cc5b22016-11-04 11:19:09 +00009060 GetAssembler()->LoadFromOffset(kLoadWord, RegisterFrom(temp), tr, offset);
9061 break;
9062 }
9063 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
Vladimir Marko86c87522020-05-11 16:55:55 +01009064 callee_method = invoke->GetLocations()->InAt(invoke->GetCurrentMethodIndex());
Artem Serovd4cc5b22016-11-04 11:19:09 +00009065 break;
Vladimir Marko65979462017-05-19 17:25:12 +01009066 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko44ca0752019-07-29 10:18:25 +01009067 DCHECK(GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009068 PcRelativePatchInfo* labels = NewBootImageMethodPatch(invoke->GetTargetMethod());
Vladimir Marko65979462017-05-19 17:25:12 +01009069 vixl32::Register temp_reg = RegisterFrom(temp);
9070 EmitMovwMovtPlaceholder(labels, temp_reg);
9071 break;
9072 }
Vladimir Markob066d432018-01-03 13:14:37 +00009073 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009074 uint32_t boot_image_offset = GetBootImageOffset(invoke);
Vladimir Markob066d432018-01-03 13:14:37 +00009075 PcRelativePatchInfo* labels = NewBootImageRelRoPatch(boot_image_offset);
9076 vixl32::Register temp_reg = RegisterFrom(temp);
9077 EmitMovwMovtPlaceholder(labels, temp_reg);
9078 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
9079 break;
9080 }
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009081 case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
9082 PcRelativePatchInfo* labels = NewMethodBssEntryPatch(
9083 MethodReference(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex()));
9084 vixl32::Register temp_reg = RegisterFrom(temp);
9085 EmitMovwMovtPlaceholder(labels, temp_reg);
Vladimir Markod5fd5c32019-07-02 14:46:32 +01009086 // All aligned loads are implicitly atomic consume operations on ARM.
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009087 GetAssembler()->LoadFromOffset(kLoadWord, temp_reg, temp_reg, /* offset*/ 0);
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009088 break;
9089 }
Vladimir Marko8e524ad2018-07-13 10:27:43 +01009090 case HInvokeStaticOrDirect::MethodLoadKind::kJitDirectAddress:
9091 __ Mov(RegisterFrom(temp), Operand::From(invoke->GetMethodAddress()));
9092 break;
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009093 case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
9094 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
9095 return; // No code pointer retrieval; the runtime performs the call directly.
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009096 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009097 }
9098
Vladimir Marko86c87522020-05-11 16:55:55 +01009099 auto call_code_pointer_member = [&](MemberOffset offset) {
9100 // LR = callee_method->member;
9101 GetAssembler()->LoadFromOffset(kLoadWord, lr, RegisterFrom(callee_method), offset.Int32Value());
9102 {
9103 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9104 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9105 ExactAssemblyScope aas(GetVIXLAssembler(),
9106 vixl32::k16BitT32InstructionSizeInBytes,
9107 CodeBufferCheckScope::kExactSize);
9108 // LR()
9109 __ blx(lr);
9110 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9111 }
9112 };
Artem Serovd4cc5b22016-11-04 11:19:09 +00009113 switch (invoke->GetCodePtrLocation()) {
9114 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009115 {
9116 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9117 ExactAssemblyScope aas(GetVIXLAssembler(),
9118 vixl32::k32BitT32InstructionSizeInBytes,
9119 CodeBufferCheckScope::kMaximumSize);
9120 __ bl(GetFrameEntryLabel());
9121 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9122 }
Artem Serovd4cc5b22016-11-04 11:19:09 +00009123 break;
Vladimir Marko86c87522020-05-11 16:55:55 +01009124 case HInvokeStaticOrDirect::CodePtrLocation::kCallCriticalNative: {
Vladimir Marko86c87522020-05-11 16:55:55 +01009125 size_t out_frame_size =
9126 PrepareCriticalNativeCall<CriticalNativeCallingConventionVisitorARMVIXL,
9127 kAapcsStackAlignment,
Vladimir Markodec78172020-06-19 15:31:23 +01009128 GetCriticalNativeDirectCallFrameSize>(invoke);
Vladimir Marko86c87522020-05-11 16:55:55 +01009129 call_code_pointer_member(ArtMethod::EntryPointFromJniOffset(kArmPointerSize));
9130 // Move the result when needed due to native and managed ABI mismatch.
9131 switch (invoke->GetType()) {
9132 case DataType::Type::kFloat32:
9133 __ Vmov(s0, r0);
9134 break;
9135 case DataType::Type::kFloat64:
9136 __ Vmov(d0, r0, r1);
9137 break;
9138 case DataType::Type::kBool:
9139 case DataType::Type::kInt8:
9140 case DataType::Type::kUint16:
9141 case DataType::Type::kInt16:
9142 case DataType::Type::kInt32:
9143 case DataType::Type::kInt64:
9144 case DataType::Type::kVoid:
9145 break;
9146 default:
9147 DCHECK(false) << invoke->GetType();
9148 break;
9149 }
9150 if (out_frame_size != 0u) {
Vladimir Markodec78172020-06-19 15:31:23 +01009151 DecreaseFrame(out_frame_size);
Vladimir Marko86c87522020-05-11 16:55:55 +01009152 }
9153 break;
9154 }
9155 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
9156 call_code_pointer_member(ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009157 break;
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009158 }
9159
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009160 DCHECK(!IsLeafMethod());
9161}
9162
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009163void CodeGeneratorARMVIXL::GenerateVirtualCall(
9164 HInvokeVirtual* invoke, Location temp_location, SlowPathCode* slow_path) {
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009165 vixl32::Register temp = RegisterFrom(temp_location);
9166 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9167 invoke->GetVTableIndex(), kArmPointerSize).Uint32Value();
9168
9169 // Use the calling convention instead of the location of the receiver, as
9170 // intrinsics may have put the receiver in a different register. In the intrinsics
9171 // slow path, the arguments have been moved to the right place, so here we are
9172 // guaranteed that the receiver is the first register of the calling convention.
9173 InvokeDexCallingConventionARMVIXL calling_convention;
9174 vixl32::Register receiver = calling_convention.GetRegisterAt(0);
9175 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
Alexandre Rames374ddf32016-11-04 10:40:49 +00009176 {
9177 // Make sure the pc is recorded immediately after the `ldr` instruction.
Artem Serov0fb37192016-12-06 18:13:40 +00009178 ExactAssemblyScope aas(GetVIXLAssembler(),
9179 vixl32::kMaxInstructionSizeInBytes,
9180 CodeBufferCheckScope::kMaximumSize);
Alexandre Rames374ddf32016-11-04 10:40:49 +00009181 // /* HeapReference<Class> */ temp = receiver->klass_
9182 __ ldr(temp, MemOperand(receiver, class_offset));
9183 MaybeRecordImplicitNullCheck(invoke);
9184 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009185 // Instead of simply (possibly) unpoisoning `temp` here, we should
9186 // emit a read barrier for the previous class reference load.
9187 // However this is not required in practice, as this is an
9188 // intermediate/temporary reference and because the current
9189 // concurrent copying collector keeps the from-space memory
9190 // intact/accessible until the end of the marking phase (the
9191 // concurrent copying collector may not in the future).
9192 GetAssembler()->MaybeUnpoisonHeapReference(temp);
9193
Nicolas Geoffraye2a3aa92019-11-25 17:52:58 +00009194 // If we're compiling baseline, update the inline cache.
9195 MaybeGenerateInlineCacheCheck(invoke, temp);
9196
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009197 // temp = temp->GetMethodAt(method_offset);
9198 uint32_t entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(
9199 kArmPointerSize).Int32Value();
9200 GetAssembler()->LoadFromOffset(kLoadWord, temp, temp, method_offset);
9201 // LR = temp->GetEntryPoint();
9202 GetAssembler()->LoadFromOffset(kLoadWord, lr, temp, entry_point);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01009203 {
9204 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
9205 // blx in T32 has only 16bit encoding that's why a stricter check for the scope is used.
9206 ExactAssemblyScope aas(GetVIXLAssembler(),
9207 vixl32::k16BitT32InstructionSizeInBytes,
9208 CodeBufferCheckScope::kExactSize);
9209 // LR();
9210 __ blx(lr);
9211 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
9212 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009213}
9214
Vladimir Marko6fd16062018-06-26 11:02:04 +01009215CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageIntrinsicPatch(
9216 uint32_t intrinsic_data) {
Vladimir Marko2d06e022019-07-08 15:45:19 +01009217 return NewPcRelativePatch(/* dex_file= */ nullptr, intrinsic_data, &boot_image_other_patches_);
Vladimir Marko6fd16062018-06-26 11:02:04 +01009218}
9219
Vladimir Markob066d432018-01-03 13:14:37 +00009220CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageRelRoPatch(
9221 uint32_t boot_image_offset) {
Andreas Gampe3db70682018-12-26 15:12:03 -08009222 return NewPcRelativePatch(/* dex_file= */ nullptr,
Vladimir Markob066d432018-01-03 13:14:37 +00009223 boot_image_offset,
Vladimir Marko2d06e022019-07-08 15:45:19 +01009224 &boot_image_other_patches_);
Vladimir Markob066d432018-01-03 13:14:37 +00009225}
9226
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009227CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageMethodPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01009228 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009229 return NewPcRelativePatch(
9230 target_method.dex_file, target_method.index, &boot_image_method_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009231}
9232
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009233CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewMethodBssEntryPatch(
9234 MethodReference target_method) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009235 return NewPcRelativePatch(
9236 target_method.dex_file, target_method.index, &method_bss_entry_patches_);
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009237}
9238
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009239CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageTypePatch(
Artem Serovd4cc5b22016-11-04 11:19:09 +00009240 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009241 return NewPcRelativePatch(&dex_file, type_index.index_, &boot_image_type_patches_);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009242}
9243
Vladimir Marko1998cd02017-01-13 13:02:58 +00009244CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewTypeBssEntryPatch(
9245 const DexFile& dex_file, dex::TypeIndex type_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009246 return NewPcRelativePatch(&dex_file, type_index.index_, &type_bss_entry_patches_);
Vladimir Marko1998cd02017-01-13 13:02:58 +00009247}
9248
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009249CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewBootImageStringPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01009250 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009251 return NewPcRelativePatch(&dex_file, string_index.index_, &boot_image_string_patches_);
Vladimir Marko65979462017-05-19 17:25:12 +01009252}
9253
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009254CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewStringBssEntryPatch(
9255 const DexFile& dex_file, dex::StringIndex string_index) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009256 return NewPcRelativePatch(&dex_file, string_index.index_, &string_bss_entry_patches_);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009257}
9258
Artem Serovd4cc5b22016-11-04 11:19:09 +00009259CodeGeneratorARMVIXL::PcRelativePatchInfo* CodeGeneratorARMVIXL::NewPcRelativePatch(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009260 const DexFile* dex_file, uint32_t offset_or_index, ArenaDeque<PcRelativePatchInfo>* patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009261 patches->emplace_back(dex_file, offset_or_index);
9262 return &patches->back();
9263}
9264
Vladimir Markof6675082019-05-17 12:05:28 +01009265void CodeGeneratorARMVIXL::EmitEntrypointThunkCall(ThreadOffset32 entrypoint_offset) {
9266 DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope.
Vladimir Marko695348f2020-05-19 14:42:02 +01009267 DCHECK(!GetCompilerOptions().IsJitCompiler());
Vladimir Markof6675082019-05-17 12:05:28 +01009268 call_entrypoint_patches_.emplace_back(/*dex_file*/ nullptr, entrypoint_offset.Uint32Value());
9269 vixl::aarch32::Label* bl_label = &call_entrypoint_patches_.back().label;
9270 __ bind(bl_label);
9271 vixl32::Label placeholder_label;
9272 __ bl(&placeholder_label); // Placeholder, patched at link-time.
9273 __ bind(&placeholder_label);
9274}
9275
Vladimir Marko966b46f2018-08-03 10:20:19 +00009276void CodeGeneratorARMVIXL::EmitBakerReadBarrierBne(uint32_t custom_data) {
Vladimir Markod887ed82018-08-14 13:52:12 +00009277 DCHECK(!__ AllowMacroInstructions()); // In ExactAssemblyScope.
Vladimir Marko695348f2020-05-19 14:42:02 +01009278 if (GetCompilerOptions().IsJitCompiler()) {
Vladimir Marko966b46f2018-08-03 10:20:19 +00009279 auto it = jit_baker_read_barrier_slow_paths_.FindOrAdd(custom_data);
9280 vixl::aarch32::Label* slow_path_entry = &it->second.label;
9281 __ b(ne, EncodingSize(Wide), slow_path_entry);
9282 } else {
9283 baker_read_barrier_patches_.emplace_back(custom_data);
9284 vixl::aarch32::Label* patch_label = &baker_read_barrier_patches_.back().label;
9285 __ bind(patch_label);
9286 vixl32::Label placeholder_label;
9287 __ b(ne, EncodingSize(Wide), &placeholder_label); // Placeholder, patched at link-time.
9288 __ bind(&placeholder_label);
9289 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009290}
9291
Artem Serovc5fcb442016-12-02 19:19:58 +00009292VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateBootImageAddressLiteral(uint32_t address) {
Vladimir Marko8e524ad2018-07-13 10:27:43 +01009293 return DeduplicateUint32Literal(address, &uint32_literals_);
Artem Serovc5fcb442016-12-02 19:19:58 +00009294}
9295
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00009296VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitStringLiteral(
9297 const DexFile& dex_file,
9298 dex::StringIndex string_index,
9299 Handle<mirror::String> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01009300 ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00009301 return jit_string_patches_.GetOrCreate(
9302 StringReference(&dex_file, string_index),
9303 [this]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08009304 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00009305 });
9306}
9307
9308VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateJitClassLiteral(const DexFile& dex_file,
9309 dex::TypeIndex type_index,
Nicolas Geoffray5247c082017-01-13 14:17:29 +00009310 Handle<mirror::Class> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01009311 ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
Artem Serovc5fcb442016-12-02 19:19:58 +00009312 return jit_class_patches_.GetOrCreate(
9313 TypeReference(&dex_file, type_index),
9314 [this]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08009315 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ 0u);
Artem Serovc5fcb442016-12-02 19:19:58 +00009316 });
9317}
9318
Vladimir Marko6fd16062018-06-26 11:02:04 +01009319void CodeGeneratorARMVIXL::LoadBootImageAddress(vixl32::Register reg,
9320 uint32_t boot_image_reference) {
9321 if (GetCompilerOptions().IsBootImage()) {
9322 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
9323 NewBootImageIntrinsicPatch(boot_image_reference);
9324 EmitMovwMovtPlaceholder(labels, reg);
Vladimir Markoa2da9b92018-10-10 14:21:55 +01009325 } else if (GetCompilerOptions().GetCompilePic()) {
Vladimir Marko6fd16062018-06-26 11:02:04 +01009326 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels =
9327 NewBootImageRelRoPatch(boot_image_reference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01009328 EmitMovwMovtPlaceholder(labels, reg);
Andreas Gampe3db70682018-12-26 15:12:03 -08009329 __ Ldr(reg, MemOperand(reg, /* offset= */ 0));
Vladimir Markoeebb8212018-06-05 14:57:24 +01009330 } else {
Vladimir Marko695348f2020-05-19 14:42:02 +01009331 DCHECK(GetCompilerOptions().IsJitCompiler());
Vladimir Markoeebb8212018-06-05 14:57:24 +01009332 gc::Heap* heap = Runtime::Current()->GetHeap();
9333 DCHECK(!heap->GetBootImageSpaces().empty());
9334 uintptr_t address =
Vladimir Marko6fd16062018-06-26 11:02:04 +01009335 reinterpret_cast<uintptr_t>(heap->GetBootImageSpaces()[0]->Begin() + boot_image_reference);
Vladimir Markoeebb8212018-06-05 14:57:24 +01009336 __ Ldr(reg, DeduplicateBootImageAddressLiteral(dchecked_integral_cast<uint32_t>(address)));
9337 }
9338}
9339
Vladimir Marko6fd16062018-06-26 11:02:04 +01009340void CodeGeneratorARMVIXL::AllocateInstanceForIntrinsic(HInvokeStaticOrDirect* invoke,
9341 uint32_t boot_image_offset) {
9342 DCHECK(invoke->IsStatic());
9343 InvokeRuntimeCallingConventionARMVIXL calling_convention;
9344 vixl32::Register argument = calling_convention.GetRegisterAt(0);
9345 if (GetCompilerOptions().IsBootImage()) {
9346 DCHECK_EQ(boot_image_offset, IntrinsicVisitor::IntegerValueOfInfo::kInvalidReference);
9347 // Load the class the same way as for HLoadClass::LoadKind::kBootImageLinkTimePcRelative.
9348 MethodReference target_method = invoke->GetTargetMethod();
9349 dex::TypeIndex type_idx = target_method.dex_file->GetMethodId(target_method.index).class_idx_;
9350 PcRelativePatchInfo* labels = NewBootImageTypePatch(*target_method.dex_file, type_idx);
9351 EmitMovwMovtPlaceholder(labels, argument);
9352 } else {
9353 LoadBootImageAddress(argument, boot_image_offset);
9354 }
9355 InvokeRuntime(kQuickAllocObjectInitialized, invoke, invoke->GetDexPc());
9356 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
9357}
9358
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009359template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
Artem Serovd4cc5b22016-11-04 11:19:09 +00009360inline void CodeGeneratorARMVIXL::EmitPcRelativeLinkerPatches(
9361 const ArenaDeque<PcRelativePatchInfo>& infos,
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009362 ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009363 for (const PcRelativePatchInfo& info : infos) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009364 const DexFile* dex_file = info.target_dex_file;
Artem Serovd4cc5b22016-11-04 11:19:09 +00009365 size_t offset_or_index = info.offset_or_index;
9366 DCHECK(info.add_pc_label.IsBound());
9367 uint32_t add_pc_offset = dchecked_integral_cast<uint32_t>(info.add_pc_label.GetLocation());
9368 // Add MOVW patch.
9369 DCHECK(info.movw_label.IsBound());
9370 uint32_t movw_offset = dchecked_integral_cast<uint32_t>(info.movw_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009371 linker_patches->push_back(Factory(movw_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009372 // Add MOVT patch.
9373 DCHECK(info.movt_label.IsBound());
9374 uint32_t movt_offset = dchecked_integral_cast<uint32_t>(info.movt_label.GetLocation());
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009375 linker_patches->push_back(Factory(movt_offset, dex_file, add_pc_offset, offset_or_index));
Artem Serovd4cc5b22016-11-04 11:19:09 +00009376 }
9377}
9378
Vladimir Marko6fd16062018-06-26 11:02:04 +01009379template <linker::LinkerPatch (*Factory)(size_t, uint32_t, uint32_t)>
9380linker::LinkerPatch NoDexFileAdapter(size_t literal_offset,
9381 const DexFile* target_dex_file,
9382 uint32_t pc_insn_offset,
9383 uint32_t boot_image_offset) {
9384 DCHECK(target_dex_file == nullptr); // Unused for these patches, should be null.
9385 return Factory(literal_offset, pc_insn_offset, boot_image_offset);
Vladimir Markob066d432018-01-03 13:14:37 +00009386}
9387
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009388void CodeGeneratorARMVIXL::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
Artem Serovd4cc5b22016-11-04 11:19:09 +00009389 DCHECK(linker_patches->empty());
9390 size_t size =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009391 /* MOVW+MOVT for each entry */ 2u * boot_image_method_patches_.size() +
Vladimir Marko0eb882b2017-05-15 13:39:18 +01009392 /* MOVW+MOVT for each entry */ 2u * method_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009393 /* MOVW+MOVT for each entry */ 2u * boot_image_type_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009394 /* MOVW+MOVT for each entry */ 2u * type_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009395 /* MOVW+MOVT for each entry */ 2u * boot_image_string_patches_.size() +
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01009396 /* MOVW+MOVT for each entry */ 2u * string_bss_entry_patches_.size() +
Vladimir Marko2d06e022019-07-08 15:45:19 +01009397 /* MOVW+MOVT for each entry */ 2u * boot_image_other_patches_.size() +
Vladimir Markof6675082019-05-17 12:05:28 +01009398 call_entrypoint_patches_.size() +
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009399 baker_read_barrier_patches_.size();
Artem Serovd4cc5b22016-11-04 11:19:09 +00009400 linker_patches->reserve(size);
Vladimir Marko44ca0752019-07-29 10:18:25 +01009401 if (GetCompilerOptions().IsBootImage() || GetCompilerOptions().IsBootImageExtension()) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009402 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009403 boot_image_method_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009404 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009405 boot_image_type_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009406 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00009407 boot_image_string_patches_, linker_patches);
Vladimir Marko65979462017-05-19 17:25:12 +01009408 } else {
Vladimir Marko2d06e022019-07-08 15:45:19 +01009409 DCHECK(boot_image_method_patches_.empty());
Vladimir Markoe47f60c2018-02-21 13:43:28 +00009410 DCHECK(boot_image_type_patches_.empty());
9411 DCHECK(boot_image_string_patches_.empty());
Vladimir Marko2d06e022019-07-08 15:45:19 +01009412 }
9413 if (GetCompilerOptions().IsBootImage()) {
9414 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::IntrinsicReferencePatch>>(
9415 boot_image_other_patches_, linker_patches);
9416 } else {
9417 EmitPcRelativeLinkerPatches<NoDexFileAdapter<linker::LinkerPatch::DataBimgRelRoPatch>>(
9418 boot_image_other_patches_, linker_patches);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009419 }
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009420 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
9421 method_bss_entry_patches_, linker_patches);
9422 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
9423 type_bss_entry_patches_, linker_patches);
9424 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
9425 string_bss_entry_patches_, linker_patches);
Vladimir Markof6675082019-05-17 12:05:28 +01009426 for (const PatchInfo<vixl32::Label>& info : call_entrypoint_patches_) {
9427 DCHECK(info.target_dex_file == nullptr);
9428 linker_patches->push_back(linker::LinkerPatch::CallEntrypointPatch(
9429 info.label.GetLocation(), info.offset_or_index));
9430 }
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009431 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01009432 linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch(
9433 info.label.GetLocation(), info.custom_data));
Vladimir Markoeee1c0e2017-04-21 17:58:41 +01009434 }
Vladimir Marko1998cd02017-01-13 13:02:58 +00009435 DCHECK_EQ(size, linker_patches->size());
Artem Serovc5fcb442016-12-02 19:19:58 +00009436}
9437
Vladimir Markoca1e0382018-04-11 09:58:41 +00009438bool CodeGeneratorARMVIXL::NeedsThunkCode(const linker::LinkerPatch& patch) const {
Vladimir Markof6675082019-05-17 12:05:28 +01009439 return patch.GetType() == linker::LinkerPatch::Type::kCallEntrypoint ||
9440 patch.GetType() == linker::LinkerPatch::Type::kBakerReadBarrierBranch ||
Vladimir Markoca1e0382018-04-11 09:58:41 +00009441 patch.GetType() == linker::LinkerPatch::Type::kCallRelative;
9442}
9443
9444void CodeGeneratorARMVIXL::EmitThunkCode(const linker::LinkerPatch& patch,
9445 /*out*/ ArenaVector<uint8_t>* code,
9446 /*out*/ std::string* debug_name) {
9447 arm::ArmVIXLAssembler assembler(GetGraph()->GetAllocator());
9448 switch (patch.GetType()) {
Vladimir Markof6675082019-05-17 12:05:28 +01009449 case linker::LinkerPatch::Type::kCallRelative: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009450 // The thunk just uses the entry point in the ArtMethod. This works even for calls
9451 // to the generic JNI and interpreter trampolines.
Vladimir Markof6675082019-05-17 12:05:28 +01009452 MemberOffset offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArmPointerSize);
9453 assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, vixl32::r0, offset.Int32Value());
Vladimir Markoca1e0382018-04-11 09:58:41 +00009454 assembler.GetVIXLAssembler()->Bkpt(0);
9455 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
9456 *debug_name = "MethodCallThunk";
9457 }
9458 break;
Vladimir Markof6675082019-05-17 12:05:28 +01009459 }
9460 case linker::LinkerPatch::Type::kCallEntrypoint: {
9461 assembler.LoadFromOffset(arm::kLoadWord, vixl32::pc, tr, patch.EntrypointOffset());
9462 assembler.GetVIXLAssembler()->Bkpt(0);
9463 if (GetCompilerOptions().GenerateAnyDebugInfo()) {
9464 *debug_name = "EntrypointCallThunk_" + std::to_string(patch.EntrypointOffset());
9465 }
9466 break;
9467 }
9468 case linker::LinkerPatch::Type::kBakerReadBarrierBranch: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009469 DCHECK_EQ(patch.GetBakerCustomValue2(), 0u);
9470 CompileBakerReadBarrierThunk(assembler, patch.GetBakerCustomValue1(), debug_name);
9471 break;
Vladimir Markof6675082019-05-17 12:05:28 +01009472 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00009473 default:
9474 LOG(FATAL) << "Unexpected patch type " << patch.GetType();
9475 UNREACHABLE();
9476 }
9477
9478 // Ensure we emit the literal pool if any.
9479 assembler.FinalizeCode();
9480 code->resize(assembler.CodeSize());
9481 MemoryRegion code_region(code->data(), code->size());
9482 assembler.FinalizeInstructions(code_region);
9483}
9484
Artem Serovc5fcb442016-12-02 19:19:58 +00009485VIXLUInt32Literal* CodeGeneratorARMVIXL::DeduplicateUint32Literal(
9486 uint32_t value,
9487 Uint32ToLiteralMap* map) {
9488 return map->GetOrCreate(
9489 value,
9490 [this, value]() {
Andreas Gampe3db70682018-12-26 15:12:03 -08009491 return GetAssembler()->CreateLiteralDestroyedWithPool<uint32_t>(/* value= */ value);
Artem Serovc5fcb442016-12-02 19:19:58 +00009492 });
9493}
9494
Artem Serov2bbc9532016-10-21 11:51:50 +01009495void LocationsBuilderARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9496 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009497 new (GetGraph()->GetAllocator()) LocationSummary(instr, LocationSummary::kNoCall);
Artem Serov2bbc9532016-10-21 11:51:50 +01009498 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
9499 Location::RequiresRegister());
9500 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
9501 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
9502 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
9503}
9504
9505void InstructionCodeGeneratorARMVIXL::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
9506 vixl32::Register res = OutputRegister(instr);
9507 vixl32::Register accumulator =
9508 InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
9509 vixl32::Register mul_left =
9510 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
9511 vixl32::Register mul_right =
9512 InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
9513
9514 if (instr->GetOpKind() == HInstruction::kAdd) {
9515 __ Mla(res, mul_left, mul_right, accumulator);
9516 } else {
9517 __ Mls(res, mul_left, mul_right, accumulator);
9518 }
9519}
9520
Artem Serov551b28f2016-10-18 19:11:30 +01009521void LocationsBuilderARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9522 // Nothing to do, this should be removed during prepare for register allocator.
9523 LOG(FATAL) << "Unreachable";
9524}
9525
9526void InstructionCodeGeneratorARMVIXL::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
9527 // Nothing to do, this should be removed during prepare for register allocator.
9528 LOG(FATAL) << "Unreachable";
9529}
9530
9531// Simple implementation of packed switch - generate cascaded compare/jumps.
9532void LocationsBuilderARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9533 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009534 new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
Artem Serov551b28f2016-10-18 19:11:30 +01009535 locations->SetInAt(0, Location::RequiresRegister());
9536 if (switch_instr->GetNumEntries() > kPackedSwitchCompareJumpThreshold &&
9537 codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9538 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the table base.
9539 if (switch_instr->GetStartValue() != 0) {
9540 locations->AddTemp(Location::RequiresRegister()); // We need a temp for the bias.
9541 }
9542 }
9543}
9544
9545// TODO(VIXL): Investigate and reach the parity with old arm codegen.
9546void InstructionCodeGeneratorARMVIXL::VisitPackedSwitch(HPackedSwitch* switch_instr) {
9547 int32_t lower_bound = switch_instr->GetStartValue();
9548 uint32_t num_entries = switch_instr->GetNumEntries();
9549 LocationSummary* locations = switch_instr->GetLocations();
9550 vixl32::Register value_reg = InputRegisterAt(switch_instr, 0);
9551 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
9552
9553 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
9554 !codegen_->GetAssembler()->GetVIXLAssembler()->IsUsingT32()) {
9555 // Create a series of compare/jumps.
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009556 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009557 vixl32::Register temp_reg = temps.Acquire();
9558 // Note: It is fine for the below AddConstantSetFlags() using IP register to temporarily store
9559 // the immediate, because IP is used as the destination register. For the other
9560 // AddConstantSetFlags() and GenerateCompareWithImmediate(), the immediate values are constant,
9561 // and they can be encoded in the instruction without making use of IP register.
9562 __ Adds(temp_reg, value_reg, -lower_bound);
9563
9564 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
9565 // Jump to successors[0] if value == lower_bound.
9566 __ B(eq, codegen_->GetLabelOf(successors[0]));
9567 int32_t last_index = 0;
9568 for (; num_entries - last_index > 2; last_index += 2) {
9569 __ Adds(temp_reg, temp_reg, -2);
9570 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
9571 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
9572 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
9573 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
9574 }
9575 if (num_entries - last_index == 2) {
9576 // The last missing case_value.
9577 __ Cmp(temp_reg, 1);
9578 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
9579 }
9580
9581 // And the default for any other value.
9582 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
9583 __ B(codegen_->GetLabelOf(default_block));
9584 }
9585 } else {
9586 // Create a table lookup.
9587 vixl32::Register table_base = RegisterFrom(locations->GetTemp(0));
9588
9589 JumpTableARMVIXL* jump_table = codegen_->CreateJumpTable(switch_instr);
9590
9591 // Remove the bias.
9592 vixl32::Register key_reg;
9593 if (lower_bound != 0) {
9594 key_reg = RegisterFrom(locations->GetTemp(1));
9595 __ Sub(key_reg, value_reg, lower_bound);
9596 } else {
9597 key_reg = value_reg;
9598 }
9599
9600 // Check whether the value is in the table, jump to default block if not.
9601 __ Cmp(key_reg, num_entries - 1);
9602 __ B(hi, codegen_->GetLabelOf(default_block));
9603
Anton Kirilovedb2ac32016-11-30 15:14:10 +00009604 UseScratchRegisterScope temps(GetVIXLAssembler());
Artem Serov551b28f2016-10-18 19:11:30 +01009605 vixl32::Register jump_offset = temps.Acquire();
9606
9607 // Load jump offset from the table.
Scott Wakeling86e9d262017-01-18 15:59:24 +00009608 {
9609 const size_t jump_size = switch_instr->GetNumEntries() * sizeof(int32_t);
9610 ExactAssemblyScope aas(GetVIXLAssembler(),
9611 (vixl32::kMaxInstructionSizeInBytes * 4) + jump_size,
9612 CodeBufferCheckScope::kMaximumSize);
9613 __ adr(table_base, jump_table->GetTableStartLabel());
9614 __ ldr(jump_offset, MemOperand(table_base, key_reg, vixl32::LSL, 2));
Artem Serov551b28f2016-10-18 19:11:30 +01009615
Scott Wakeling86e9d262017-01-18 15:59:24 +00009616 // Jump to target block by branching to table_base(pc related) + offset.
9617 vixl32::Register target_address = table_base;
9618 __ add(target_address, table_base, jump_offset);
9619 __ bx(target_address);
Artem Serov09a940d2016-11-11 16:15:11 +00009620
Scott Wakeling86e9d262017-01-18 15:59:24 +00009621 jump_table->EmitTable(codegen_);
9622 }
Artem Serov551b28f2016-10-18 19:11:30 +01009623 }
9624}
9625
Artem Serov02d37832016-10-25 15:25:33 +01009626// Copy the result of a call into the given target.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009627void CodeGeneratorARMVIXL::MoveFromReturnRegister(Location trg, DataType::Type type) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009628 if (!trg.IsValid()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009629 DCHECK_EQ(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009630 return;
9631 }
9632
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009633 DCHECK_NE(type, DataType::Type::kVoid);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009634
Artem Serovd4cc5b22016-11-04 11:19:09 +00009635 Location return_loc = InvokeDexCallingConventionVisitorARMVIXL().GetReturnLocation(type);
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009636 if (return_loc.Equals(trg)) {
9637 return;
9638 }
9639
9640 // TODO: Consider pairs in the parallel move resolver, then this could be nicely merged
9641 // with the last branch.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009642 if (type == DataType::Type::kInt64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009643 TODO_VIXL32(FATAL);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01009644 } else if (type == DataType::Type::kFloat64) {
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009645 TODO_VIXL32(FATAL);
9646 } else {
9647 // Let the parallel move resolver take care of all of this.
Vladimir Markoca6fff82017-10-03 14:49:14 +01009648 HParallelMove parallel_move(GetGraph()->GetAllocator());
Anton Kirilove28d9ae2016-10-25 18:17:23 +01009649 parallel_move.AddMove(return_loc, trg, type, nullptr);
9650 GetMoveResolver()->EmitNativeCode(&parallel_move);
9651 }
Scott Wakelinga7812ae2016-10-17 10:03:36 +01009652}
Scott Wakelingfe885462016-09-22 10:24:38 +01009653
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009654void LocationsBuilderARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9655 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01009656 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009657 locations->SetInAt(0, Location::RequiresRegister());
9658 locations->SetOut(Location::RequiresRegister());
Artem Serov551b28f2016-10-18 19:11:30 +01009659}
9660
xueliang.zhong8d2c4592016-11-23 17:05:25 +00009661void InstructionCodeGeneratorARMVIXL::VisitClassTableGet(HClassTableGet* instruction) {
9662 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
9663 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
9664 instruction->GetIndex(), kArmPointerSize).SizeValue();
9665 GetAssembler()->LoadFromOffset(kLoadWord,
9666 OutputRegister(instruction),
9667 InputRegisterAt(instruction, 0),
9668 method_offset);
9669 } else {
9670 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
9671 instruction->GetIndex(), kArmPointerSize));
9672 GetAssembler()->LoadFromOffset(kLoadWord,
9673 OutputRegister(instruction),
9674 InputRegisterAt(instruction, 0),
9675 mirror::Class::ImtPtrOffset(kArmPointerSize).Uint32Value());
9676 GetAssembler()->LoadFromOffset(kLoadWord,
9677 OutputRegister(instruction),
9678 OutputRegister(instruction),
9679 method_offset);
9680 }
Artem Serov551b28f2016-10-18 19:11:30 +01009681}
9682
Artem Serovc5fcb442016-12-02 19:19:58 +00009683static void PatchJitRootUse(uint8_t* code,
9684 const uint8_t* roots_data,
9685 VIXLUInt32Literal* literal,
9686 uint64_t index_in_table) {
9687 DCHECK(literal->IsBound());
9688 uint32_t literal_offset = literal->GetLocation();
9689 uintptr_t address =
9690 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
9691 uint8_t* data = code + literal_offset;
9692 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
9693}
9694
9695void CodeGeneratorARMVIXL::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
9696 for (const auto& entry : jit_string_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009697 const StringReference& string_reference = entry.first;
9698 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009699 uint64_t index_in_table = GetJitStringRootIndex(string_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009700 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009701 }
9702 for (const auto& entry : jit_class_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009703 const TypeReference& type_reference = entry.first;
9704 VIXLUInt32Literal* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01009705 uint64_t index_in_table = GetJitClassRootIndex(type_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01009706 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Artem Serovc5fcb442016-12-02 19:19:58 +00009707 }
9708}
9709
Artem Serovd4cc5b22016-11-04 11:19:09 +00009710void CodeGeneratorARMVIXL::EmitMovwMovtPlaceholder(
9711 CodeGeneratorARMVIXL::PcRelativePatchInfo* labels,
9712 vixl32::Register out) {
Artem Serov0fb37192016-12-06 18:13:40 +00009713 ExactAssemblyScope aas(GetVIXLAssembler(),
9714 3 * vixl32::kMaxInstructionSizeInBytes,
9715 CodeBufferCheckScope::kMaximumSize);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009716 // TODO(VIXL): Think about using mov instead of movw.
9717 __ bind(&labels->movw_label);
Andreas Gampe3db70682018-12-26 15:12:03 -08009718 __ movw(out, /* operand= */ 0u);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009719 __ bind(&labels->movt_label);
Andreas Gampe3db70682018-12-26 15:12:03 -08009720 __ movt(out, /* operand= */ 0u);
Artem Serovd4cc5b22016-11-04 11:19:09 +00009721 __ bind(&labels->add_pc_label);
9722 __ add(out, out, pc);
9723}
9724
Scott Wakelingfe885462016-09-22 10:24:38 +01009725#undef __
9726#undef QUICK_ENTRY_POINT
9727#undef TODO_VIXL32
9728
Vladimir Markoca1e0382018-04-11 09:58:41 +00009729#define __ assembler.GetVIXLAssembler()->
9730
9731static void EmitGrayCheckAndFastPath(ArmVIXLAssembler& assembler,
9732 vixl32::Register base_reg,
9733 vixl32::MemOperand& lock_word,
9734 vixl32::Label* slow_path,
Vladimir Marko7a695052018-04-12 10:26:50 +01009735 int32_t raw_ldr_offset,
9736 vixl32::Label* throw_npe = nullptr) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009737 // Load the lock word containing the rb_state.
9738 __ Ldr(ip, lock_word);
9739 // Given the numeric representation, it's enough to check the low bit of the rb_state.
Roland Levillain14e5a292018-06-28 12:00:56 +01009740 static_assert(ReadBarrier::NonGrayState() == 0, "Expecting non-gray to have value 0");
Vladimir Markoca1e0382018-04-11 09:58:41 +00009741 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
9742 __ Tst(ip, Operand(LockWord::kReadBarrierStateMaskShifted));
Andreas Gampe3db70682018-12-26 15:12:03 -08009743 __ B(ne, slow_path, /* is_far_target= */ false);
Vladimir Marko7a695052018-04-12 10:26:50 +01009744 // To throw NPE, we return to the fast path; the artificial dependence below does not matter.
9745 if (throw_npe != nullptr) {
9746 __ Bind(throw_npe);
9747 }
Vladimir Markoca1e0382018-04-11 09:58:41 +00009748 __ Add(lr, lr, raw_ldr_offset);
9749 // Introduce a dependency on the lock_word including rb_state,
9750 // to prevent load-load reordering, and without using
9751 // a memory barrier (which would be more expensive).
9752 __ Add(base_reg, base_reg, Operand(ip, LSR, 32));
9753 __ Bx(lr); // And return back to the function.
9754 // Note: The fake dependency is unnecessary for the slow path.
9755}
9756
9757// Load the read barrier introspection entrypoint in register `entrypoint`
Vladimir Markodcd117e2018-04-19 11:54:00 +01009758static vixl32::Register LoadReadBarrierMarkIntrospectionEntrypoint(ArmVIXLAssembler& assembler) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009759 // The register where the read barrier introspection entrypoint is loaded
Vladimir Markodcd117e2018-04-19 11:54:00 +01009760 // is the marking register. We clobber it here and the entrypoint restores it to 1.
9761 vixl32::Register entrypoint = mr;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009762 // entrypoint = Thread::Current()->pReadBarrierMarkReg12, i.e. pReadBarrierMarkIntrospection.
9763 DCHECK_EQ(ip.GetCode(), 12u);
9764 const int32_t entry_point_offset =
9765 Thread::ReadBarrierMarkEntryPointsOffset<kArmPointerSize>(ip.GetCode());
9766 __ Ldr(entrypoint, MemOperand(tr, entry_point_offset));
Vladimir Markodcd117e2018-04-19 11:54:00 +01009767 return entrypoint;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009768}
9769
9770void CodeGeneratorARMVIXL::CompileBakerReadBarrierThunk(ArmVIXLAssembler& assembler,
9771 uint32_t encoded_data,
9772 /*out*/ std::string* debug_name) {
9773 BakerReadBarrierKind kind = BakerReadBarrierKindField::Decode(encoded_data);
9774 switch (kind) {
9775 case BakerReadBarrierKind::kField: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009776 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9777 CheckValidReg(base_reg.GetCode());
9778 vixl32::Register holder_reg(BakerReadBarrierSecondRegField::Decode(encoded_data));
9779 CheckValidReg(holder_reg.GetCode());
9780 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9781 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9782 temps.Exclude(ip);
Roland Levillain988c3912019-09-25 19:33:35 +01009783 // In the case of a field load, if `base_reg` differs from
9784 // `holder_reg`, the offset was too large and we must have emitted (during the construction
9785 // of the HIR graph, see `art::HInstructionBuilder::BuildInstanceFieldAccess`) and preserved
9786 // (see `art::PrepareForRegisterAllocation::VisitNullCheck`) an explicit null check before
9787 // the load. Otherwise, for implicit null checks, we need to null-check the holder as we do
9788 // not necessarily do that check before going to the thunk.
Vladimir Marko7a695052018-04-12 10:26:50 +01009789 vixl32::Label throw_npe_label;
9790 vixl32::Label* throw_npe = nullptr;
9791 if (GetCompilerOptions().GetImplicitNullChecks() && holder_reg.Is(base_reg)) {
9792 throw_npe = &throw_npe_label;
Andreas Gampe3db70682018-12-26 15:12:03 -08009793 __ CompareAndBranchIfZero(holder_reg, throw_npe, /* is_far_target= */ false);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009794 }
Vladimir Marko7a695052018-04-12 10:26:50 +01009795 // Check if the holder is gray and, if not, add fake dependency to the base register
9796 // and return to the LDR instruction to load the reference. Otherwise, use introspection
9797 // to load the reference and call the entrypoint that performs further checks on the
9798 // reference and marks it if needed.
Vladimir Markoca1e0382018-04-11 09:58:41 +00009799 vixl32::Label slow_path;
9800 MemOperand lock_word(holder_reg, mirror::Object::MonitorOffset().Int32Value());
9801 const int32_t raw_ldr_offset = (width == BakerReadBarrierWidth::kWide)
9802 ? BAKER_MARK_INTROSPECTION_FIELD_LDR_WIDE_OFFSET
9803 : BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_OFFSET;
Vladimir Marko7a695052018-04-12 10:26:50 +01009804 EmitGrayCheckAndFastPath(
9805 assembler, base_reg, lock_word, &slow_path, raw_ldr_offset, throw_npe);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009806 __ Bind(&slow_path);
9807 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
9808 raw_ldr_offset;
Vladimir Markodcd117e2018-04-19 11:54:00 +01009809 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009810 if (width == BakerReadBarrierWidth::kWide) {
9811 MemOperand ldr_half_address(lr, ldr_offset + 2);
9812 __ Ldrh(ip, ldr_half_address); // Load the LDR immediate half-word with "Rt | imm12".
9813 __ Ubfx(ip, ip, 0, 12); // Extract the offset imm12.
9814 __ Ldr(ip, MemOperand(base_reg, ip)); // Load the reference.
9815 } else {
9816 MemOperand ldr_address(lr, ldr_offset);
9817 __ Ldrh(ip, ldr_address); // Load the LDR immediate, encoding T1.
9818 __ Add(ep_reg, // Adjust the entrypoint address to the entrypoint
9819 ep_reg, // for narrow LDR.
9820 Operand(BAKER_MARK_INTROSPECTION_FIELD_LDR_NARROW_ENTRYPOINT_OFFSET));
9821 __ Ubfx(ip, ip, 6, 5); // Extract the imm5, i.e. offset / 4.
9822 __ Ldr(ip, MemOperand(base_reg, ip, LSL, 2)); // Load the reference.
9823 }
9824 // Do not unpoison. With heap poisoning enabled, the entrypoint expects a poisoned reference.
9825 __ Bx(ep_reg); // Jump to the entrypoint.
Vladimir Markoca1e0382018-04-11 09:58:41 +00009826 break;
9827 }
9828 case BakerReadBarrierKind::kArray: {
9829 vixl32::Register base_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9830 CheckValidReg(base_reg.GetCode());
9831 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9832 BakerReadBarrierSecondRegField::Decode(encoded_data));
9833 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
9834 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9835 temps.Exclude(ip);
9836 vixl32::Label slow_path;
9837 int32_t data_offset =
9838 mirror::Array::DataOffset(Primitive::ComponentSize(Primitive::kPrimNot)).Int32Value();
9839 MemOperand lock_word(base_reg, mirror::Object::MonitorOffset().Int32Value() - data_offset);
9840 DCHECK_LT(lock_word.GetOffsetImmediate(), 0);
9841 const int32_t raw_ldr_offset = BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET;
9842 EmitGrayCheckAndFastPath(assembler, base_reg, lock_word, &slow_path, raw_ldr_offset);
9843 __ Bind(&slow_path);
9844 const int32_t ldr_offset = /* Thumb state adjustment (LR contains Thumb state). */ -1 +
9845 raw_ldr_offset;
9846 MemOperand ldr_address(lr, ldr_offset + 2);
9847 __ Ldrb(ip, ldr_address); // Load the LDR (register) byte with "00 | imm2 | Rm",
9848 // i.e. Rm+32 because the scale in imm2 is 2.
Vladimir Markodcd117e2018-04-19 11:54:00 +01009849 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009850 __ Bfi(ep_reg, ip, 3, 6); // Insert ip to the entrypoint address to create
9851 // a switch case target based on the index register.
9852 __ Mov(ip, base_reg); // Move the base register to ip0.
9853 __ Bx(ep_reg); // Jump to the entrypoint's array switch case.
9854 break;
9855 }
Vladimir Markod887ed82018-08-14 13:52:12 +00009856 case BakerReadBarrierKind::kGcRoot:
9857 case BakerReadBarrierKind::kUnsafeCas: {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009858 // Check if the reference needs to be marked and if so (i.e. not null, not marked yet
9859 // and it does not have a forwarding address), call the correct introspection entrypoint;
9860 // otherwise return the reference (or the extracted forwarding address).
9861 // There is no gray bit check for GC roots.
9862 vixl32::Register root_reg(BakerReadBarrierFirstRegField::Decode(encoded_data));
9863 CheckValidReg(root_reg.GetCode());
9864 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9865 BakerReadBarrierSecondRegField::Decode(encoded_data));
9866 BakerReadBarrierWidth width = BakerReadBarrierWidthField::Decode(encoded_data);
9867 UseScratchRegisterScope temps(assembler.GetVIXLAssembler());
9868 temps.Exclude(ip);
9869 vixl32::Label return_label, not_marked, forwarding_address;
Andreas Gampe3db70682018-12-26 15:12:03 -08009870 __ CompareAndBranchIfZero(root_reg, &return_label, /* is_far_target= */ false);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009871 MemOperand lock_word(root_reg, mirror::Object::MonitorOffset().Int32Value());
9872 __ Ldr(ip, lock_word);
9873 __ Tst(ip, LockWord::kMarkBitStateMaskShifted);
9874 __ B(eq, &not_marked);
9875 __ Bind(&return_label);
9876 __ Bx(lr);
9877 __ Bind(&not_marked);
9878 static_assert(LockWord::kStateShift == 30 && LockWord::kStateForwardingAddress == 3,
9879 "To use 'CMP ip, #modified-immediate; BHS', we need the lock word state in "
9880 " the highest bits and the 'forwarding address' state to have all bits set");
9881 __ Cmp(ip, Operand(0xc0000000));
9882 __ B(hs, &forwarding_address);
Vladimir Markodcd117e2018-04-19 11:54:00 +01009883 vixl32::Register ep_reg = LoadReadBarrierMarkIntrospectionEntrypoint(assembler);
Vladimir Markoca1e0382018-04-11 09:58:41 +00009884 // Adjust the art_quick_read_barrier_mark_introspection address in kBakerCcEntrypointRegister
Vladimir Markod887ed82018-08-14 13:52:12 +00009885 // to one of art_quick_read_barrier_mark_introspection_{gc_roots_{wide,narrow},unsafe_cas}.
9886 DCHECK(kind != BakerReadBarrierKind::kUnsafeCas || width == BakerReadBarrierWidth::kWide);
9887 int32_t entrypoint_offset =
9888 (kind == BakerReadBarrierKind::kGcRoot)
9889 ? (width == BakerReadBarrierWidth::kWide)
9890 ? BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_WIDE_ENTRYPOINT_OFFSET
9891 : BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_NARROW_ENTRYPOINT_OFFSET
9892 : BAKER_MARK_INTROSPECTION_UNSAFE_CAS_ENTRYPOINT_OFFSET;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009893 __ Add(ep_reg, ep_reg, Operand(entrypoint_offset));
9894 __ Mov(ip, root_reg);
9895 __ Bx(ep_reg);
9896 __ Bind(&forwarding_address);
9897 __ Lsl(root_reg, ip, LockWord::kForwardingAddressShift);
9898 __ Bx(lr);
9899 break;
9900 }
9901 default:
9902 LOG(FATAL) << "Unexpected kind: " << static_cast<uint32_t>(kind);
9903 UNREACHABLE();
9904 }
9905
Vladimir Marko966b46f2018-08-03 10:20:19 +00009906 // For JIT, the slow path is considered part of the compiled method,
Vladimir Markof91fc122020-05-13 09:21:00 +01009907 // so JIT should pass null as `debug_name`.
Vladimir Marko695348f2020-05-19 14:42:02 +01009908 DCHECK(!GetCompilerOptions().IsJitCompiler() || debug_name == nullptr);
Vladimir Marko966b46f2018-08-03 10:20:19 +00009909 if (debug_name != nullptr && GetCompilerOptions().GenerateAnyDebugInfo()) {
Vladimir Markoca1e0382018-04-11 09:58:41 +00009910 std::ostringstream oss;
9911 oss << "BakerReadBarrierThunk";
9912 switch (kind) {
9913 case BakerReadBarrierKind::kField:
9914 oss << "Field";
9915 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
9916 oss << "Wide";
9917 }
9918 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data)
9919 << "_r" << BakerReadBarrierSecondRegField::Decode(encoded_data);
9920 break;
9921 case BakerReadBarrierKind::kArray:
9922 oss << "Array_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
9923 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9924 BakerReadBarrierSecondRegField::Decode(encoded_data));
9925 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
9926 break;
9927 case BakerReadBarrierKind::kGcRoot:
9928 oss << "GcRoot";
9929 if (BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide) {
9930 oss << "Wide";
9931 }
9932 oss << "_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
9933 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9934 BakerReadBarrierSecondRegField::Decode(encoded_data));
9935 break;
Vladimir Markod887ed82018-08-14 13:52:12 +00009936 case BakerReadBarrierKind::kUnsafeCas:
9937 oss << "UnsafeCas_r" << BakerReadBarrierFirstRegField::Decode(encoded_data);
9938 DCHECK_EQ(kBakerReadBarrierInvalidEncodedReg,
9939 BakerReadBarrierSecondRegField::Decode(encoded_data));
9940 DCHECK(BakerReadBarrierWidthField::Decode(encoded_data) == BakerReadBarrierWidth::kWide);
9941 break;
Vladimir Markoca1e0382018-04-11 09:58:41 +00009942 }
9943 *debug_name = oss.str();
9944 }
9945}
9946
9947#undef __
9948
Scott Wakelingfe885462016-09-22 10:24:38 +01009949} // namespace arm
9950} // namespace art