blob: a024df8537f26e5a5f10212395c3f9e9b5c199e5 [file] [log] [blame]
Alexandre Rames5319def2014-10-23 10:03:10 +01001/*
2 * Copyright (C) 2014 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_arm64.h"
18
Vladimir Markof4f2daa2017-03-20 18:26:59 +000019#include "arch/arm64/asm_support_arm64.h"
Serban Constantinescu579885a2015-02-22 20:51:33 +000020#include "arch/arm64/instruction_set_features_arm64.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070021#include "art_method.h"
Andreas Gampe5678db52017-06-08 14:11:18 -070022#include "base/bit_utils.h"
23#include "base/bit_utils_iterator.h"
Vladimir Marko94ec2db2017-09-06 17:21:03 +010024#include "class_table.h"
Zheng Xuc6667102015-05-15 16:08:45 +080025#include "code_generator_utils.h"
Vladimir Marko58155012015-08-19 12:49:41 +000026#include "compiled_method.h"
Alexandre Rames5319def2014-10-23 10:03:10 +010027#include "entrypoints/quick/quick_entrypoints.h"
Andreas Gampe1cc7dba2014-12-17 18:43:01 -080028#include "entrypoints/quick/quick_entrypoints_enum.h"
Alexandre Rames5319def2014-10-23 10:03:10 +010029#include "gc/accounting/card_table.h"
Andreas Gampe09659c22017-09-18 18:23:32 -070030#include "heap_poisoning.h"
Andreas Gampe878d58c2015-01-15 23:24:00 -080031#include "intrinsics.h"
32#include "intrinsics_arm64.h"
Vladimir Markof4f2daa2017-03-20 18:26:59 +000033#include "linker/arm64/relative_patcher_arm64.h"
Vladimir Markod8dbc8d2017-09-20 13:37:47 +010034#include "linker/linker_patch.h"
Andreas Gampe8cf9cb32017-07-19 09:28:38 -070035#include "lock_word.h"
Alexandre Rames5319def2014-10-23 10:03:10 +010036#include "mirror/array-inl.h"
Mathieu Chartiere401d142015-04-22 13:56:20 -070037#include "mirror/class-inl.h"
Calin Juravlecd6dffe2015-01-08 17:35:35 +000038#include "offsets.h"
Alexandre Rames5319def2014-10-23 10:03:10 +010039#include "thread.h"
40#include "utils/arm64/assembler_arm64.h"
41#include "utils/assembler.h"
42#include "utils/stack_checks.h"
43
Scott Wakeling97c72b72016-06-24 16:19:36 +010044using namespace vixl::aarch64; // NOLINT(build/namespaces)
Artem Serov914d7a82017-02-07 14:33:49 +000045using vixl::ExactAssemblyScope;
46using vixl::CodeBufferCheckScope;
47using vixl::EmissionCheckScope;
Alexandre Rames5319def2014-10-23 10:03:10 +010048
49#ifdef __
50#error "ARM64 Codegen VIXL macro-assembler macro already defined."
51#endif
52
Alexandre Rames5319def2014-10-23 10:03:10 +010053namespace art {
54
Roland Levillain22ccc3a2015-11-24 13:10:05 +000055template<class MirrorType>
56class GcRoot;
57
Alexandre Rames5319def2014-10-23 10:03:10 +010058namespace arm64 {
59
Alexandre Ramesbe919d92016-08-23 18:33:36 +010060using helpers::ARM64EncodableConstantOrRegister;
61using helpers::ArtVixlRegCodeCoherentForRegSet;
Andreas Gampe878d58c2015-01-15 23:24:00 -080062using helpers::CPURegisterFrom;
63using helpers::DRegisterFrom;
64using helpers::FPRegisterFrom;
65using helpers::HeapOperand;
66using helpers::HeapOperandFrom;
67using helpers::InputCPURegisterAt;
Alexandre Ramesbe919d92016-08-23 18:33:36 +010068using helpers::InputCPURegisterOrZeroRegAt;
Andreas Gampe878d58c2015-01-15 23:24:00 -080069using helpers::InputFPRegisterAt;
Andreas Gampe878d58c2015-01-15 23:24:00 -080070using helpers::InputOperandAt;
Alexandre Ramesbe919d92016-08-23 18:33:36 +010071using helpers::InputRegisterAt;
Andreas Gampe878d58c2015-01-15 23:24:00 -080072using helpers::Int64ConstantFrom;
Alexandre Ramesbe919d92016-08-23 18:33:36 +010073using helpers::IsConstantZeroBitPattern;
Andreas Gampe878d58c2015-01-15 23:24:00 -080074using helpers::LocationFrom;
75using helpers::OperandFromMemOperand;
76using helpers::OutputCPURegister;
77using helpers::OutputFPRegister;
78using helpers::OutputRegister;
Artem Serovd4bccf12017-04-03 18:47:32 +010079using helpers::QRegisterFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080080using helpers::RegisterFrom;
Aart Bik1f8d51b2018-02-15 10:42:37 -080081using helpers::SRegisterFrom;
Andreas Gampe878d58c2015-01-15 23:24:00 -080082using helpers::StackOperandFrom;
83using helpers::VIXLRegCodeFromART;
84using helpers::WRegisterFrom;
85using helpers::XRegisterFrom;
86
Vladimir Markof3e0ee22015-12-17 15:23:13 +000087// The compare/jump sequence will generate about (1.5 * num_entries + 3) instructions. While jump
Zheng Xu3927c8b2015-11-18 17:46:25 +080088// table version generates 7 instructions and num_entries literals. Compare/jump sequence will
89// generates less code/data with a small num_entries.
Vladimir Markof3e0ee22015-12-17 15:23:13 +000090static constexpr uint32_t kPackedSwitchCompareJumpThreshold = 7;
Alexandre Rames5319def2014-10-23 10:03:10 +010091
Vladimir Markof4f2daa2017-03-20 18:26:59 +000092// Reference load (except object array loads) is using LDR Wt, [Xn, #offset] which can handle
93// offset < 16KiB. For offsets >= 16KiB, the load shall be emitted as two or more instructions.
94// For the Baker read barrier implementation using link-generated thunks we need to split
95// the offset explicitly.
96constexpr uint32_t kReferenceLoadMinFarOffset = 16 * KB;
97
98// Flags controlling the use of link-time generated thunks for Baker read barriers.
Vladimir Markod1ef8732017-04-18 13:55:13 +010099constexpr bool kBakerReadBarrierLinkTimeThunksEnableForFields = true;
Vladimir Marko66d691d2017-04-07 17:53:39 +0100100constexpr bool kBakerReadBarrierLinkTimeThunksEnableForArrays = true;
Vladimir Markod1ef8732017-04-18 13:55:13 +0100101constexpr bool kBakerReadBarrierLinkTimeThunksEnableForGcRoots = true;
Vladimir Markof4f2daa2017-03-20 18:26:59 +0000102
103// Some instructions have special requirements for a temporary, for example
104// LoadClass/kBssEntry and LoadString/kBssEntry for Baker read barrier require
105// temp that's not an R0 (to avoid an extra move) and Baker read barrier field
106// loads with large offsets need a fixed register to limit the number of link-time
107// thunks we generate. For these and similar cases, we want to reserve a specific
108// register that's neither callee-save nor an argument register. We choose x15.
109inline Location FixedTempLocation() {
110 return Location::RegisterLocation(x15.GetCode());
111}
112
Alexandre Rames5319def2014-10-23 10:03:10 +0100113inline Condition ARM64Condition(IfCondition cond) {
114 switch (cond) {
115 case kCondEQ: return eq;
116 case kCondNE: return ne;
117 case kCondLT: return lt;
118 case kCondLE: return le;
119 case kCondGT: return gt;
120 case kCondGE: return ge;
Aart Bike9f37602015-10-09 11:15:55 -0700121 case kCondB: return lo;
122 case kCondBE: return ls;
123 case kCondA: return hi;
124 case kCondAE: return hs;
Alexandre Rames5319def2014-10-23 10:03:10 +0100125 }
Roland Levillain7f63c522015-07-13 15:54:55 +0000126 LOG(FATAL) << "Unreachable";
127 UNREACHABLE();
Alexandre Rames5319def2014-10-23 10:03:10 +0100128}
129
Vladimir Markod6e069b2016-01-18 11:11:01 +0000130inline Condition ARM64FPCondition(IfCondition cond, bool gt_bias) {
131 // The ARM64 condition codes can express all the necessary branches, see the
132 // "Meaning (floating-point)" column in the table C1-1 in the ARMv8 reference manual.
133 // There is no dex instruction or HIR that would need the missing conditions
134 // "equal or unordered" or "not equal".
135 switch (cond) {
136 case kCondEQ: return eq;
137 case kCondNE: return ne /* unordered */;
138 case kCondLT: return gt_bias ? cc : lt /* unordered */;
139 case kCondLE: return gt_bias ? ls : le /* unordered */;
140 case kCondGT: return gt_bias ? hi /* unordered */ : gt;
141 case kCondGE: return gt_bias ? cs /* unordered */ : ge;
142 default:
143 LOG(FATAL) << "UNREACHABLE";
144 UNREACHABLE();
145 }
146}
147
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100148Location ARM64ReturnLocation(DataType::Type return_type) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +0000149 // Note that in practice, `LocationFrom(x0)` and `LocationFrom(w0)` create the
150 // same Location object, and so do `LocationFrom(d0)` and `LocationFrom(s0)`,
151 // but we use the exact registers for clarity.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100152 if (return_type == DataType::Type::kFloat32) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +0000153 return LocationFrom(s0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100154 } else if (return_type == DataType::Type::kFloat64) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +0000155 return LocationFrom(d0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100156 } else if (return_type == DataType::Type::kInt64) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +0000157 return LocationFrom(x0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100158 } else if (return_type == DataType::Type::kVoid) {
Nicolas Geoffray925e5622015-06-03 12:23:32 +0100159 return Location::NoLocation();
Alexandre Ramesa89086e2014-11-07 17:13:25 +0000160 } else {
161 return LocationFrom(w0);
162 }
163}
164
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100165Location InvokeRuntimeCallingConvention::GetReturnLocation(DataType::Type return_type) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +0000166 return ARM64ReturnLocation(return_type);
Alexandre Rames5319def2014-10-23 10:03:10 +0100167}
168
Roland Levillain7cbd27f2016-08-11 23:53:33 +0100169// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy.
170#define __ down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler()-> // NOLINT
Andreas Gampe542451c2016-07-26 09:02:02 -0700171#define QUICK_ENTRY_POINT(x) QUICK_ENTRYPOINT_OFFSET(kArm64PointerSize, x).Int32Value()
Alexandre Rames5319def2014-10-23 10:03:10 +0100172
Zheng Xuda403092015-04-24 17:35:39 +0800173// Calculate memory accessing operand for save/restore live registers.
174static void SaveRestoreLiveRegistersHelper(CodeGenerator* codegen,
Vladimir Marko804b03f2016-09-14 16:26:36 +0100175 LocationSummary* locations,
Zheng Xuda403092015-04-24 17:35:39 +0800176 int64_t spill_offset,
177 bool is_save) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100178 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
179 const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
180 DCHECK(ArtVixlRegCodeCoherentForRegSet(core_spills,
Zheng Xuda403092015-04-24 17:35:39 +0800181 codegen->GetNumberOfCoreRegisters(),
Vladimir Marko804b03f2016-09-14 16:26:36 +0100182 fp_spills,
Zheng Xuda403092015-04-24 17:35:39 +0800183 codegen->GetNumberOfFloatingPointRegisters()));
184
Vladimir Marko804b03f2016-09-14 16:26:36 +0100185 CPURegList core_list = CPURegList(CPURegister::kRegister, kXRegSize, core_spills);
Artem Serov7957d952017-04-04 15:44:09 +0100186 unsigned v_reg_size = codegen->GetGraph()->HasSIMD() ? kQRegSize : kDRegSize;
187 CPURegList fp_list = CPURegList(CPURegister::kVRegister, v_reg_size, fp_spills);
Zheng Xuda403092015-04-24 17:35:39 +0800188
189 MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen)->GetVIXLAssembler();
190 UseScratchRegisterScope temps(masm);
191
192 Register base = masm->StackPointer();
Scott Wakeling97c72b72016-06-24 16:19:36 +0100193 int64_t core_spill_size = core_list.GetTotalSizeInBytes();
194 int64_t fp_spill_size = fp_list.GetTotalSizeInBytes();
Zheng Xuda403092015-04-24 17:35:39 +0800195 int64_t reg_size = kXRegSizeInBytes;
196 int64_t max_ls_pair_offset = spill_offset + core_spill_size + fp_spill_size - 2 * reg_size;
197 uint32_t ls_access_size = WhichPowerOf2(reg_size);
Scott Wakeling97c72b72016-06-24 16:19:36 +0100198 if (((core_list.GetCount() > 1) || (fp_list.GetCount() > 1)) &&
Zheng Xuda403092015-04-24 17:35:39 +0800199 !masm->IsImmLSPair(max_ls_pair_offset, ls_access_size)) {
200 // If the offset does not fit in the instruction's immediate field, use an alternate register
201 // to compute the base address(float point registers spill base address).
202 Register new_base = temps.AcquireSameSizeAs(base);
203 __ Add(new_base, base, Operand(spill_offset + core_spill_size));
204 base = new_base;
205 spill_offset = -core_spill_size;
206 int64_t new_max_ls_pair_offset = fp_spill_size - 2 * reg_size;
207 DCHECK(masm->IsImmLSPair(spill_offset, ls_access_size));
208 DCHECK(masm->IsImmLSPair(new_max_ls_pair_offset, ls_access_size));
209 }
210
211 if (is_save) {
212 __ StoreCPURegList(core_list, MemOperand(base, spill_offset));
213 __ StoreCPURegList(fp_list, MemOperand(base, spill_offset + core_spill_size));
214 } else {
215 __ LoadCPURegList(core_list, MemOperand(base, spill_offset));
216 __ LoadCPURegList(fp_list, MemOperand(base, spill_offset + core_spill_size));
217 }
218}
219
220void SlowPathCodeARM64::SaveLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
Zheng Xuda403092015-04-24 17:35:39 +0800221 size_t stack_offset = codegen->GetFirstRegisterSlotInSlowPath();
Vladimir Marko804b03f2016-09-14 16:26:36 +0100222 const uint32_t core_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ true);
223 for (uint32_t i : LowToHighBits(core_spills)) {
224 // If the register holds an object, update the stack mask.
225 if (locations->RegisterContainsObject(i)) {
226 locations->SetStackBit(stack_offset / kVRegSize);
Zheng Xuda403092015-04-24 17:35:39 +0800227 }
Vladimir Marko804b03f2016-09-14 16:26:36 +0100228 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
229 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
230 saved_core_stack_offsets_[i] = stack_offset;
231 stack_offset += kXRegSizeInBytes;
Zheng Xuda403092015-04-24 17:35:39 +0800232 }
233
Vladimir Marko804b03f2016-09-14 16:26:36 +0100234 const uint32_t fp_spills = codegen->GetSlowPathSpills(locations, /* core_registers */ false);
235 for (uint32_t i : LowToHighBits(fp_spills)) {
236 DCHECK_LT(stack_offset, codegen->GetFrameSize() - codegen->FrameEntrySpillSize());
237 DCHECK_LT(i, kMaximumNumberOfExpectedRegisters);
238 saved_fpu_stack_offsets_[i] = stack_offset;
239 stack_offset += kDRegSizeInBytes;
Zheng Xuda403092015-04-24 17:35:39 +0800240 }
241
Vladimir Marko804b03f2016-09-14 16:26:36 +0100242 SaveRestoreLiveRegistersHelper(codegen,
243 locations,
Zheng Xuda403092015-04-24 17:35:39 +0800244 codegen->GetFirstRegisterSlotInSlowPath(), true /* is_save */);
245}
246
247void SlowPathCodeARM64::RestoreLiveRegisters(CodeGenerator* codegen, LocationSummary* locations) {
Vladimir Marko804b03f2016-09-14 16:26:36 +0100248 SaveRestoreLiveRegistersHelper(codegen,
249 locations,
Zheng Xuda403092015-04-24 17:35:39 +0800250 codegen->GetFirstRegisterSlotInSlowPath(), false /* is_save */);
251}
252
Alexandre Rames5319def2014-10-23 10:03:10 +0100253class BoundsCheckSlowPathARM64 : public SlowPathCodeARM64 {
254 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000255 explicit BoundsCheckSlowPathARM64(HBoundsCheck* instruction) : SlowPathCodeARM64(instruction) {}
Alexandre Rames5319def2014-10-23 10:03:10 +0100256
Alexandre Rames67555f72014-11-18 10:55:16 +0000257 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Serban Constantinescu5a6cc492015-08-13 15:20:25 +0100258 LocationSummary* locations = instruction_->GetLocations();
Alexandre Rames3e69f162014-12-10 10:36:50 +0000259 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
Serban Constantinescu5a6cc492015-08-13 15:20:25 +0100260
Alexandre Rames5319def2014-10-23 10:03:10 +0100261 __ Bind(GetEntryLabel());
David Brazdil77a48ae2015-09-15 12:34:04 +0000262 if (instruction_->CanThrowIntoCatchBlock()) {
263 // Live registers will be restored in the catch block if caught.
264 SaveLiveRegisters(codegen, instruction_->GetLocations());
265 }
Alexandre Rames3e69f162014-12-10 10:36:50 +0000266 // We're moving two locations to locations that could overlap, so we need a parallel
267 // move resolver.
268 InvokeRuntimeCallingConvention calling_convention;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100269 codegen->EmitParallelMoves(locations->InAt(0),
270 LocationFrom(calling_convention.GetRegisterAt(0)),
271 DataType::Type::kInt32,
272 locations->InAt(1),
273 LocationFrom(calling_convention.GetRegisterAt(1)),
274 DataType::Type::kInt32);
Serban Constantinescu22f81d32016-02-18 16:06:31 +0000275 QuickEntrypointEnum entrypoint = instruction_->AsBoundsCheck()->IsStringCharAt()
276 ? kQuickThrowStringBounds
277 : kQuickThrowArrayBounds;
278 arm64_codegen->InvokeRuntime(entrypoint, instruction_, instruction_->GetDexPc(), this);
Vladimir Marko87f3fcb2016-04-28 15:52:11 +0100279 CheckEntrypointTypes<kQuickThrowStringBounds, void, int32_t, int32_t>();
Andreas Gampe1cc7dba2014-12-17 18:43:01 -0800280 CheckEntrypointTypes<kQuickThrowArrayBounds, void, int32_t, int32_t>();
Alexandre Rames5319def2014-10-23 10:03:10 +0100281 }
282
Alexandre Rames8158f282015-08-07 10:26:17 +0100283 bool IsFatal() const OVERRIDE { return true; }
284
Alexandre Rames9931f312015-06-19 14:47:01 +0100285 const char* GetDescription() const OVERRIDE { return "BoundsCheckSlowPathARM64"; }
286
Alexandre Rames5319def2014-10-23 10:03:10 +0100287 private:
Alexandre Rames5319def2014-10-23 10:03:10 +0100288 DISALLOW_COPY_AND_ASSIGN(BoundsCheckSlowPathARM64);
289};
290
Alexandre Rames67555f72014-11-18 10:55:16 +0000291class DivZeroCheckSlowPathARM64 : public SlowPathCodeARM64 {
292 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000293 explicit DivZeroCheckSlowPathARM64(HDivZeroCheck* instruction) : SlowPathCodeARM64(instruction) {}
Alexandre Rames67555f72014-11-18 10:55:16 +0000294
295 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
296 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
297 __ Bind(GetEntryLabel());
Serban Constantinescu22f81d32016-02-18 16:06:31 +0000298 arm64_codegen->InvokeRuntime(kQuickThrowDivZero, instruction_, instruction_->GetDexPc(), this);
Andreas Gampe1cc7dba2014-12-17 18:43:01 -0800299 CheckEntrypointTypes<kQuickThrowDivZero, void, void>();
Alexandre Rames67555f72014-11-18 10:55:16 +0000300 }
301
Alexandre Rames8158f282015-08-07 10:26:17 +0100302 bool IsFatal() const OVERRIDE { return true; }
303
Alexandre Rames9931f312015-06-19 14:47:01 +0100304 const char* GetDescription() const OVERRIDE { return "DivZeroCheckSlowPathARM64"; }
305
Alexandre Rames67555f72014-11-18 10:55:16 +0000306 private:
Alexandre Rames67555f72014-11-18 10:55:16 +0000307 DISALLOW_COPY_AND_ASSIGN(DivZeroCheckSlowPathARM64);
308};
309
310class LoadClassSlowPathARM64 : public SlowPathCodeARM64 {
311 public:
312 LoadClassSlowPathARM64(HLoadClass* cls,
313 HInstruction* at,
314 uint32_t dex_pc,
Vladimir Markof3c52b42017-11-17 17:32:12 +0000315 bool do_clinit)
Vladimir Markoea4c1262017-02-06 19:59:33 +0000316 : SlowPathCodeARM64(at),
317 cls_(cls),
318 dex_pc_(dex_pc),
Vladimir Markof3c52b42017-11-17 17:32:12 +0000319 do_clinit_(do_clinit) {
Alexandre Rames67555f72014-11-18 10:55:16 +0000320 DCHECK(at->IsLoadClass() || at->IsClinitCheck());
321 }
322
323 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000324 LocationSummary* locations = instruction_->GetLocations();
Vladimir Markoea4c1262017-02-06 19:59:33 +0000325 Location out = locations->Out();
Alexandre Rames67555f72014-11-18 10:55:16 +0000326 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
327
328 __ Bind(GetEntryLabel());
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000329 SaveLiveRegisters(codegen, locations);
Alexandre Rames67555f72014-11-18 10:55:16 +0000330
Vladimir Markof3c52b42017-11-17 17:32:12 +0000331 InvokeRuntimeCallingConvention calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000332 dex::TypeIndex type_index = cls_->GetTypeIndex();
333 __ Mov(calling_convention.GetRegisterAt(0).W(), type_index.index_);
Serban Constantinescu22f81d32016-02-18 16:06:31 +0000334 QuickEntrypointEnum entrypoint = do_clinit_ ? kQuickInitializeStaticStorage
335 : kQuickInitializeType;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000336 arm64_codegen->InvokeRuntime(entrypoint, instruction_, dex_pc_, this);
Andreas Gampe1cc7dba2014-12-17 18:43:01 -0800337 if (do_clinit_) {
Vladimir Marko5ea536a2015-04-20 20:11:30 +0100338 CheckEntrypointTypes<kQuickInitializeStaticStorage, void*, uint32_t>();
Andreas Gampe1cc7dba2014-12-17 18:43:01 -0800339 } else {
Vladimir Marko5ea536a2015-04-20 20:11:30 +0100340 CheckEntrypointTypes<kQuickInitializeType, void*, uint32_t>();
Andreas Gampe1cc7dba2014-12-17 18:43:01 -0800341 }
Alexandre Rames67555f72014-11-18 10:55:16 +0000342
343 // Move the class to the desired location.
Alexandre Rames67555f72014-11-18 10:55:16 +0000344 if (out.IsValid()) {
345 DCHECK(out.IsRegister() && !locations->GetLiveRegisters()->ContainsCoreRegister(out.reg()));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100346 DataType::Type type = instruction_->GetType();
Alexandre Rames3e69f162014-12-10 10:36:50 +0000347 arm64_codegen->MoveLocation(out, calling_convention.GetReturnLocation(type), type);
Alexandre Rames67555f72014-11-18 10:55:16 +0000348 }
Nicolas Geoffraya8ac9132015-03-13 16:36:36 +0000349 RestoreLiveRegisters(codegen, locations);
Alexandre Rames67555f72014-11-18 10:55:16 +0000350 __ B(GetExitLabel());
351 }
352
Alexandre Rames9931f312015-06-19 14:47:01 +0100353 const char* GetDescription() const OVERRIDE { return "LoadClassSlowPathARM64"; }
354
Alexandre Rames67555f72014-11-18 10:55:16 +0000355 private:
356 // The class this slow path will load.
357 HLoadClass* const cls_;
358
Alexandre Rames67555f72014-11-18 10:55:16 +0000359 // The dex PC of `at_`.
360 const uint32_t dex_pc_;
361
362 // Whether to initialize the class.
363 const bool do_clinit_;
364
365 DISALLOW_COPY_AND_ASSIGN(LoadClassSlowPathARM64);
366};
367
Vladimir Markoaad75c62016-10-03 08:46:48 +0000368class LoadStringSlowPathARM64 : public SlowPathCodeARM64 {
369 public:
Vladimir Markof3c52b42017-11-17 17:32:12 +0000370 explicit LoadStringSlowPathARM64(HLoadString* instruction)
371 : SlowPathCodeARM64(instruction) {}
Vladimir Markoaad75c62016-10-03 08:46:48 +0000372
373 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
374 LocationSummary* locations = instruction_->GetLocations();
375 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
376 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
377
378 __ Bind(GetEntryLabel());
379 SaveLiveRegisters(codegen, locations);
380
Vladimir Markof3c52b42017-11-17 17:32:12 +0000381 InvokeRuntimeCallingConvention calling_convention;
Vladimir Marko6bec91c2017-01-09 15:03:12 +0000382 const dex::StringIndex string_index = instruction_->AsLoadString()->GetStringIndex();
383 __ Mov(calling_convention.GetRegisterAt(0).W(), string_index.index_);
Vladimir Markoaad75c62016-10-03 08:46:48 +0000384 arm64_codegen->InvokeRuntime(kQuickResolveString, instruction_, instruction_->GetDexPc(), this);
385 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100386 DataType::Type type = instruction_->GetType();
Vladimir Markoaad75c62016-10-03 08:46:48 +0000387 arm64_codegen->MoveLocation(locations->Out(), calling_convention.GetReturnLocation(type), type);
388
389 RestoreLiveRegisters(codegen, locations);
390
Vladimir Markoaad75c62016-10-03 08:46:48 +0000391 __ B(GetExitLabel());
392 }
393
394 const char* GetDescription() const OVERRIDE { return "LoadStringSlowPathARM64"; }
395
396 private:
397 DISALLOW_COPY_AND_ASSIGN(LoadStringSlowPathARM64);
398};
399
Alexandre Rames5319def2014-10-23 10:03:10 +0100400class NullCheckSlowPathARM64 : public SlowPathCodeARM64 {
401 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000402 explicit NullCheckSlowPathARM64(HNullCheck* instr) : SlowPathCodeARM64(instr) {}
Alexandre Rames5319def2014-10-23 10:03:10 +0100403
Alexandre Rames67555f72014-11-18 10:55:16 +0000404 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
405 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
Alexandre Rames5319def2014-10-23 10:03:10 +0100406 __ Bind(GetEntryLabel());
David Brazdil77a48ae2015-09-15 12:34:04 +0000407 if (instruction_->CanThrowIntoCatchBlock()) {
408 // Live registers will be restored in the catch block if caught.
409 SaveLiveRegisters(codegen, instruction_->GetLocations());
410 }
Serban Constantinescu22f81d32016-02-18 16:06:31 +0000411 arm64_codegen->InvokeRuntime(kQuickThrowNullPointer,
412 instruction_,
413 instruction_->GetDexPc(),
414 this);
Andreas Gampe1cc7dba2014-12-17 18:43:01 -0800415 CheckEntrypointTypes<kQuickThrowNullPointer, void, void>();
Alexandre Rames5319def2014-10-23 10:03:10 +0100416 }
417
Alexandre Rames8158f282015-08-07 10:26:17 +0100418 bool IsFatal() const OVERRIDE { return true; }
419
Alexandre Rames9931f312015-06-19 14:47:01 +0100420 const char* GetDescription() const OVERRIDE { return "NullCheckSlowPathARM64"; }
421
Alexandre Rames5319def2014-10-23 10:03:10 +0100422 private:
Alexandre Rames5319def2014-10-23 10:03:10 +0100423 DISALLOW_COPY_AND_ASSIGN(NullCheckSlowPathARM64);
424};
425
426class SuspendCheckSlowPathARM64 : public SlowPathCodeARM64 {
427 public:
Roland Levillain3887c462015-08-12 18:15:42 +0100428 SuspendCheckSlowPathARM64(HSuspendCheck* instruction, HBasicBlock* successor)
David Srbecky9cd6d372016-02-09 15:24:47 +0000429 : SlowPathCodeARM64(instruction), successor_(successor) {}
Alexandre Rames5319def2014-10-23 10:03:10 +0100430
Alexandre Rames67555f72014-11-18 10:55:16 +0000431 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Artem Serov7957d952017-04-04 15:44:09 +0100432 LocationSummary* locations = instruction_->GetLocations();
Alexandre Rames67555f72014-11-18 10:55:16 +0000433 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
Alexandre Rames5319def2014-10-23 10:03:10 +0100434 __ Bind(GetEntryLabel());
Artem Serov7957d952017-04-04 15:44:09 +0100435 SaveLiveRegisters(codegen, locations); // Only saves live 128-bit regs for SIMD.
Serban Constantinescu22f81d32016-02-18 16:06:31 +0000436 arm64_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this);
Andreas Gampe1cc7dba2014-12-17 18:43:01 -0800437 CheckEntrypointTypes<kQuickTestSuspend, void, void>();
Artem Serov7957d952017-04-04 15:44:09 +0100438 RestoreLiveRegisters(codegen, locations); // Only restores live 128-bit regs for SIMD.
Alexandre Rames67555f72014-11-18 10:55:16 +0000439 if (successor_ == nullptr) {
440 __ B(GetReturnLabel());
441 } else {
442 __ B(arm64_codegen->GetLabelOf(successor_));
443 }
Alexandre Rames5319def2014-10-23 10:03:10 +0100444 }
445
Scott Wakeling97c72b72016-06-24 16:19:36 +0100446 vixl::aarch64::Label* GetReturnLabel() {
Alexandre Rames5319def2014-10-23 10:03:10 +0100447 DCHECK(successor_ == nullptr);
448 return &return_label_;
449 }
450
Nicolas Geoffraydb216f42015-05-05 17:02:20 +0100451 HBasicBlock* GetSuccessor() const {
452 return successor_;
453 }
454
Alexandre Rames9931f312015-06-19 14:47:01 +0100455 const char* GetDescription() const OVERRIDE { return "SuspendCheckSlowPathARM64"; }
456
Alexandre Rames5319def2014-10-23 10:03:10 +0100457 private:
Alexandre Rames5319def2014-10-23 10:03:10 +0100458 // If not null, the block to branch to after the suspend check.
459 HBasicBlock* const successor_;
460
461 // If `successor_` is null, the label to branch to after the suspend check.
Scott Wakeling97c72b72016-06-24 16:19:36 +0100462 vixl::aarch64::Label return_label_;
Alexandre Rames5319def2014-10-23 10:03:10 +0100463
464 DISALLOW_COPY_AND_ASSIGN(SuspendCheckSlowPathARM64);
465};
466
Alexandre Rames67555f72014-11-18 10:55:16 +0000467class TypeCheckSlowPathARM64 : public SlowPathCodeARM64 {
468 public:
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +0000469 TypeCheckSlowPathARM64(HInstruction* instruction, bool is_fatal)
David Srbecky9cd6d372016-02-09 15:24:47 +0000470 : SlowPathCodeARM64(instruction), is_fatal_(is_fatal) {}
Alexandre Rames67555f72014-11-18 10:55:16 +0000471
472 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Alexandre Rames3e69f162014-12-10 10:36:50 +0000473 LocationSummary* locations = instruction_->GetLocations();
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800474
Alexandre Rames3e69f162014-12-10 10:36:50 +0000475 DCHECK(instruction_->IsCheckCast()
476 || !locations->GetLiveRegisters()->ContainsCoreRegister(locations->Out().reg()));
477 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
Serban Constantinescu5a6cc492015-08-13 15:20:25 +0100478 uint32_t dex_pc = instruction_->GetDexPc();
Alexandre Rames3e69f162014-12-10 10:36:50 +0000479
Alexandre Rames67555f72014-11-18 10:55:16 +0000480 __ Bind(GetEntryLabel());
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +0000481
Vladimir Marko87584542017-12-12 17:47:52 +0000482 if (!is_fatal_ || instruction_->CanThrowIntoCatchBlock()) {
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +0000483 SaveLiveRegisters(codegen, locations);
484 }
Alexandre Rames3e69f162014-12-10 10:36:50 +0000485
486 // We're moving two locations to locations that could overlap, so we need a parallel
487 // move resolver.
488 InvokeRuntimeCallingConvention calling_convention;
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800489 codegen->EmitParallelMoves(locations->InAt(0),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800490 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100491 DataType::Type::kReference,
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800492 locations->InAt(1),
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800493 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100494 DataType::Type::kReference);
Alexandre Rames3e69f162014-12-10 10:36:50 +0000495 if (instruction_->IsInstanceOf()) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +0000496 arm64_codegen->InvokeRuntime(kQuickInstanceofNonTrivial, instruction_, dex_pc, this);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -0800497 CheckEntrypointTypes<kQuickInstanceofNonTrivial, size_t, mirror::Object*, mirror::Class*>();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100498 DataType::Type ret_type = instruction_->GetType();
Alexandre Rames3e69f162014-12-10 10:36:50 +0000499 Location ret_loc = calling_convention.GetReturnLocation(ret_type);
500 arm64_codegen->MoveLocation(locations->Out(), ret_loc, ret_type);
501 } else {
502 DCHECK(instruction_->IsCheckCast());
Mathieu Chartierb99f4d62016-11-07 16:17:26 -0800503 arm64_codegen->InvokeRuntime(kQuickCheckInstanceOf, instruction_, dex_pc, this);
504 CheckEntrypointTypes<kQuickCheckInstanceOf, void, mirror::Object*, mirror::Class*>();
Alexandre Rames3e69f162014-12-10 10:36:50 +0000505 }
506
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +0000507 if (!is_fatal_) {
508 RestoreLiveRegisters(codegen, locations);
509 __ B(GetExitLabel());
510 }
Alexandre Rames67555f72014-11-18 10:55:16 +0000511 }
512
Alexandre Rames9931f312015-06-19 14:47:01 +0100513 const char* GetDescription() const OVERRIDE { return "TypeCheckSlowPathARM64"; }
Roland Levillainf41f9562016-09-14 19:26:48 +0100514 bool IsFatal() const OVERRIDE { return is_fatal_; }
Alexandre Rames9931f312015-06-19 14:47:01 +0100515
Alexandre Rames67555f72014-11-18 10:55:16 +0000516 private:
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +0000517 const bool is_fatal_;
Alexandre Rames3e69f162014-12-10 10:36:50 +0000518
Alexandre Rames67555f72014-11-18 10:55:16 +0000519 DISALLOW_COPY_AND_ASSIGN(TypeCheckSlowPathARM64);
520};
521
Mingyao Yangd43b3ac2015-04-01 14:03:04 -0700522class DeoptimizationSlowPathARM64 : public SlowPathCodeARM64 {
523 public:
Aart Bik42249c32016-01-07 15:33:50 -0800524 explicit DeoptimizationSlowPathARM64(HDeoptimize* instruction)
David Srbecky9cd6d372016-02-09 15:24:47 +0000525 : SlowPathCodeARM64(instruction) {}
Mingyao Yangd43b3ac2015-04-01 14:03:04 -0700526
527 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Aart Bik42249c32016-01-07 15:33:50 -0800528 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
Mingyao Yangd43b3ac2015-04-01 14:03:04 -0700529 __ Bind(GetEntryLabel());
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100530 LocationSummary* locations = instruction_->GetLocations();
531 SaveLiveRegisters(codegen, locations);
532 InvokeRuntimeCallingConvention calling_convention;
533 __ Mov(calling_convention.GetRegisterAt(0),
534 static_cast<uint32_t>(instruction_->AsDeoptimize()->GetDeoptimizationKind()));
Serban Constantinescu22f81d32016-02-18 16:06:31 +0000535 arm64_codegen->InvokeRuntime(kQuickDeoptimize, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +0100536 CheckEntrypointTypes<kQuickDeoptimize, void, DeoptimizationKind>();
Mingyao Yangd43b3ac2015-04-01 14:03:04 -0700537 }
538
Alexandre Rames9931f312015-06-19 14:47:01 +0100539 const char* GetDescription() const OVERRIDE { return "DeoptimizationSlowPathARM64"; }
540
Mingyao Yangd43b3ac2015-04-01 14:03:04 -0700541 private:
Mingyao Yangd43b3ac2015-04-01 14:03:04 -0700542 DISALLOW_COPY_AND_ASSIGN(DeoptimizationSlowPathARM64);
543};
544
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +0100545class ArraySetSlowPathARM64 : public SlowPathCodeARM64 {
546 public:
David Srbecky9cd6d372016-02-09 15:24:47 +0000547 explicit ArraySetSlowPathARM64(HInstruction* instruction) : SlowPathCodeARM64(instruction) {}
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +0100548
549 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
550 LocationSummary* locations = instruction_->GetLocations();
551 __ Bind(GetEntryLabel());
552 SaveLiveRegisters(codegen, locations);
553
554 InvokeRuntimeCallingConvention calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +0100555 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +0100556 parallel_move.AddMove(
557 locations->InAt(0),
558 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100559 DataType::Type::kReference,
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +0100560 nullptr);
561 parallel_move.AddMove(
562 locations->InAt(1),
563 LocationFrom(calling_convention.GetRegisterAt(1)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100564 DataType::Type::kInt32,
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +0100565 nullptr);
566 parallel_move.AddMove(
567 locations->InAt(2),
568 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +0100569 DataType::Type::kReference,
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +0100570 nullptr);
571 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
572
573 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
Serban Constantinescu22f81d32016-02-18 16:06:31 +0000574 arm64_codegen->InvokeRuntime(kQuickAputObject, instruction_, instruction_->GetDexPc(), this);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +0100575 CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>();
576 RestoreLiveRegisters(codegen, locations);
577 __ B(GetExitLabel());
578 }
579
580 const char* GetDescription() const OVERRIDE { return "ArraySetSlowPathARM64"; }
581
582 private:
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +0100583 DISALLOW_COPY_AND_ASSIGN(ArraySetSlowPathARM64);
584};
585
Zheng Xu3927c8b2015-11-18 17:46:25 +0800586void JumpTableARM64::EmitTable(CodeGeneratorARM64* codegen) {
587 uint32_t num_entries = switch_instr_->GetNumEntries();
Vladimir Markof3e0ee22015-12-17 15:23:13 +0000588 DCHECK_GE(num_entries, kPackedSwitchCompareJumpThreshold);
Zheng Xu3927c8b2015-11-18 17:46:25 +0800589
590 // We are about to use the assembler to place literals directly. Make sure we have enough
591 // underlying code buffer and we have generated the jump table with right size.
Artem Serov914d7a82017-02-07 14:33:49 +0000592 EmissionCheckScope scope(codegen->GetVIXLAssembler(),
593 num_entries * sizeof(int32_t),
594 CodeBufferCheckScope::kExactSize);
Zheng Xu3927c8b2015-11-18 17:46:25 +0800595
596 __ Bind(&table_start_);
597 const ArenaVector<HBasicBlock*>& successors = switch_instr_->GetBlock()->GetSuccessors();
598 for (uint32_t i = 0; i < num_entries; i++) {
Scott Wakeling97c72b72016-06-24 16:19:36 +0100599 vixl::aarch64::Label* target_label = codegen->GetLabelOf(successors[i]);
Zheng Xu3927c8b2015-11-18 17:46:25 +0800600 DCHECK(target_label->IsBound());
Scott Wakeling97c72b72016-06-24 16:19:36 +0100601 ptrdiff_t jump_offset = target_label->GetLocation() - table_start_.GetLocation();
Zheng Xu3927c8b2015-11-18 17:46:25 +0800602 DCHECK_GT(jump_offset, std::numeric_limits<int32_t>::min());
603 DCHECK_LE(jump_offset, std::numeric_limits<int32_t>::max());
604 Literal<int32_t> literal(jump_offset);
605 __ place(&literal);
606 }
607}
608
Roland Levillain54f869e2017-03-06 13:54:11 +0000609// Abstract base class for read barrier slow paths marking a reference
610// `ref`.
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000611//
Roland Levillain54f869e2017-03-06 13:54:11 +0000612// Argument `entrypoint` must be a register location holding the read
Roland Levillain97c46462017-05-11 14:04:03 +0100613// barrier marking runtime entry point to be invoked or an empty
614// location; in the latter case, the read barrier marking runtime
615// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000616class ReadBarrierMarkSlowPathBaseARM64 : public SlowPathCodeARM64 {
617 protected:
618 ReadBarrierMarkSlowPathBaseARM64(HInstruction* instruction, Location ref, Location entrypoint)
619 : SlowPathCodeARM64(instruction), ref_(ref), entrypoint_(entrypoint) {
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000620 DCHECK(kEmitCompilerReadBarrier);
621 }
622
Roland Levillain54f869e2017-03-06 13:54:11 +0000623 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathBaseARM64"; }
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000624
Roland Levillain54f869e2017-03-06 13:54:11 +0000625 // Generate assembly code calling the read barrier marking runtime
626 // entry point (ReadBarrierMarkRegX).
627 void GenerateReadBarrierMarkRuntimeCall(CodeGenerator* codegen) {
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000628 // No need to save live registers; it's taken care of by the
629 // entrypoint. Also, there is no need to update the stack mask,
630 // as this runtime call will not trigger a garbage collection.
631 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
632 DCHECK_NE(ref_.reg(), LR);
633 DCHECK_NE(ref_.reg(), WSP);
634 DCHECK_NE(ref_.reg(), WZR);
635 // IP0 is used internally by the ReadBarrierMarkRegX entry point
636 // as a temporary, it cannot be the entry point's input/output.
637 DCHECK_NE(ref_.reg(), IP0);
638 DCHECK(0 <= ref_.reg() && ref_.reg() < kNumberOfWRegisters) << ref_.reg();
639 // "Compact" slow path, saving two moves.
640 //
641 // Instead of using the standard runtime calling convention (input
642 // and output in W0):
643 //
644 // W0 <- ref
645 // W0 <- ReadBarrierMark(W0)
646 // ref <- W0
647 //
648 // we just use rX (the register containing `ref`) as input and output
649 // of a dedicated entrypoint:
650 //
651 // rX <- ReadBarrierMarkRegX(rX)
652 //
653 if (entrypoint_.IsValid()) {
654 arm64_codegen->ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction_, this);
655 __ Blr(XRegisterFrom(entrypoint_));
656 } else {
657 // Entrypoint is not already loaded, load from the thread.
658 int32_t entry_point_offset =
Roland Levillain97c46462017-05-11 14:04:03 +0100659 Thread::ReadBarrierMarkEntryPointsOffset<kArm64PointerSize>(ref_.reg());
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000660 // This runtime call does not require a stack map.
661 arm64_codegen->InvokeRuntimeWithoutRecordingPcInfo(entry_point_offset, instruction_, this);
662 }
663 }
664
665 // The location (register) of the marked object reference.
666 const Location ref_;
667
668 // The location of the entrypoint if it is already loaded.
669 const Location entrypoint_;
670
Roland Levillain54f869e2017-03-06 13:54:11 +0000671 private:
672 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathBaseARM64);
673};
674
Alexandre Rames5319def2014-10-23 10:03:10 +0100675// Slow path marking an object reference `ref` during a read
676// barrier. The field `obj.field` in the object `obj` holding this
Roland Levillain54f869e2017-03-06 13:54:11 +0000677// reference does not get updated by this slow path after marking.
Alexandre Rames5319def2014-10-23 10:03:10 +0100678//
679// This means that after the execution of this slow path, `ref` will
680// always be up-to-date, but `obj.field` may not; i.e., after the
681// flip, `ref` will be a to-space reference, but `obj.field` will
682// probably still be a from-space reference (unless it gets updated by
683// another thread, or if another thread installed another object
684// reference (different from `ref`) in `obj.field`).
685//
Roland Levillain97c46462017-05-11 14:04:03 +0100686// Argument `entrypoint` must be a register location holding the read
687// barrier marking runtime entry point to be invoked or an empty
688// location; in the latter case, the read barrier marking runtime
689// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000690class ReadBarrierMarkSlowPathARM64 : public ReadBarrierMarkSlowPathBaseARM64 {
Alexandre Rames5319def2014-10-23 10:03:10 +0100691 public:
692 ReadBarrierMarkSlowPathARM64(HInstruction* instruction,
693 Location ref,
694 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000695 : ReadBarrierMarkSlowPathBaseARM64(instruction, ref, entrypoint) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +0100696 DCHECK(kEmitCompilerReadBarrier);
Alexandre Rames5319def2014-10-23 10:03:10 +0100697 }
698
699 const char* GetDescription() const OVERRIDE { return "ReadBarrierMarkSlowPathARM64"; }
700
701 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
Alexandre Rames542361f2015-01-29 16:57:31 +0000702 LocationSummary* locations = instruction_->GetLocations();
Roland Levillain2d27c8e2015-04-28 15:48:45 +0100703 DCHECK(locations->CanCall());
704 DCHECK(ref_.IsRegister()) << ref_;
Alexandre Rames542361f2015-01-29 16:57:31 +0000705 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
Roland Levillain54f869e2017-03-06 13:54:11 +0000706 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
707 << "Unexpected instruction in read barrier marking slow path: "
708 << instruction_->DebugName();
709
710 __ Bind(GetEntryLabel());
711 GenerateReadBarrierMarkRuntimeCall(codegen);
712 __ B(GetExitLabel());
713 }
714
715 private:
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000716 DISALLOW_COPY_AND_ASSIGN(ReadBarrierMarkSlowPathARM64);
717};
718
Roland Levillain54f869e2017-03-06 13:54:11 +0000719// Slow path loading `obj`'s lock word, loading a reference from
720// object `*(obj + offset + (index << scale_factor))` into `ref`, and
721// marking `ref` if `obj` is gray according to the lock word (Baker
722// read barrier). The field `obj.field` in the object `obj` holding
723// this reference does not get updated by this slow path after marking
724// (see LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64
725// below for that).
726//
727// This means that after the execution of this slow path, `ref` will
728// always be up-to-date, but `obj.field` may not; i.e., after the
729// flip, `ref` will be a to-space reference, but `obj.field` will
730// probably still be a from-space reference (unless it gets updated by
731// another thread, or if another thread installed another object
732// reference (different from `ref`) in `obj.field`).
733//
734// Argument `entrypoint` must be a register location holding the read
Roland Levillain97c46462017-05-11 14:04:03 +0100735// barrier marking runtime entry point to be invoked or an empty
736// location; in the latter case, the read barrier marking runtime
737// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000738class LoadReferenceWithBakerReadBarrierSlowPathARM64 : public ReadBarrierMarkSlowPathBaseARM64 {
739 public:
740 LoadReferenceWithBakerReadBarrierSlowPathARM64(HInstruction* instruction,
741 Location ref,
742 Register obj,
743 uint32_t offset,
744 Location index,
745 size_t scale_factor,
746 bool needs_null_check,
747 bool use_load_acquire,
748 Register temp,
Roland Levillain97c46462017-05-11 14:04:03 +0100749 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000750 : ReadBarrierMarkSlowPathBaseARM64(instruction, ref, entrypoint),
751 obj_(obj),
752 offset_(offset),
753 index_(index),
754 scale_factor_(scale_factor),
755 needs_null_check_(needs_null_check),
756 use_load_acquire_(use_load_acquire),
757 temp_(temp) {
758 DCHECK(kEmitCompilerReadBarrier);
759 DCHECK(kUseBakerReadBarrier);
760 }
761
762 const char* GetDescription() const OVERRIDE {
763 return "LoadReferenceWithBakerReadBarrierSlowPathARM64";
764 }
765
766 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
767 LocationSummary* locations = instruction_->GetLocations();
768 DCHECK(locations->CanCall());
769 DCHECK(ref_.IsRegister()) << ref_;
770 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
771 DCHECK(obj_.IsW());
772 DCHECK_NE(ref_.reg(), LocationFrom(temp_).reg());
Alexandre Rames5319def2014-10-23 10:03:10 +0100773 DCHECK(instruction_->IsInstanceFieldGet() ||
774 instruction_->IsStaticFieldGet() ||
775 instruction_->IsArrayGet() ||
776 instruction_->IsArraySet() ||
Alexandre Rames5319def2014-10-23 10:03:10 +0100777 instruction_->IsInstanceOf() ||
778 instruction_->IsCheckCast() ||
779 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()) ||
780 (instruction_->IsInvokeStaticOrDirect() && instruction_->GetLocations()->Intrinsified()))
781 << "Unexpected instruction in read barrier marking slow path: "
782 << instruction_->DebugName();
783 // The read barrier instrumentation of object ArrayGet
784 // instructions does not support the HIntermediateAddress
Alexandre Ramesa89086e2014-11-07 17:13:25 +0000785 // instruction.
786 DCHECK(!(instruction_->IsArrayGet() &&
Alexandre Rames542361f2015-01-29 16:57:31 +0000787 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
788
Roland Levillain54f869e2017-03-06 13:54:11 +0000789 // Temporary register `temp_`, used to store the lock word, must
790 // not be IP0 nor IP1, as we may use them to emit the reference
791 // load (in the call to GenerateRawReferenceLoad below), and we
792 // need the lock word to still be in `temp_` after the reference
793 // load.
794 DCHECK_NE(LocationFrom(temp_).reg(), IP0);
795 DCHECK_NE(LocationFrom(temp_).reg(), IP1);
796
Alexandre Rames5319def2014-10-23 10:03:10 +0100797 __ Bind(GetEntryLabel());
Roland Levillain54f869e2017-03-06 13:54:11 +0000798
799 // When using MaybeGenerateReadBarrierSlow, the read barrier call is
800 // inserted after the original load. However, in fast path based
801 // Baker's read barriers, we need to perform the load of
802 // mirror::Object::monitor_ *before* the original reference load.
803 // This load-load ordering is required by the read barrier.
Roland Levillainff487002017-03-07 16:50:01 +0000804 // The slow path (for Baker's algorithm) should look like:
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100805 //
Roland Levillain54f869e2017-03-06 13:54:11 +0000806 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
807 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
808 // HeapReference<mirror::Object> ref = *src; // Original reference load.
809 // bool is_gray = (rb_state == ReadBarrier::GrayState());
810 // if (is_gray) {
811 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
812 // }
Roland Levillaind966ce72017-02-09 16:20:14 +0000813 //
Roland Levillain54f869e2017-03-06 13:54:11 +0000814 // Note: the original implementation in ReadBarrier::Barrier is
815 // slightly more complex as it performs additional checks that we do
816 // not do here for performance reasons.
817
818 // /* int32_t */ monitor = obj->monitor_
819 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
820 __ Ldr(temp_, HeapOperand(obj_, monitor_offset));
821 if (needs_null_check_) {
822 codegen->MaybeRecordImplicitNullCheck(instruction_);
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100823 }
Roland Levillain54f869e2017-03-06 13:54:11 +0000824 // /* LockWord */ lock_word = LockWord(monitor)
825 static_assert(sizeof(LockWord) == sizeof(int32_t),
826 "art::LockWord and int32_t have different sizes.");
827
828 // Introduce a dependency on the lock_word including rb_state,
829 // to prevent load-load reordering, and without using
830 // a memory barrier (which would be more expensive).
831 // `obj` is unchanged by this operation, but its value now depends
832 // on `temp`.
833 __ Add(obj_.X(), obj_.X(), Operand(temp_.X(), LSR, 32));
834
835 // The actual reference load.
836 // A possible implicit null check has already been handled above.
837 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
838 arm64_codegen->GenerateRawReferenceLoad(instruction_,
839 ref_,
840 obj_,
841 offset_,
842 index_,
843 scale_factor_,
844 /* needs_null_check */ false,
845 use_load_acquire_);
846
847 // Mark the object `ref` when `obj` is gray.
848 //
849 // if (rb_state == ReadBarrier::GrayState())
850 // ref = ReadBarrier::Mark(ref);
851 //
852 // Given the numeric representation, it's enough to check the low bit of the rb_state.
853 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
854 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
855 __ Tbz(temp_, LockWord::kReadBarrierStateShift, GetExitLabel());
856 GenerateReadBarrierMarkRuntimeCall(codegen);
857
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000858 __ B(GetExitLabel());
859 }
860
861 private:
Roland Levillain54f869e2017-03-06 13:54:11 +0000862 // The register containing the object holding the marked object reference field.
863 Register obj_;
864 // The offset, index and scale factor to access the reference in `obj_`.
865 uint32_t offset_;
866 Location index_;
867 size_t scale_factor_;
868 // Is a null check required?
869 bool needs_null_check_;
870 // Should this reference load use Load-Acquire semantics?
871 bool use_load_acquire_;
872 // A temporary register used to hold the lock word of `obj_`.
873 Register temp_;
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000874
Roland Levillain54f869e2017-03-06 13:54:11 +0000875 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierSlowPathARM64);
Roland Levillain27b1f9c2017-01-17 16:56:34 +0000876};
877
Roland Levillain54f869e2017-03-06 13:54:11 +0000878// Slow path loading `obj`'s lock word, loading a reference from
879// object `*(obj + offset + (index << scale_factor))` into `ref`, and
880// marking `ref` if `obj` is gray according to the lock word (Baker
881// read barrier). If needed, this slow path also atomically updates
882// the field `obj.field` in the object `obj` holding this reference
883// after marking (contrary to
884// LoadReferenceWithBakerReadBarrierSlowPathARM64 above, which never
885// tries to update `obj.field`).
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100886//
887// This means that after the execution of this slow path, both `ref`
888// and `obj.field` will be up-to-date; i.e., after the flip, both will
889// hold the same to-space reference (unless another thread installed
890// another object reference (different from `ref`) in `obj.field`).
Roland Levillainba650a42017-03-06 13:52:32 +0000891//
Roland Levillain54f869e2017-03-06 13:54:11 +0000892// Argument `entrypoint` must be a register location holding the read
Roland Levillain97c46462017-05-11 14:04:03 +0100893// barrier marking runtime entry point to be invoked or an empty
894// location; in the latter case, the read barrier marking runtime
895// entry point will be loaded by the slow path code itself.
Roland Levillain54f869e2017-03-06 13:54:11 +0000896class LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64
897 : public ReadBarrierMarkSlowPathBaseARM64 {
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100898 public:
Roland Levillain97c46462017-05-11 14:04:03 +0100899 LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64(
900 HInstruction* instruction,
901 Location ref,
902 Register obj,
903 uint32_t offset,
904 Location index,
905 size_t scale_factor,
906 bool needs_null_check,
907 bool use_load_acquire,
908 Register temp,
909 Location entrypoint = Location::NoLocation())
Roland Levillain54f869e2017-03-06 13:54:11 +0000910 : ReadBarrierMarkSlowPathBaseARM64(instruction, ref, entrypoint),
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100911 obj_(obj),
Roland Levillain54f869e2017-03-06 13:54:11 +0000912 offset_(offset),
913 index_(index),
914 scale_factor_(scale_factor),
915 needs_null_check_(needs_null_check),
916 use_load_acquire_(use_load_acquire),
Roland Levillain35345a52017-02-27 14:32:08 +0000917 temp_(temp) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100918 DCHECK(kEmitCompilerReadBarrier);
Roland Levillain54f869e2017-03-06 13:54:11 +0000919 DCHECK(kUseBakerReadBarrier);
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100920 }
921
922 const char* GetDescription() const OVERRIDE {
Roland Levillain54f869e2017-03-06 13:54:11 +0000923 return "LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64";
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100924 }
925
926 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
927 LocationSummary* locations = instruction_->GetLocations();
928 Register ref_reg = WRegisterFrom(ref_);
929 DCHECK(locations->CanCall());
930 DCHECK(ref_.IsRegister()) << ref_;
931 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_.reg())) << ref_.reg();
Roland Levillain54f869e2017-03-06 13:54:11 +0000932 DCHECK(obj_.IsW());
933 DCHECK_NE(ref_.reg(), LocationFrom(temp_).reg());
934
935 // This slow path is only used by the UnsafeCASObject intrinsic at the moment.
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100936 DCHECK((instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
937 << "Unexpected instruction in read barrier marking and field updating slow path: "
938 << instruction_->DebugName();
939 DCHECK(instruction_->GetLocations()->Intrinsified());
940 DCHECK_EQ(instruction_->AsInvoke()->GetIntrinsic(), Intrinsics::kUnsafeCASObject);
Roland Levillain54f869e2017-03-06 13:54:11 +0000941 DCHECK_EQ(offset_, 0u);
942 DCHECK_EQ(scale_factor_, 0u);
943 DCHECK_EQ(use_load_acquire_, false);
944 // The location of the offset of the marked reference field within `obj_`.
945 Location field_offset = index_;
946 DCHECK(field_offset.IsRegister()) << field_offset;
947
948 // Temporary register `temp_`, used to store the lock word, must
949 // not be IP0 nor IP1, as we may use them to emit the reference
950 // load (in the call to GenerateRawReferenceLoad below), and we
951 // need the lock word to still be in `temp_` after the reference
952 // load.
953 DCHECK_NE(LocationFrom(temp_).reg(), IP0);
954 DCHECK_NE(LocationFrom(temp_).reg(), IP1);
Roland Levillaina1aa3b12016-10-26 13:03:38 +0100955
956 __ Bind(GetEntryLabel());
957
Roland Levillainff487002017-03-07 16:50:01 +0000958 // The implementation is similar to LoadReferenceWithBakerReadBarrierSlowPathARM64's:
959 //
960 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
961 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
962 // HeapReference<mirror::Object> ref = *src; // Original reference load.
963 // bool is_gray = (rb_state == ReadBarrier::GrayState());
964 // if (is_gray) {
965 // old_ref = ref;
966 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
967 // compareAndSwapObject(obj, field_offset, old_ref, ref);
968 // }
969
Roland Levillain54f869e2017-03-06 13:54:11 +0000970 // /* int32_t */ monitor = obj->monitor_
971 uint32_t monitor_offset = mirror::Object::MonitorOffset().Int32Value();
972 __ Ldr(temp_, HeapOperand(obj_, monitor_offset));
973 if (needs_null_check_) {
974 codegen->MaybeRecordImplicitNullCheck(instruction_);
975 }
976 // /* LockWord */ lock_word = LockWord(monitor)
977 static_assert(sizeof(LockWord) == sizeof(int32_t),
978 "art::LockWord and int32_t have different sizes.");
979
980 // Introduce a dependency on the lock_word including rb_state,
981 // to prevent load-load reordering, and without using
982 // a memory barrier (which would be more expensive).
983 // `obj` is unchanged by this operation, but its value now depends
984 // on `temp`.
985 __ Add(obj_.X(), obj_.X(), Operand(temp_.X(), LSR, 32));
986
987 // The actual reference load.
988 // A possible implicit null check has already been handled above.
989 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
990 arm64_codegen->GenerateRawReferenceLoad(instruction_,
991 ref_,
992 obj_,
993 offset_,
994 index_,
995 scale_factor_,
996 /* needs_null_check */ false,
997 use_load_acquire_);
998
999 // Mark the object `ref` when `obj` is gray.
1000 //
1001 // if (rb_state == ReadBarrier::GrayState())
1002 // ref = ReadBarrier::Mark(ref);
1003 //
1004 // Given the numeric representation, it's enough to check the low bit of the rb_state.
1005 static_assert(ReadBarrier::WhiteState() == 0, "Expecting white to have value 0");
1006 static_assert(ReadBarrier::GrayState() == 1, "Expecting gray to have value 1");
1007 __ Tbz(temp_, LockWord::kReadBarrierStateShift, GetExitLabel());
1008
1009 // Save the old value of the reference before marking it.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001010 // Note that we cannot use IP to save the old reference, as IP is
1011 // used internally by the ReadBarrierMarkRegX entry point, and we
1012 // need the old reference after the call to that entry point.
1013 DCHECK_NE(LocationFrom(temp_).reg(), IP0);
1014 __ Mov(temp_.W(), ref_reg);
1015
Roland Levillain54f869e2017-03-06 13:54:11 +00001016 GenerateReadBarrierMarkRuntimeCall(codegen);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001017
1018 // If the new reference is different from the old reference,
Roland Levillain54f869e2017-03-06 13:54:11 +00001019 // update the field in the holder (`*(obj_ + field_offset)`).
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001020 //
1021 // Note that this field could also hold a different object, if
1022 // another thread had concurrently changed it. In that case, the
1023 // LDXR/CMP/BNE sequence of instructions in the compare-and-set
1024 // (CAS) operation below would abort the CAS, leaving the field
1025 // as-is.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001026 __ Cmp(temp_.W(), ref_reg);
Roland Levillain54f869e2017-03-06 13:54:11 +00001027 __ B(eq, GetExitLabel());
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001028
1029 // Update the the holder's field atomically. This may fail if
1030 // mutator updates before us, but it's OK. This is achieved
1031 // using a strong compare-and-set (CAS) operation with relaxed
1032 // memory synchronization ordering, where the expected value is
1033 // the old reference and the desired value is the new reference.
1034
1035 MacroAssembler* masm = arm64_codegen->GetVIXLAssembler();
1036 UseScratchRegisterScope temps(masm);
1037
1038 // Convenience aliases.
1039 Register base = obj_.W();
Roland Levillain54f869e2017-03-06 13:54:11 +00001040 Register offset = XRegisterFrom(field_offset);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001041 Register expected = temp_.W();
1042 Register value = ref_reg;
1043 Register tmp_ptr = temps.AcquireX(); // Pointer to actual memory.
1044 Register tmp_value = temps.AcquireW(); // Value in memory.
1045
1046 __ Add(tmp_ptr, base.X(), Operand(offset));
1047
1048 if (kPoisonHeapReferences) {
1049 arm64_codegen->GetAssembler()->PoisonHeapReference(expected);
1050 if (value.Is(expected)) {
1051 // Do not poison `value`, as it is the same register as
1052 // `expected`, which has just been poisoned.
1053 } else {
1054 arm64_codegen->GetAssembler()->PoisonHeapReference(value);
1055 }
1056 }
1057
1058 // do {
1059 // tmp_value = [tmp_ptr] - expected;
1060 // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value));
1061
Roland Levillain24a4d112016-10-26 13:10:46 +01001062 vixl::aarch64::Label loop_head, comparison_failed, exit_loop;
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001063 __ Bind(&loop_head);
1064 __ Ldxr(tmp_value, MemOperand(tmp_ptr));
1065 __ Cmp(tmp_value, expected);
Roland Levillain24a4d112016-10-26 13:10:46 +01001066 __ B(&comparison_failed, ne);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001067 __ Stxr(tmp_value, value, MemOperand(tmp_ptr));
1068 __ Cbnz(tmp_value, &loop_head);
Roland Levillain24a4d112016-10-26 13:10:46 +01001069 __ B(&exit_loop);
1070 __ Bind(&comparison_failed);
1071 __ Clrex();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001072 __ Bind(&exit_loop);
1073
1074 if (kPoisonHeapReferences) {
1075 arm64_codegen->GetAssembler()->UnpoisonHeapReference(expected);
1076 if (value.Is(expected)) {
1077 // Do not unpoison `value`, as it is the same register as
1078 // `expected`, which has just been unpoisoned.
1079 } else {
1080 arm64_codegen->GetAssembler()->UnpoisonHeapReference(value);
1081 }
1082 }
1083
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001084 __ B(GetExitLabel());
1085 }
1086
1087 private:
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001088 // The register containing the object holding the marked object reference field.
1089 const Register obj_;
Roland Levillain54f869e2017-03-06 13:54:11 +00001090 // The offset, index and scale factor to access the reference in `obj_`.
1091 uint32_t offset_;
1092 Location index_;
1093 size_t scale_factor_;
1094 // Is a null check required?
1095 bool needs_null_check_;
1096 // Should this reference load use Load-Acquire semantics?
1097 bool use_load_acquire_;
1098 // A temporary register used to hold the lock word of `obj_`; and
1099 // also to hold the original reference value, when the reference is
1100 // marked.
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001101 const Register temp_;
1102
Roland Levillain54f869e2017-03-06 13:54:11 +00001103 DISALLOW_COPY_AND_ASSIGN(LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64);
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001104};
1105
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001106// Slow path generating a read barrier for a heap reference.
1107class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 {
1108 public:
1109 ReadBarrierForHeapReferenceSlowPathARM64(HInstruction* instruction,
1110 Location out,
1111 Location ref,
1112 Location obj,
1113 uint32_t offset,
1114 Location index)
David Srbecky9cd6d372016-02-09 15:24:47 +00001115 : SlowPathCodeARM64(instruction),
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001116 out_(out),
1117 ref_(ref),
1118 obj_(obj),
1119 offset_(offset),
1120 index_(index) {
1121 DCHECK(kEmitCompilerReadBarrier);
1122 // If `obj` is equal to `out` or `ref`, it means the initial object
1123 // has been overwritten by (or after) the heap object reference load
1124 // to be instrumented, e.g.:
1125 //
1126 // __ Ldr(out, HeapOperand(out, class_offset);
Roland Levillain44015862016-01-22 11:47:17 +00001127 // codegen_->GenerateReadBarrierSlow(instruction, out_loc, out_loc, out_loc, offset);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001128 //
1129 // In that case, we have lost the information about the original
1130 // object, and the emitted read barrier cannot work properly.
1131 DCHECK(!obj.Equals(out)) << "obj=" << obj << " out=" << out;
1132 DCHECK(!obj.Equals(ref)) << "obj=" << obj << " ref=" << ref;
1133 }
1134
1135 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1136 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
1137 LocationSummary* locations = instruction_->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001138 DataType::Type type = DataType::Type::kReference;
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001139 DCHECK(locations->CanCall());
1140 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg()));
Roland Levillain3d312422016-06-23 13:53:42 +01001141 DCHECK(instruction_->IsInstanceFieldGet() ||
1142 instruction_->IsStaticFieldGet() ||
1143 instruction_->IsArrayGet() ||
1144 instruction_->IsInstanceOf() ||
1145 instruction_->IsCheckCast() ||
Andreas Gamped9911ee2017-03-27 13:27:24 -07001146 (instruction_->IsInvokeVirtual() && instruction_->GetLocations()->Intrinsified()))
Roland Levillain44015862016-01-22 11:47:17 +00001147 << "Unexpected instruction in read barrier for heap reference slow path: "
1148 << instruction_->DebugName();
Roland Levillain19c54192016-11-04 13:44:09 +00001149 // The read barrier instrumentation of object ArrayGet
1150 // instructions does not support the HIntermediateAddress
1151 // instruction.
Roland Levillaincd3d0fb2016-01-15 19:26:48 +00001152 DCHECK(!(instruction_->IsArrayGet() &&
Artem Serov328429f2016-07-06 16:23:04 +01001153 instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress()));
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001154
1155 __ Bind(GetEntryLabel());
1156
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001157 SaveLiveRegisters(codegen, locations);
1158
1159 // We may have to change the index's value, but as `index_` is a
1160 // constant member (like other "inputs" of this slow path),
1161 // introduce a copy of it, `index`.
1162 Location index = index_;
1163 if (index_.IsValid()) {
Roland Levillain3d312422016-06-23 13:53:42 +01001164 // Handle `index_` for HArrayGet and UnsafeGetObject/UnsafeGetObjectVolatile intrinsics.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001165 if (instruction_->IsArrayGet()) {
1166 // Compute the actual memory offset and store it in `index`.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001167 Register index_reg = RegisterFrom(index_, DataType::Type::kInt32);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001168 DCHECK(locations->GetLiveRegisters()->ContainsCoreRegister(index_.reg()));
1169 if (codegen->IsCoreCalleeSaveRegister(index_.reg())) {
1170 // We are about to change the value of `index_reg` (see the
1171 // calls to vixl::MacroAssembler::Lsl and
1172 // vixl::MacroAssembler::Mov below), but it has
1173 // not been saved by the previous call to
1174 // art::SlowPathCode::SaveLiveRegisters, as it is a
1175 // callee-save register --
1176 // art::SlowPathCode::SaveLiveRegisters does not consider
1177 // callee-save registers, as it has been designed with the
1178 // assumption that callee-save registers are supposed to be
1179 // handled by the called function. So, as a callee-save
1180 // register, `index_reg` _would_ eventually be saved onto
1181 // the stack, but it would be too late: we would have
1182 // changed its value earlier. Therefore, we manually save
1183 // it here into another freely available register,
1184 // `free_reg`, chosen of course among the caller-save
1185 // registers (as a callee-save `free_reg` register would
1186 // exhibit the same problem).
1187 //
1188 // Note we could have requested a temporary register from
1189 // the register allocator instead; but we prefer not to, as
1190 // this is a slow path, and we know we can find a
1191 // caller-save register that is available.
1192 Register free_reg = FindAvailableCallerSaveRegister(codegen);
1193 __ Mov(free_reg.W(), index_reg);
1194 index_reg = free_reg;
1195 index = LocationFrom(index_reg);
1196 } else {
1197 // The initial register stored in `index_` has already been
1198 // saved in the call to art::SlowPathCode::SaveLiveRegisters
1199 // (as it is not a callee-save register), so we can freely
1200 // use it.
1201 }
1202 // Shifting the index value contained in `index_reg` by the scale
1203 // factor (2) cannot overflow in practice, as the runtime is
1204 // unable to allocate object arrays with a size larger than
1205 // 2^26 - 1 (that is, 2^28 - 4 bytes).
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001206 __ Lsl(index_reg, index_reg, DataType::SizeShift(type));
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001207 static_assert(
1208 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
1209 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
1210 __ Add(index_reg, index_reg, Operand(offset_));
1211 } else {
Roland Levillain3d312422016-06-23 13:53:42 +01001212 // In the case of the UnsafeGetObject/UnsafeGetObjectVolatile
1213 // intrinsics, `index_` is not shifted by a scale factor of 2
1214 // (as in the case of ArrayGet), as it is actually an offset
1215 // to an object field within an object.
1216 DCHECK(instruction_->IsInvoke()) << instruction_->DebugName();
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001217 DCHECK(instruction_->GetLocations()->Intrinsified());
1218 DCHECK((instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObject) ||
1219 (instruction_->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile))
1220 << instruction_->AsInvoke()->GetIntrinsic();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01001221 DCHECK_EQ(offset_, 0u);
Roland Levillaina7426c62016-08-03 15:02:10 +01001222 DCHECK(index_.IsRegister());
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001223 }
1224 }
1225
1226 // We're moving two or three locations to locations that could
1227 // overlap, so we need a parallel move resolver.
1228 InvokeRuntimeCallingConvention calling_convention;
Vladimir Markoca6fff82017-10-03 14:49:14 +01001229 HParallelMove parallel_move(codegen->GetGraph()->GetAllocator());
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001230 parallel_move.AddMove(ref_,
1231 LocationFrom(calling_convention.GetRegisterAt(0)),
1232 type,
1233 nullptr);
1234 parallel_move.AddMove(obj_,
1235 LocationFrom(calling_convention.GetRegisterAt(1)),
1236 type,
1237 nullptr);
1238 if (index.IsValid()) {
1239 parallel_move.AddMove(index,
1240 LocationFrom(calling_convention.GetRegisterAt(2)),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001241 DataType::Type::kInt32,
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001242 nullptr);
1243 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1244 } else {
1245 codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
1246 arm64_codegen->MoveConstant(LocationFrom(calling_convention.GetRegisterAt(2)), offset_);
1247 }
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001248 arm64_codegen->InvokeRuntime(kQuickReadBarrierSlow,
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001249 instruction_,
1250 instruction_->GetDexPc(),
1251 this);
1252 CheckEntrypointTypes<
1253 kQuickReadBarrierSlow, mirror::Object*, mirror::Object*, mirror::Object*, uint32_t>();
1254 arm64_codegen->MoveLocation(out_, calling_convention.GetReturnLocation(type), type);
1255
1256 RestoreLiveRegisters(codegen, locations);
1257
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001258 __ B(GetExitLabel());
1259 }
1260
1261 const char* GetDescription() const OVERRIDE { return "ReadBarrierForHeapReferenceSlowPathARM64"; }
1262
1263 private:
1264 Register FindAvailableCallerSaveRegister(CodeGenerator* codegen) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001265 size_t ref = static_cast<int>(XRegisterFrom(ref_).GetCode());
1266 size_t obj = static_cast<int>(XRegisterFrom(obj_).GetCode());
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001267 for (size_t i = 0, e = codegen->GetNumberOfCoreRegisters(); i < e; ++i) {
1268 if (i != ref && i != obj && !codegen->IsCoreCalleeSaveRegister(i)) {
1269 return Register(VIXLRegCodeFromART(i), kXRegSize);
1270 }
1271 }
1272 // We shall never fail to find a free caller-save register, as
1273 // there are more than two core caller-save registers on ARM64
1274 // (meaning it is possible to find one which is different from
1275 // `ref` and `obj`).
1276 DCHECK_GT(codegen->GetNumberOfCoreCallerSaveRegisters(), 2u);
1277 LOG(FATAL) << "Could not find a free register";
1278 UNREACHABLE();
1279 }
1280
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001281 const Location out_;
1282 const Location ref_;
1283 const Location obj_;
1284 const uint32_t offset_;
1285 // An additional location containing an index to an array.
1286 // Only used for HArrayGet and the UnsafeGetObject &
1287 // UnsafeGetObjectVolatile intrinsics.
1288 const Location index_;
1289
1290 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForHeapReferenceSlowPathARM64);
1291};
1292
1293// Slow path generating a read barrier for a GC root.
1294class ReadBarrierForRootSlowPathARM64 : public SlowPathCodeARM64 {
1295 public:
1296 ReadBarrierForRootSlowPathARM64(HInstruction* instruction, Location out, Location root)
David Srbecky9cd6d372016-02-09 15:24:47 +00001297 : SlowPathCodeARM64(instruction), out_(out), root_(root) {
Roland Levillain44015862016-01-22 11:47:17 +00001298 DCHECK(kEmitCompilerReadBarrier);
1299 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001300
1301 void EmitNativeCode(CodeGenerator* codegen) OVERRIDE {
1302 LocationSummary* locations = instruction_->GetLocations();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001303 DataType::Type type = DataType::Type::kReference;
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001304 DCHECK(locations->CanCall());
1305 DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg()));
Roland Levillain44015862016-01-22 11:47:17 +00001306 DCHECK(instruction_->IsLoadClass() || instruction_->IsLoadString())
1307 << "Unexpected instruction in read barrier for GC root slow path: "
1308 << instruction_->DebugName();
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001309
1310 __ Bind(GetEntryLabel());
1311 SaveLiveRegisters(codegen, locations);
1312
1313 InvokeRuntimeCallingConvention calling_convention;
1314 CodeGeneratorARM64* arm64_codegen = down_cast<CodeGeneratorARM64*>(codegen);
1315 // The argument of the ReadBarrierForRootSlow is not a managed
1316 // reference (`mirror::Object*`), but a `GcRoot<mirror::Object>*`;
1317 // thus we need a 64-bit move here, and we cannot use
1318 //
1319 // arm64_codegen->MoveLocation(
1320 // LocationFrom(calling_convention.GetRegisterAt(0)),
1321 // root_,
1322 // type);
1323 //
1324 // which would emit a 32-bit move, as `type` is a (32-bit wide)
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001325 // reference type (`DataType::Type::kReference`).
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001326 __ Mov(calling_convention.GetRegisterAt(0), XRegisterFrom(out_));
Serban Constantinescu22f81d32016-02-18 16:06:31 +00001327 arm64_codegen->InvokeRuntime(kQuickReadBarrierForRootSlow,
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001328 instruction_,
1329 instruction_->GetDexPc(),
1330 this);
1331 CheckEntrypointTypes<kQuickReadBarrierForRootSlow, mirror::Object*, GcRoot<mirror::Object>*>();
1332 arm64_codegen->MoveLocation(out_, calling_convention.GetReturnLocation(type), type);
1333
1334 RestoreLiveRegisters(codegen, locations);
1335 __ B(GetExitLabel());
1336 }
1337
1338 const char* GetDescription() const OVERRIDE { return "ReadBarrierForRootSlowPathARM64"; }
1339
1340 private:
Roland Levillain22ccc3a2015-11-24 13:10:05 +00001341 const Location out_;
1342 const Location root_;
1343
1344 DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM64);
1345};
1346
Alexandre Rames5319def2014-10-23 10:03:10 +01001347#undef __
1348
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001349Location InvokeDexCallingConventionVisitorARM64::GetNextLocation(DataType::Type type) {
Alexandre Rames5319def2014-10-23 10:03:10 +01001350 Location next_location;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001351 if (type == DataType::Type::kVoid) {
Alexandre Rames5319def2014-10-23 10:03:10 +01001352 LOG(FATAL) << "Unreachable type " << type;
1353 }
1354
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001355 if (DataType::IsFloatingPointType(type) &&
Alexandre Rames5319def2014-10-23 10:03:10 +01001356 (float_index_ < calling_convention.GetNumberOfFpuRegisters())) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +00001357 next_location = LocationFrom(calling_convention.GetFpuRegisterAt(float_index_++));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001358 } else if (!DataType::IsFloatingPointType(type) &&
Alexandre Ramesa89086e2014-11-07 17:13:25 +00001359 (gp_index_ < calling_convention.GetNumberOfRegisters())) {
1360 next_location = LocationFrom(calling_convention.GetRegisterAt(gp_index_++));
1361 } else {
1362 size_t stack_offset = calling_convention.GetStackOffsetOf(stack_index_);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001363 next_location = DataType::Is64BitType(type) ? Location::DoubleStackSlot(stack_offset)
1364 : Location::StackSlot(stack_offset);
Alexandre Rames5319def2014-10-23 10:03:10 +01001365 }
1366
Alexandre Ramesa89086e2014-11-07 17:13:25 +00001367 // Space on the stack is reserved for all arguments.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001368 stack_index_ += DataType::Is64BitType(type) ? 2 : 1;
Alexandre Rames5319def2014-10-23 10:03:10 +01001369 return next_location;
1370}
1371
Nicolas Geoffrayfd88f162015-06-03 11:23:52 +01001372Location InvokeDexCallingConventionVisitorARM64::GetMethodLocation() const {
Nicolas Geoffray38207af2015-06-01 15:46:22 +01001373 return LocationFrom(kArtMethodRegister);
Nicolas Geoffrayfd88f162015-06-03 11:23:52 +01001374}
1375
Serban Constantinescu579885a2015-02-22 20:51:33 +00001376CodeGeneratorARM64::CodeGeneratorARM64(HGraph* graph,
1377 const Arm64InstructionSetFeatures& isa_features,
Serban Constantinescuecc43662015-08-13 13:33:12 +01001378 const CompilerOptions& compiler_options,
1379 OptimizingCompilerStats* stats)
Alexandre Rames5319def2014-10-23 10:03:10 +01001380 : CodeGenerator(graph,
1381 kNumberOfAllocatableRegisters,
Alexandre Ramesa89086e2014-11-07 17:13:25 +00001382 kNumberOfAllocatableFPRegisters,
Calin Juravlecd6dffe2015-01-08 17:35:35 +00001383 kNumberOfAllocatableRegisterPairs,
Scott Wakeling97c72b72016-06-24 16:19:36 +01001384 callee_saved_core_registers.GetList(),
1385 callee_saved_fp_registers.GetList(),
Serban Constantinescuecc43662015-08-13 13:33:12 +01001386 compiler_options,
1387 stats),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001388 block_labels_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1389 jump_tables_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Alexandre Rames5319def2014-10-23 10:03:10 +01001390 location_builder_(graph, this),
Alexandre Rames3e69f162014-12-10 10:36:50 +00001391 instruction_visitor_(graph, this),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001392 move_resolver_(graph->GetAllocator(), this),
1393 assembler_(graph->GetAllocator()),
Vladimir Marko58155012015-08-19 12:49:41 +00001394 isa_features_(isa_features),
Vladimir Markocac5a7e2016-02-22 10:39:50 +00001395 uint32_literals_(std::less<uint32_t>(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001396 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko5233f932015-09-29 19:01:15 +01001397 uint64_literals_(std::less<uint64_t>(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001398 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001399 boot_image_method_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001400 method_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001401 boot_image_type_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001402 type_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00001403 boot_image_string_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001404 string_bss_entry_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
1405 baker_read_barrier_patches_(graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Nicolas Geoffray132d8362016-11-16 09:19:42 +00001406 jit_string_patches_(StringReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001407 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)),
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00001408 jit_class_patches_(TypeReferenceValueComparator(),
Vladimir Markoca6fff82017-10-03 14:49:14 +01001409 graph->GetAllocator()->Adapter(kArenaAllocCodeGenerator)) {
Nicolas Geoffrayd97dc402015-01-22 13:50:01 +00001410 // Save the link register (containing the return address) to mimic Quick.
Serban Constantinescu3d087de2015-01-28 11:57:05 +00001411 AddAllocatedRegister(LocationFrom(lr));
Nicolas Geoffrayd97dc402015-01-22 13:50:01 +00001412}
Alexandre Rames5319def2014-10-23 10:03:10 +01001413
Alexandre Rames67555f72014-11-18 10:55:16 +00001414#define __ GetVIXLAssembler()->
Alexandre Rames5319def2014-10-23 10:03:10 +01001415
Zheng Xu3927c8b2015-11-18 17:46:25 +08001416void CodeGeneratorARM64::EmitJumpTables() {
Alexandre Ramesc01a6642016-04-15 11:54:06 +01001417 for (auto&& jump_table : jump_tables_) {
Zheng Xu3927c8b2015-11-18 17:46:25 +08001418 jump_table->EmitTable(this);
1419 }
1420}
1421
Serban Constantinescu32f5b4d2014-11-25 20:05:46 +00001422void CodeGeneratorARM64::Finalize(CodeAllocator* allocator) {
Zheng Xu3927c8b2015-11-18 17:46:25 +08001423 EmitJumpTables();
Serban Constantinescu32f5b4d2014-11-25 20:05:46 +00001424 // Ensure we emit the literal pool.
1425 __ FinalizeCode();
Vladimir Marko58155012015-08-19 12:49:41 +00001426
Serban Constantinescu32f5b4d2014-11-25 20:05:46 +00001427 CodeGenerator::Finalize(allocator);
1428}
1429
Zheng Xuad4450e2015-04-17 18:48:56 +08001430void ParallelMoveResolverARM64::PrepareForEmitNativeCode() {
1431 // Note: There are 6 kinds of moves:
1432 // 1. constant -> GPR/FPR (non-cycle)
1433 // 2. constant -> stack (non-cycle)
1434 // 3. GPR/FPR -> GPR/FPR
1435 // 4. GPR/FPR -> stack
1436 // 5. stack -> GPR/FPR
1437 // 6. stack -> stack (non-cycle)
1438 // Case 1, 2 and 6 should never be included in a dependency cycle on ARM64. For case 3, 4, and 5
1439 // VIXL uses at most 1 GPR. VIXL has 2 GPR and 1 FPR temps, and there should be no intersecting
1440 // cycles on ARM64, so we always have 1 GPR and 1 FPR available VIXL temps to resolve the
1441 // dependency.
1442 vixl_temps_.Open(GetVIXLAssembler());
1443}
1444
1445void ParallelMoveResolverARM64::FinishEmitNativeCode() {
1446 vixl_temps_.Close();
1447}
1448
1449Location ParallelMoveResolverARM64::AllocateScratchLocationFor(Location::Kind kind) {
Artem Serovd4bccf12017-04-03 18:47:32 +01001450 DCHECK(kind == Location::kRegister || kind == Location::kFpuRegister
1451 || kind == Location::kStackSlot || kind == Location::kDoubleStackSlot
1452 || kind == Location::kSIMDStackSlot);
1453 kind = (kind == Location::kFpuRegister || kind == Location::kSIMDStackSlot)
1454 ? Location::kFpuRegister
1455 : Location::kRegister;
Zheng Xuad4450e2015-04-17 18:48:56 +08001456 Location scratch = GetScratchLocation(kind);
1457 if (!scratch.Equals(Location::NoLocation())) {
1458 return scratch;
1459 }
1460 // Allocate from VIXL temp registers.
1461 if (kind == Location::kRegister) {
1462 scratch = LocationFrom(vixl_temps_.AcquireX());
1463 } else {
Roland Levillain952b2352017-05-03 19:49:14 +01001464 DCHECK_EQ(kind, Location::kFpuRegister);
Artem Serovd4bccf12017-04-03 18:47:32 +01001465 scratch = LocationFrom(codegen_->GetGraph()->HasSIMD()
1466 ? vixl_temps_.AcquireVRegisterOfSize(kQRegSize)
1467 : vixl_temps_.AcquireD());
Zheng Xuad4450e2015-04-17 18:48:56 +08001468 }
1469 AddScratchLocation(scratch);
1470 return scratch;
1471}
1472
1473void ParallelMoveResolverARM64::FreeScratchLocation(Location loc) {
1474 if (loc.IsRegister()) {
1475 vixl_temps_.Release(XRegisterFrom(loc));
1476 } else {
1477 DCHECK(loc.IsFpuRegister());
Artem Serovd4bccf12017-04-03 18:47:32 +01001478 vixl_temps_.Release(codegen_->GetGraph()->HasSIMD() ? QRegisterFrom(loc) : DRegisterFrom(loc));
Zheng Xuad4450e2015-04-17 18:48:56 +08001479 }
1480 RemoveScratchLocation(loc);
1481}
1482
Alexandre Rames3e69f162014-12-10 10:36:50 +00001483void ParallelMoveResolverARM64::EmitMove(size_t index) {
Vladimir Marko225b6462015-09-28 12:17:40 +01001484 MoveOperands* move = moves_[index];
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001485 codegen_->MoveLocation(move->GetDestination(), move->GetSource(), DataType::Type::kVoid);
Alexandre Rames3e69f162014-12-10 10:36:50 +00001486}
1487
Alexandre Rames5319def2014-10-23 10:03:10 +01001488void CodeGeneratorARM64::GenerateFrameEntry() {
Alexandre Ramesd921d642015-04-16 15:07:16 +01001489 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffray1cf95282014-12-12 19:22:03 +00001490 __ Bind(&frame_entry_label_);
1491
Nicolas Geoffray8d728322018-01-18 22:44:32 +00001492 if (GetCompilerOptions().CountHotnessInCompiledCode()) {
1493 UseScratchRegisterScope temps(masm);
1494 Register temp = temps.AcquireX();
1495 __ Ldrh(temp, MemOperand(kArtMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
1496 __ Add(temp, temp, 1);
1497 __ Strh(temp, MemOperand(kArtMethodRegister, ArtMethod::HotnessCountOffset().Int32Value()));
1498 }
1499
Vladimir Marko33bff252017-11-01 14:35:42 +00001500 bool do_overflow_check =
1501 FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm64) || !IsLeafMethod();
Serban Constantinescu02164b32014-11-13 14:05:07 +00001502 if (do_overflow_check) {
Alexandre Ramesd921d642015-04-16 15:07:16 +01001503 UseScratchRegisterScope temps(masm);
Serban Constantinescu02164b32014-11-13 14:05:07 +00001504 Register temp = temps.AcquireX();
Nicolas Geoffrayd97dc402015-01-22 13:50:01 +00001505 DCHECK(GetCompilerOptions().GetImplicitStackOverflowChecks());
Vladimir Marko33bff252017-11-01 14:35:42 +00001506 __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(InstructionSet::kArm64)));
Artem Serov914d7a82017-02-07 14:33:49 +00001507 {
1508 // Ensure that between load and RecordPcInfo there are no pools emitted.
1509 ExactAssemblyScope eas(GetVIXLAssembler(),
1510 kInstructionSize,
1511 CodeBufferCheckScope::kExactSize);
1512 __ ldr(wzr, MemOperand(temp, 0));
1513 RecordPcInfo(nullptr, 0);
1514 }
Serban Constantinescu02164b32014-11-13 14:05:07 +00001515 }
Alexandre Rames5319def2014-10-23 10:03:10 +01001516
Nicolas Geoffrayc0572a42015-02-06 14:35:25 +00001517 if (!HasEmptyFrame()) {
1518 int frame_size = GetFrameSize();
1519 // Stack layout:
1520 // sp[frame_size - 8] : lr.
1521 // ... : other preserved core registers.
1522 // ... : other preserved fp registers.
1523 // ... : reserved frame space.
1524 // sp[0] : current method.
Nicolas Geoffray96eeb4e2016-10-12 22:03:31 +01001525
1526 // Save the current method if we need it. Note that we do not
1527 // do this in HCurrentMethod, as the instruction might have been removed
1528 // in the SSA graph.
1529 if (RequiresCurrentMethod()) {
1530 __ Str(kArtMethodRegister, MemOperand(sp, -frame_size, PreIndex));
Nicolas Geoffray9989b162016-10-13 13:42:30 +01001531 } else {
1532 __ Claim(frame_size);
Nicolas Geoffray96eeb4e2016-10-12 22:03:31 +01001533 }
David Srbeckyc6b4dd82015-04-07 20:32:43 +01001534 GetAssembler()->cfi().AdjustCFAOffset(frame_size);
Zheng Xu69a50302015-04-14 20:04:41 +08001535 GetAssembler()->SpillRegisters(GetFramePreservedCoreRegisters(),
1536 frame_size - GetCoreSpillSize());
1537 GetAssembler()->SpillRegisters(GetFramePreservedFPRegisters(),
1538 frame_size - FrameEntrySpillSize());
Mingyao Yang063fc772016-08-02 11:02:54 -07001539
1540 if (GetGraph()->HasShouldDeoptimizeFlag()) {
1541 // Initialize should_deoptimize flag to 0.
1542 Register wzr = Register(VIXLRegCodeFromART(WZR), kWRegSize);
1543 __ Str(wzr, MemOperand(sp, GetStackOffsetOfShouldDeoptimizeFlag()));
1544 }
Nicolas Geoffrayc0572a42015-02-06 14:35:25 +00001545 }
Roland Levillain2b03a1f2017-06-06 16:09:59 +01001546
1547 MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames5319def2014-10-23 10:03:10 +01001548}
1549
1550void CodeGeneratorARM64::GenerateFrameExit() {
David Srbeckyc34dc932015-04-12 09:27:43 +01001551 GetAssembler()->cfi().RememberState();
Nicolas Geoffrayc0572a42015-02-06 14:35:25 +00001552 if (!HasEmptyFrame()) {
1553 int frame_size = GetFrameSize();
Zheng Xu69a50302015-04-14 20:04:41 +08001554 GetAssembler()->UnspillRegisters(GetFramePreservedFPRegisters(),
1555 frame_size - FrameEntrySpillSize());
1556 GetAssembler()->UnspillRegisters(GetFramePreservedCoreRegisters(),
1557 frame_size - GetCoreSpillSize());
Nicolas Geoffrayc0572a42015-02-06 14:35:25 +00001558 __ Drop(frame_size);
David Srbeckyc6b4dd82015-04-07 20:32:43 +01001559 GetAssembler()->cfi().AdjustCFAOffset(-frame_size);
Nicolas Geoffrayc0572a42015-02-06 14:35:25 +00001560 }
David Srbeckyc34dc932015-04-12 09:27:43 +01001561 __ Ret();
1562 GetAssembler()->cfi().RestoreState();
1563 GetAssembler()->cfi().DefCFAOffset(GetFrameSize());
Alexandre Rames5319def2014-10-23 10:03:10 +01001564}
1565
Scott Wakeling97c72b72016-06-24 16:19:36 +01001566CPURegList CodeGeneratorARM64::GetFramePreservedCoreRegisters() const {
Zheng Xuda403092015-04-24 17:35:39 +08001567 DCHECK(ArtVixlRegCodeCoherentForRegSet(core_spill_mask_, GetNumberOfCoreRegisters(), 0, 0));
Scott Wakeling97c72b72016-06-24 16:19:36 +01001568 return CPURegList(CPURegister::kRegister, kXRegSize,
1569 core_spill_mask_);
Zheng Xuda403092015-04-24 17:35:39 +08001570}
1571
Scott Wakeling97c72b72016-06-24 16:19:36 +01001572CPURegList CodeGeneratorARM64::GetFramePreservedFPRegisters() const {
Zheng Xuda403092015-04-24 17:35:39 +08001573 DCHECK(ArtVixlRegCodeCoherentForRegSet(0, 0, fpu_spill_mask_,
1574 GetNumberOfFloatingPointRegisters()));
Scott Wakeling97c72b72016-06-24 16:19:36 +01001575 return CPURegList(CPURegister::kFPRegister, kDRegSize,
1576 fpu_spill_mask_);
Zheng Xuda403092015-04-24 17:35:39 +08001577}
1578
Alexandre Rames5319def2014-10-23 10:03:10 +01001579void CodeGeneratorARM64::Bind(HBasicBlock* block) {
1580 __ Bind(GetLabelOf(block));
1581}
1582
Calin Juravle175dc732015-08-25 15:42:32 +01001583void CodeGeneratorARM64::MoveConstant(Location location, int32_t value) {
1584 DCHECK(location.IsRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001585 __ Mov(RegisterFrom(location, DataType::Type::kInt32), value);
Calin Juravle175dc732015-08-25 15:42:32 +01001586}
1587
Calin Juravlee460d1d2015-09-29 04:52:17 +01001588void CodeGeneratorARM64::AddLocationAsTemp(Location location, LocationSummary* locations) {
1589 if (location.IsRegister()) {
1590 locations->AddTemp(location);
1591 } else {
1592 UNIMPLEMENTED(FATAL) << "AddLocationAsTemp not implemented for location " << location;
1593 }
1594}
1595
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001596void CodeGeneratorARM64::MarkGCCard(Register object, Register value, bool value_can_be_null) {
Alexandre Rames67555f72014-11-18 10:55:16 +00001597 UseScratchRegisterScope temps(GetVIXLAssembler());
Alexandre Rames5319def2014-10-23 10:03:10 +01001598 Register card = temps.AcquireX();
Serban Constantinescu02164b32014-11-13 14:05:07 +00001599 Register temp = temps.AcquireW(); // Index within the CardTable - 32bit.
Scott Wakeling97c72b72016-06-24 16:19:36 +01001600 vixl::aarch64::Label done;
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001601 if (value_can_be_null) {
1602 __ Cbz(value, &done);
1603 }
Andreas Gampe542451c2016-07-26 09:02:02 -07001604 __ Ldr(card, MemOperand(tr, Thread::CardTableOffset<kArm64PointerSize>().Int32Value()));
Alexandre Rames5319def2014-10-23 10:03:10 +01001605 __ Lsr(temp, object, gc::accounting::CardTable::kCardShift);
Serban Constantinescu02164b32014-11-13 14:05:07 +00001606 __ Strb(card, MemOperand(card, temp.X()));
Nicolas Geoffray07276db2015-05-18 14:22:09 +01001607 if (value_can_be_null) {
1608 __ Bind(&done);
1609 }
Alexandre Rames5319def2014-10-23 10:03:10 +01001610}
1611
David Brazdil58282f42016-01-14 12:45:10 +00001612void CodeGeneratorARM64::SetupBlockedRegisters() const {
Serban Constantinescu3d087de2015-01-28 11:57:05 +00001613 // Blocked core registers:
1614 // lr : Runtime reserved.
1615 // tr : Runtime reserved.
Roland Levillain97c46462017-05-11 14:04:03 +01001616 // mr : Runtime reserved.
Serban Constantinescu3d087de2015-01-28 11:57:05 +00001617 // ip1 : VIXL core temp.
1618 // ip0 : VIXL core temp.
1619 //
1620 // Blocked fp registers:
1621 // d31 : VIXL fp temp.
Alexandre Rames5319def2014-10-23 10:03:10 +01001622 CPURegList reserved_core_registers = vixl_reserved_core_registers;
1623 reserved_core_registers.Combine(runtime_reserved_core_registers);
Alexandre Rames5319def2014-10-23 10:03:10 +01001624 while (!reserved_core_registers.IsEmpty()) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001625 blocked_core_registers_[reserved_core_registers.PopLowestIndex().GetCode()] = true;
Alexandre Rames5319def2014-10-23 10:03:10 +01001626 }
Serban Constantinescu3d087de2015-01-28 11:57:05 +00001627
Alexandre Ramesa89086e2014-11-07 17:13:25 +00001628 CPURegList reserved_fp_registers = vixl_reserved_fp_registers;
Zheng Xua3ec3942015-02-15 18:39:46 +08001629 while (!reserved_fp_registers.IsEmpty()) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001630 blocked_fpu_registers_[reserved_fp_registers.PopLowestIndex().GetCode()] = true;
Alexandre Ramesa89086e2014-11-07 17:13:25 +00001631 }
Serban Constantinescu3d087de2015-01-28 11:57:05 +00001632
David Brazdil58282f42016-01-14 12:45:10 +00001633 if (GetGraph()->IsDebuggable()) {
Nicolas Geoffrayecf680d2015-10-05 11:15:37 +01001634 // Stubs do not save callee-save floating point registers. If the graph
1635 // is debuggable, we need to deal with these registers differently. For
1636 // now, just block them.
David Brazdil58282f42016-01-14 12:45:10 +00001637 CPURegList reserved_fp_registers_debuggable = callee_saved_fp_registers;
1638 while (!reserved_fp_registers_debuggable.IsEmpty()) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01001639 blocked_fpu_registers_[reserved_fp_registers_debuggable.PopLowestIndex().GetCode()] = true;
Serban Constantinescu3d087de2015-01-28 11:57:05 +00001640 }
1641 }
Alexandre Rames5319def2014-10-23 10:03:10 +01001642}
1643
Alexandre Rames3e69f162014-12-10 10:36:50 +00001644size_t CodeGeneratorARM64::SaveCoreRegister(size_t stack_index, uint32_t reg_id) {
1645 Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize);
1646 __ Str(reg, MemOperand(sp, stack_index));
1647 return kArm64WordSize;
1648}
1649
1650size_t CodeGeneratorARM64::RestoreCoreRegister(size_t stack_index, uint32_t reg_id) {
1651 Register reg = Register(VIXLRegCodeFromART(reg_id), kXRegSize);
1652 __ Ldr(reg, MemOperand(sp, stack_index));
1653 return kArm64WordSize;
1654}
1655
1656size_t CodeGeneratorARM64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1657 FPRegister reg = FPRegister(reg_id, kDRegSize);
1658 __ Str(reg, MemOperand(sp, stack_index));
1659 return kArm64WordSize;
1660}
1661
1662size_t CodeGeneratorARM64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) {
1663 FPRegister reg = FPRegister(reg_id, kDRegSize);
1664 __ Ldr(reg, MemOperand(sp, stack_index));
1665 return kArm64WordSize;
1666}
1667
Alexandre Rames5319def2014-10-23 10:03:10 +01001668void CodeGeneratorARM64::DumpCoreRegister(std::ostream& stream, int reg) const {
David Brazdilc74652862015-05-13 17:50:09 +01001669 stream << XRegister(reg);
Alexandre Rames5319def2014-10-23 10:03:10 +01001670}
1671
1672void CodeGeneratorARM64::DumpFloatingPointRegister(std::ostream& stream, int reg) const {
David Brazdilc74652862015-05-13 17:50:09 +01001673 stream << DRegister(reg);
Alexandre Rames5319def2014-10-23 10:03:10 +01001674}
1675
Alexandre Rames67555f72014-11-18 10:55:16 +00001676void CodeGeneratorARM64::MoveConstant(CPURegister destination, HConstant* constant) {
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +00001677 if (constant->IsIntConstant()) {
1678 __ Mov(Register(destination), constant->AsIntConstant()->GetValue());
1679 } else if (constant->IsLongConstant()) {
1680 __ Mov(Register(destination), constant->AsLongConstant()->GetValue());
1681 } else if (constant->IsNullConstant()) {
1682 __ Mov(Register(destination), 0);
Alexandre Rames67555f72014-11-18 10:55:16 +00001683 } else if (constant->IsFloatConstant()) {
1684 __ Fmov(FPRegister(destination), constant->AsFloatConstant()->GetValue());
1685 } else {
1686 DCHECK(constant->IsDoubleConstant());
1687 __ Fmov(FPRegister(destination), constant->AsDoubleConstant()->GetValue());
1688 }
1689}
1690
Alexandre Rames3e69f162014-12-10 10:36:50 +00001691
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001692static bool CoherentConstantAndType(Location constant, DataType::Type type) {
Alexandre Rames3e69f162014-12-10 10:36:50 +00001693 DCHECK(constant.IsConstant());
1694 HConstant* cst = constant.GetConstant();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001695 return (cst->IsIntConstant() && type == DataType::Type::kInt32) ||
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +00001696 // Null is mapped to a core W register, which we associate with kPrimInt.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001697 (cst->IsNullConstant() && type == DataType::Type::kInt32) ||
1698 (cst->IsLongConstant() && type == DataType::Type::kInt64) ||
1699 (cst->IsFloatConstant() && type == DataType::Type::kFloat32) ||
1700 (cst->IsDoubleConstant() && type == DataType::Type::kFloat64);
Alexandre Rames3e69f162014-12-10 10:36:50 +00001701}
1702
Roland Levillain952b2352017-05-03 19:49:14 +01001703// Allocate a scratch register from the VIXL pool, querying first
1704// the floating-point register pool, and then the core register
1705// pool. This is essentially a reimplementation of
Roland Levillain558dea12017-01-27 19:40:44 +00001706// vixl::aarch64::UseScratchRegisterScope::AcquireCPURegisterOfSize
1707// using a different allocation strategy.
1708static CPURegister AcquireFPOrCoreCPURegisterOfSize(vixl::aarch64::MacroAssembler* masm,
1709 vixl::aarch64::UseScratchRegisterScope* temps,
1710 int size_in_bits) {
1711 return masm->GetScratchFPRegisterList()->IsEmpty()
1712 ? CPURegister(temps->AcquireRegisterOfSize(size_in_bits))
1713 : CPURegister(temps->AcquireVRegisterOfSize(size_in_bits));
1714}
1715
Calin Juravlee460d1d2015-09-29 04:52:17 +01001716void CodeGeneratorARM64::MoveLocation(Location destination,
1717 Location source,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001718 DataType::Type dst_type) {
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001719 if (source.Equals(destination)) {
1720 return;
1721 }
Alexandre Rames3e69f162014-12-10 10:36:50 +00001722
1723 // A valid move can always be inferred from the destination and source
1724 // locations. When moving from and to a register, the argument type can be
1725 // used to generate 32bit instead of 64bit moves. In debug mode we also
1726 // checks the coherency of the locations and the type.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001727 bool unspecified_type = (dst_type == DataType::Type::kVoid);
Alexandre Rames3e69f162014-12-10 10:36:50 +00001728
1729 if (destination.IsRegister() || destination.IsFpuRegister()) {
1730 if (unspecified_type) {
1731 HConstant* src_cst = source.IsConstant() ? source.GetConstant() : nullptr;
1732 if (source.IsStackSlot() ||
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +00001733 (src_cst != nullptr && (src_cst->IsIntConstant()
1734 || src_cst->IsFloatConstant()
1735 || src_cst->IsNullConstant()))) {
Alexandre Rames3e69f162014-12-10 10:36:50 +00001736 // For stack slots and 32bit constants, a 64bit type is appropriate.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001737 dst_type = destination.IsRegister() ? DataType::Type::kInt32 : DataType::Type::kFloat32;
Alexandre Rames67555f72014-11-18 10:55:16 +00001738 } else {
Alexandre Rames3e69f162014-12-10 10:36:50 +00001739 // If the source is a double stack slot or a 64bit constant, a 64bit
1740 // type is appropriate. Else the source is a register, and since the
1741 // type has not been specified, we chose a 64bit type to force a 64bit
1742 // move.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001743 dst_type = destination.IsRegister() ? DataType::Type::kInt64 : DataType::Type::kFloat64;
Alexandre Rames67555f72014-11-18 10:55:16 +00001744 }
Alexandre Rames3e69f162014-12-10 10:36:50 +00001745 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001746 DCHECK((destination.IsFpuRegister() && DataType::IsFloatingPointType(dst_type)) ||
1747 (destination.IsRegister() && !DataType::IsFloatingPointType(dst_type)));
Calin Juravlee460d1d2015-09-29 04:52:17 +01001748 CPURegister dst = CPURegisterFrom(destination, dst_type);
Alexandre Rames3e69f162014-12-10 10:36:50 +00001749 if (source.IsStackSlot() || source.IsDoubleStackSlot()) {
1750 DCHECK(dst.Is64Bits() == source.IsDoubleStackSlot());
1751 __ Ldr(dst, StackOperandFrom(source));
Artem Serovd4bccf12017-04-03 18:47:32 +01001752 } else if (source.IsSIMDStackSlot()) {
1753 __ Ldr(QRegisterFrom(destination), StackOperandFrom(source));
Alexandre Rames3e69f162014-12-10 10:36:50 +00001754 } else if (source.IsConstant()) {
Calin Juravlee460d1d2015-09-29 04:52:17 +01001755 DCHECK(CoherentConstantAndType(source, dst_type));
Alexandre Rames3e69f162014-12-10 10:36:50 +00001756 MoveConstant(dst, source.GetConstant());
Calin Juravlee460d1d2015-09-29 04:52:17 +01001757 } else if (source.IsRegister()) {
Alexandre Rames3e69f162014-12-10 10:36:50 +00001758 if (destination.IsRegister()) {
Calin Juravlee460d1d2015-09-29 04:52:17 +01001759 __ Mov(Register(dst), RegisterFrom(source, dst_type));
Alexandre Rames3e69f162014-12-10 10:36:50 +00001760 } else {
Zheng Xuad4450e2015-04-17 18:48:56 +08001761 DCHECK(destination.IsFpuRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001762 DataType::Type source_type = DataType::Is64BitType(dst_type)
1763 ? DataType::Type::kInt64
1764 : DataType::Type::kInt32;
Calin Juravlee460d1d2015-09-29 04:52:17 +01001765 __ Fmov(FPRegisterFrom(destination, dst_type), RegisterFrom(source, source_type));
1766 }
1767 } else {
1768 DCHECK(source.IsFpuRegister());
1769 if (destination.IsRegister()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001770 DataType::Type source_type = DataType::Is64BitType(dst_type)
1771 ? DataType::Type::kFloat64
1772 : DataType::Type::kFloat32;
Calin Juravlee460d1d2015-09-29 04:52:17 +01001773 __ Fmov(RegisterFrom(destination, dst_type), FPRegisterFrom(source, source_type));
1774 } else {
1775 DCHECK(destination.IsFpuRegister());
Artem Serovd4bccf12017-04-03 18:47:32 +01001776 if (GetGraph()->HasSIMD()) {
1777 __ Mov(QRegisterFrom(destination), QRegisterFrom(source));
1778 } else {
1779 __ Fmov(FPRegister(dst), FPRegisterFrom(source, dst_type));
1780 }
1781 }
1782 }
1783 } else if (destination.IsSIMDStackSlot()) {
1784 if (source.IsFpuRegister()) {
1785 __ Str(QRegisterFrom(source), StackOperandFrom(destination));
1786 } else {
1787 DCHECK(source.IsSIMDStackSlot());
1788 UseScratchRegisterScope temps(GetVIXLAssembler());
1789 if (GetVIXLAssembler()->GetScratchFPRegisterList()->IsEmpty()) {
1790 Register temp = temps.AcquireX();
1791 __ Ldr(temp, MemOperand(sp, source.GetStackIndex()));
1792 __ Str(temp, MemOperand(sp, destination.GetStackIndex()));
1793 __ Ldr(temp, MemOperand(sp, source.GetStackIndex() + kArm64WordSize));
1794 __ Str(temp, MemOperand(sp, destination.GetStackIndex() + kArm64WordSize));
1795 } else {
1796 FPRegister temp = temps.AcquireVRegisterOfSize(kQRegSize);
1797 __ Ldr(temp, StackOperandFrom(source));
1798 __ Str(temp, StackOperandFrom(destination));
Alexandre Rames3e69f162014-12-10 10:36:50 +00001799 }
1800 }
Alexandre Rames3e69f162014-12-10 10:36:50 +00001801 } else { // The destination is not a register. It must be a stack slot.
1802 DCHECK(destination.IsStackSlot() || destination.IsDoubleStackSlot());
1803 if (source.IsRegister() || source.IsFpuRegister()) {
1804 if (unspecified_type) {
1805 if (source.IsRegister()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001806 dst_type = destination.IsStackSlot() ? DataType::Type::kInt32 : DataType::Type::kInt64;
Alexandre Rames3e69f162014-12-10 10:36:50 +00001807 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001808 dst_type =
1809 destination.IsStackSlot() ? DataType::Type::kFloat32 : DataType::Type::kFloat64;
Alexandre Rames3e69f162014-12-10 10:36:50 +00001810 }
1811 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001812 DCHECK((destination.IsDoubleStackSlot() == DataType::Is64BitType(dst_type)) &&
1813 (source.IsFpuRegister() == DataType::IsFloatingPointType(dst_type)));
Calin Juravlee460d1d2015-09-29 04:52:17 +01001814 __ Str(CPURegisterFrom(source, dst_type), StackOperandFrom(destination));
Alexandre Rames3e69f162014-12-10 10:36:50 +00001815 } else if (source.IsConstant()) {
Calin Juravlee460d1d2015-09-29 04:52:17 +01001816 DCHECK(unspecified_type || CoherentConstantAndType(source, dst_type))
1817 << source << " " << dst_type;
Alexandre Rames3e69f162014-12-10 10:36:50 +00001818 UseScratchRegisterScope temps(GetVIXLAssembler());
1819 HConstant* src_cst = source.GetConstant();
1820 CPURegister temp;
Alexandre Ramesb2b753c2016-08-02 13:45:28 +01001821 if (src_cst->IsZeroBitPattern()) {
Scott Wakeling79db9972017-01-19 14:08:42 +00001822 temp = (src_cst->IsLongConstant() || src_cst->IsDoubleConstant())
1823 ? Register(xzr)
1824 : Register(wzr);
Alexandre Rames3e69f162014-12-10 10:36:50 +00001825 } else {
Alexandre Ramesb2b753c2016-08-02 13:45:28 +01001826 if (src_cst->IsIntConstant()) {
1827 temp = temps.AcquireW();
1828 } else if (src_cst->IsLongConstant()) {
1829 temp = temps.AcquireX();
1830 } else if (src_cst->IsFloatConstant()) {
1831 temp = temps.AcquireS();
1832 } else {
1833 DCHECK(src_cst->IsDoubleConstant());
1834 temp = temps.AcquireD();
1835 }
1836 MoveConstant(temp, src_cst);
Alexandre Rames3e69f162014-12-10 10:36:50 +00001837 }
Alexandre Rames67555f72014-11-18 10:55:16 +00001838 __ Str(temp, StackOperandFrom(destination));
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001839 } else {
Alexandre Rames67555f72014-11-18 10:55:16 +00001840 DCHECK(source.IsStackSlot() || source.IsDoubleStackSlot());
Alexandre Rames3e69f162014-12-10 10:36:50 +00001841 DCHECK(source.IsDoubleStackSlot() == destination.IsDoubleStackSlot());
Alexandre Rames67555f72014-11-18 10:55:16 +00001842 UseScratchRegisterScope temps(GetVIXLAssembler());
Roland Levillain78b3d5d2017-01-04 10:27:50 +00001843 // Use any scratch register (a core or a floating-point one)
1844 // from VIXL scratch register pools as a temporary.
1845 //
1846 // We used to only use the FP scratch register pool, but in some
1847 // rare cases the only register from this pool (D31) would
1848 // already be used (e.g. within a ParallelMove instruction, when
1849 // a move is blocked by a another move requiring a scratch FP
1850 // register, which would reserve D31). To prevent this issue, we
1851 // ask for a scratch register of any type (core or FP).
Roland Levillain558dea12017-01-27 19:40:44 +00001852 //
1853 // Also, we start by asking for a FP scratch register first, as the
Roland Levillain952b2352017-05-03 19:49:14 +01001854 // demand of scratch core registers is higher. This is why we
Roland Levillain558dea12017-01-27 19:40:44 +00001855 // use AcquireFPOrCoreCPURegisterOfSize instead of
1856 // UseScratchRegisterScope::AcquireCPURegisterOfSize, which
1857 // allocates core scratch registers first.
1858 CPURegister temp = AcquireFPOrCoreCPURegisterOfSize(
1859 GetVIXLAssembler(),
1860 &temps,
1861 (destination.IsDoubleStackSlot() ? kXRegSize : kWRegSize));
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001862 __ Ldr(temp, StackOperandFrom(source));
1863 __ Str(temp, StackOperandFrom(destination));
1864 }
1865 }
1866}
1867
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001868void CodeGeneratorARM64::Load(DataType::Type type,
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001869 CPURegister dst,
1870 const MemOperand& src) {
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001871 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001872 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01001873 case DataType::Type::kUint8:
Alexandre Rames67555f72014-11-18 10:55:16 +00001874 __ Ldrb(Register(dst), src);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001875 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001876 case DataType::Type::kInt8:
Alexandre Rames67555f72014-11-18 10:55:16 +00001877 __ Ldrsb(Register(dst), src);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001878 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001879 case DataType::Type::kUint16:
Alexandre Rames67555f72014-11-18 10:55:16 +00001880 __ Ldrh(Register(dst), src);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001881 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01001882 case DataType::Type::kInt16:
1883 __ Ldrsh(Register(dst), src);
1884 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001885 case DataType::Type::kInt32:
1886 case DataType::Type::kReference:
1887 case DataType::Type::kInt64:
1888 case DataType::Type::kFloat32:
1889 case DataType::Type::kFloat64:
1890 DCHECK_EQ(dst.Is64Bits(), DataType::Is64BitType(type));
Alexandre Rames67555f72014-11-18 10:55:16 +00001891 __ Ldr(dst, src);
1892 break;
Aart Bik66c158e2018-01-31 12:55:04 -08001893 case DataType::Type::kUint32:
1894 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001895 case DataType::Type::kVoid:
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001896 LOG(FATAL) << "Unreachable type " << type;
1897 }
1898}
1899
Calin Juravle77520bc2015-01-12 18:45:46 +00001900void CodeGeneratorARM64::LoadAcquire(HInstruction* instruction,
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001901 CPURegister dst,
Roland Levillain44015862016-01-22 11:47:17 +00001902 const MemOperand& src,
1903 bool needs_null_check) {
Alexandre Ramesd921d642015-04-16 15:07:16 +01001904 MacroAssembler* masm = GetVIXLAssembler();
Alexandre Ramesd921d642015-04-16 15:07:16 +01001905 UseScratchRegisterScope temps(masm);
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001906 Register temp_base = temps.AcquireX();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001907 DataType::Type type = instruction->GetType();
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001908
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001909 DCHECK(!src.IsPreIndex());
1910 DCHECK(!src.IsPostIndex());
1911
1912 // TODO(vixl): Let the MacroAssembler handle MemOperand.
Scott Wakeling97c72b72016-06-24 16:19:36 +01001913 __ Add(temp_base, src.GetBaseRegister(), OperandFromMemOperand(src));
Artem Serov914d7a82017-02-07 14:33:49 +00001914 {
1915 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
1916 MemOperand base = MemOperand(temp_base);
1917 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001918 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01001919 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001920 case DataType::Type::kInt8:
Artem Serov914d7a82017-02-07 14:33:49 +00001921 {
1922 ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
1923 __ ldarb(Register(dst), base);
1924 if (needs_null_check) {
1925 MaybeRecordImplicitNullCheck(instruction);
1926 }
1927 }
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01001928 if (type == DataType::Type::kInt8) {
1929 __ Sbfx(Register(dst), Register(dst), 0, DataType::Size(type) * kBitsPerByte);
Artem Serov914d7a82017-02-07 14:33:49 +00001930 }
1931 break;
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01001932 case DataType::Type::kUint16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001933 case DataType::Type::kInt16:
Artem Serov914d7a82017-02-07 14:33:49 +00001934 {
1935 ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
1936 __ ldarh(Register(dst), base);
1937 if (needs_null_check) {
1938 MaybeRecordImplicitNullCheck(instruction);
1939 }
1940 }
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01001941 if (type == DataType::Type::kInt16) {
1942 __ Sbfx(Register(dst), Register(dst), 0, DataType::Size(type) * kBitsPerByte);
1943 }
Artem Serov914d7a82017-02-07 14:33:49 +00001944 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001945 case DataType::Type::kInt32:
1946 case DataType::Type::kReference:
1947 case DataType::Type::kInt64:
1948 DCHECK_EQ(dst.Is64Bits(), DataType::Is64BitType(type));
Artem Serov914d7a82017-02-07 14:33:49 +00001949 {
1950 ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
1951 __ ldar(Register(dst), base);
1952 if (needs_null_check) {
1953 MaybeRecordImplicitNullCheck(instruction);
1954 }
1955 }
1956 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001957 case DataType::Type::kFloat32:
1958 case DataType::Type::kFloat64: {
Artem Serov914d7a82017-02-07 14:33:49 +00001959 DCHECK(dst.IsFPRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001960 DCHECK_EQ(dst.Is64Bits(), DataType::Is64BitType(type));
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001961
Artem Serov914d7a82017-02-07 14:33:49 +00001962 Register temp = dst.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
1963 {
1964 ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
1965 __ ldar(temp, base);
1966 if (needs_null_check) {
1967 MaybeRecordImplicitNullCheck(instruction);
1968 }
1969 }
1970 __ Fmov(FPRegister(dst), temp);
1971 break;
Roland Levillain44015862016-01-22 11:47:17 +00001972 }
Aart Bik66c158e2018-01-31 12:55:04 -08001973 case DataType::Type::kUint32:
1974 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001975 case DataType::Type::kVoid:
Artem Serov914d7a82017-02-07 14:33:49 +00001976 LOG(FATAL) << "Unreachable type " << type;
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001977 }
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001978 }
1979}
1980
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001981void CodeGeneratorARM64::Store(DataType::Type type,
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001982 CPURegister src,
1983 const MemOperand& dst) {
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001984 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001985 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01001986 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001987 case DataType::Type::kInt8:
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001988 __ Strb(Register(src), dst);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001989 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001990 case DataType::Type::kUint16:
1991 case DataType::Type::kInt16:
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00001992 __ Strh(Register(src), dst);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00001993 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01001994 case DataType::Type::kInt32:
1995 case DataType::Type::kReference:
1996 case DataType::Type::kInt64:
1997 case DataType::Type::kFloat32:
1998 case DataType::Type::kFloat64:
1999 DCHECK_EQ(src.Is64Bits(), DataType::Is64BitType(type));
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002000 __ Str(src, dst);
Alexandre Rames67555f72014-11-18 10:55:16 +00002001 break;
Aart Bik66c158e2018-01-31 12:55:04 -08002002 case DataType::Type::kUint32:
2003 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002004 case DataType::Type::kVoid:
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002005 LOG(FATAL) << "Unreachable type " << type;
2006 }
2007}
2008
Artem Serov914d7a82017-02-07 14:33:49 +00002009void CodeGeneratorARM64::StoreRelease(HInstruction* instruction,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002010 DataType::Type type,
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002011 CPURegister src,
Artem Serov914d7a82017-02-07 14:33:49 +00002012 const MemOperand& dst,
2013 bool needs_null_check) {
2014 MacroAssembler* masm = GetVIXLAssembler();
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002015 UseScratchRegisterScope temps(GetVIXLAssembler());
2016 Register temp_base = temps.AcquireX();
2017
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002018 DCHECK(!dst.IsPreIndex());
2019 DCHECK(!dst.IsPostIndex());
2020
2021 // TODO(vixl): Let the MacroAssembler handle this.
Andreas Gampe878d58c2015-01-15 23:24:00 -08002022 Operand op = OperandFromMemOperand(dst);
Scott Wakeling97c72b72016-06-24 16:19:36 +01002023 __ Add(temp_base, dst.GetBaseRegister(), op);
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002024 MemOperand base = MemOperand(temp_base);
Artem Serov914d7a82017-02-07 14:33:49 +00002025 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002026 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002027 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01002028 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002029 case DataType::Type::kInt8:
Artem Serov914d7a82017-02-07 14:33:49 +00002030 {
2031 ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
2032 __ stlrb(Register(src), base);
2033 if (needs_null_check) {
2034 MaybeRecordImplicitNullCheck(instruction);
2035 }
2036 }
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002037 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002038 case DataType::Type::kUint16:
2039 case DataType::Type::kInt16:
Artem Serov914d7a82017-02-07 14:33:49 +00002040 {
2041 ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
2042 __ stlrh(Register(src), base);
2043 if (needs_null_check) {
2044 MaybeRecordImplicitNullCheck(instruction);
2045 }
2046 }
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002047 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002048 case DataType::Type::kInt32:
2049 case DataType::Type::kReference:
2050 case DataType::Type::kInt64:
2051 DCHECK_EQ(src.Is64Bits(), DataType::Is64BitType(type));
Artem Serov914d7a82017-02-07 14:33:49 +00002052 {
2053 ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
2054 __ stlr(Register(src), base);
2055 if (needs_null_check) {
2056 MaybeRecordImplicitNullCheck(instruction);
2057 }
2058 }
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002059 break;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002060 case DataType::Type::kFloat32:
2061 case DataType::Type::kFloat64: {
2062 DCHECK_EQ(src.Is64Bits(), DataType::Is64BitType(type));
Alexandre Ramesbe919d92016-08-23 18:33:36 +01002063 Register temp_src;
2064 if (src.IsZero()) {
2065 // The zero register is used to avoid synthesizing zero constants.
2066 temp_src = Register(src);
2067 } else {
2068 DCHECK(src.IsFPRegister());
2069 temp_src = src.Is64Bits() ? temps.AcquireX() : temps.AcquireW();
2070 __ Fmov(temp_src, FPRegister(src));
2071 }
Artem Serov914d7a82017-02-07 14:33:49 +00002072 {
2073 ExactAssemblyScope eas(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
2074 __ stlr(temp_src, base);
2075 if (needs_null_check) {
2076 MaybeRecordImplicitNullCheck(instruction);
2077 }
2078 }
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002079 break;
2080 }
Aart Bik66c158e2018-01-31 12:55:04 -08002081 case DataType::Type::kUint32:
2082 case DataType::Type::kUint64:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002083 case DataType::Type::kVoid:
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002084 LOG(FATAL) << "Unreachable type " << type;
2085 }
2086}
2087
Calin Juravle175dc732015-08-25 15:42:32 +01002088void CodeGeneratorARM64::InvokeRuntime(QuickEntrypointEnum entrypoint,
2089 HInstruction* instruction,
2090 uint32_t dex_pc,
2091 SlowPathCode* slow_path) {
Alexandre Rames91a65162016-09-19 13:54:30 +01002092 ValidateInvokeRuntime(entrypoint, instruction, slow_path);
Artem Serov914d7a82017-02-07 14:33:49 +00002093
2094 __ Ldr(lr, MemOperand(tr, GetThreadOffset<kArm64PointerSize>(entrypoint).Int32Value()));
2095 {
2096 // Ensure the pc position is recorded immediately after the `blr` instruction.
2097 ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
2098 __ blr(lr);
2099 if (EntrypointRequiresStackMap(entrypoint)) {
2100 RecordPcInfo(instruction, dex_pc, slow_path);
2101 }
Serban Constantinescuda8ffec2016-03-09 12:02:11 +00002102 }
Alexandre Rames67555f72014-11-18 10:55:16 +00002103}
2104
Roland Levillaindec8f632016-07-22 17:10:06 +01002105void CodeGeneratorARM64::InvokeRuntimeWithoutRecordingPcInfo(int32_t entry_point_offset,
2106 HInstruction* instruction,
2107 SlowPathCode* slow_path) {
2108 ValidateInvokeRuntimeWithoutRecordingPcInfo(instruction, slow_path);
Roland Levillaindec8f632016-07-22 17:10:06 +01002109 __ Ldr(lr, MemOperand(tr, entry_point_offset));
2110 __ Blr(lr);
2111}
2112
Alexandre Rames67555f72014-11-18 10:55:16 +00002113void InstructionCodeGeneratorARM64::GenerateClassInitializationCheck(SlowPathCodeARM64* slow_path,
Scott Wakeling97c72b72016-06-24 16:19:36 +01002114 Register class_reg) {
Alexandre Rames67555f72014-11-18 10:55:16 +00002115 UseScratchRegisterScope temps(GetVIXLAssembler());
2116 Register temp = temps.AcquireW();
Vladimir Markodc682aa2018-01-04 18:42:57 +00002117 constexpr size_t status_lsb_position = SubtypeCheckBits::BitStructSizeOf();
2118 const size_t status_byte_offset =
2119 mirror::Class::StatusOffset().SizeValue() + (status_lsb_position / kBitsPerByte);
2120 constexpr uint32_t shifted_initialized_value =
2121 enum_cast<uint32_t>(ClassStatus::kInitialized) << (status_lsb_position % kBitsPerByte);
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002122
Serban Constantinescu02164b32014-11-13 14:05:07 +00002123 // Even if the initialized flag is set, we need to ensure consistent memory ordering.
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00002124 // TODO(vixl): Let the MacroAssembler handle MemOperand.
Vladimir Markodc682aa2018-01-04 18:42:57 +00002125 __ Add(temp, class_reg, status_byte_offset);
Igor Murashkin86083f72017-10-27 10:59:04 -07002126 __ Ldarb(temp, HeapOperand(temp));
Vladimir Markodc682aa2018-01-04 18:42:57 +00002127 __ Cmp(temp, shifted_initialized_value);
Vladimir Marko2c64a832018-01-04 11:31:56 +00002128 __ B(lo, slow_path->GetEntryLabel());
Alexandre Rames67555f72014-11-18 10:55:16 +00002129 __ Bind(slow_path->GetExitLabel());
2130}
Alexandre Rames5319def2014-10-23 10:03:10 +01002131
Roland Levillain44015862016-01-22 11:47:17 +00002132void CodeGeneratorARM64::GenerateMemoryBarrier(MemBarrierKind kind) {
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00002133 BarrierType type = BarrierAll;
2134
2135 switch (kind) {
2136 case MemBarrierKind::kAnyAny:
2137 case MemBarrierKind::kAnyStore: {
2138 type = BarrierAll;
2139 break;
2140 }
2141 case MemBarrierKind::kLoadAny: {
2142 type = BarrierReads;
2143 break;
2144 }
2145 case MemBarrierKind::kStoreStore: {
2146 type = BarrierWrites;
2147 break;
2148 }
2149 default:
2150 LOG(FATAL) << "Unexpected memory barrier " << kind;
2151 }
2152 __ Dmb(InnerShareable, type);
2153}
2154
Serban Constantinescu02164b32014-11-13 14:05:07 +00002155void InstructionCodeGeneratorARM64::GenerateSuspendCheck(HSuspendCheck* instruction,
2156 HBasicBlock* successor) {
2157 SuspendCheckSlowPathARM64* slow_path =
Nicolas Geoffraydb216f42015-05-05 17:02:20 +01002158 down_cast<SuspendCheckSlowPathARM64*>(instruction->GetSlowPath());
2159 if (slow_path == nullptr) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01002160 slow_path =
2161 new (codegen_->GetScopedAllocator()) SuspendCheckSlowPathARM64(instruction, successor);
Nicolas Geoffraydb216f42015-05-05 17:02:20 +01002162 instruction->SetSlowPath(slow_path);
2163 codegen_->AddSlowPath(slow_path);
2164 if (successor != nullptr) {
2165 DCHECK(successor->IsLoopHeader());
Nicolas Geoffraydb216f42015-05-05 17:02:20 +01002166 }
2167 } else {
2168 DCHECK_EQ(slow_path->GetSuccessor(), successor);
2169 }
2170
Serban Constantinescu02164b32014-11-13 14:05:07 +00002171 UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
2172 Register temp = temps.AcquireW();
2173
Andreas Gampe542451c2016-07-26 09:02:02 -07002174 __ Ldrh(temp, MemOperand(tr, Thread::ThreadFlagsOffset<kArm64PointerSize>().SizeValue()));
Serban Constantinescu02164b32014-11-13 14:05:07 +00002175 if (successor == nullptr) {
2176 __ Cbnz(temp, slow_path->GetEntryLabel());
2177 __ Bind(slow_path->GetReturnLabel());
2178 } else {
2179 __ Cbz(temp, codegen_->GetLabelOf(successor));
2180 __ B(slow_path->GetEntryLabel());
2181 // slow_path will return to GetLabelOf(successor).
2182 }
2183}
2184
Alexandre Rames5319def2014-10-23 10:03:10 +01002185InstructionCodeGeneratorARM64::InstructionCodeGeneratorARM64(HGraph* graph,
2186 CodeGeneratorARM64* codegen)
Aart Bik42249c32016-01-07 15:33:50 -08002187 : InstructionCodeGenerator(graph, codegen),
Alexandre Rames5319def2014-10-23 10:03:10 +01002188 assembler_(codegen->GetAssembler()),
2189 codegen_(codegen) {}
2190
Alexandre Rames67555f72014-11-18 10:55:16 +00002191void LocationsBuilderARM64::HandleBinaryOp(HBinaryOperation* instr) {
Alexandre Rames5319def2014-10-23 10:03:10 +01002192 DCHECK_EQ(instr->InputCount(), 2U);
Vladimir Markoca6fff82017-10-03 14:49:14 +01002193 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instr);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002194 DataType::Type type = instr->GetResultType();
Alexandre Rames5319def2014-10-23 10:03:10 +01002195 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002196 case DataType::Type::kInt32:
2197 case DataType::Type::kInt64:
Alexandre Rames5319def2014-10-23 10:03:10 +01002198 locations->SetInAt(0, Location::RequiresRegister());
Serban Constantinescu2d35d9d2015-02-22 22:08:01 +00002199 locations->SetInAt(1, ARM64EncodableConstantOrRegister(instr->InputAt(1), instr));
Alexandre Ramesfb4e5fa2014-11-06 12:41:16 +00002200 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Alexandre Rames5319def2014-10-23 10:03:10 +01002201 break;
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002202
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002203 case DataType::Type::kFloat32:
2204 case DataType::Type::kFloat64:
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002205 locations->SetInAt(0, Location::RequiresFpuRegister());
2206 locations->SetInAt(1, Location::RequiresFpuRegister());
Alexandre Rames67555f72014-11-18 10:55:16 +00002207 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
Alexandre Rames5319def2014-10-23 10:03:10 +01002208 break;
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002209
Alexandre Rames5319def2014-10-23 10:03:10 +01002210 default:
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002211 LOG(FATAL) << "Unexpected " << instr->DebugName() << " type " << type;
Alexandre Rames5319def2014-10-23 10:03:10 +01002212 }
2213}
2214
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002215void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction,
2216 const FieldInfo& field_info) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002217 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
2218
2219 bool object_field_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002220 kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
Alexandre Rames09a99962015-04-15 11:47:56 +01002221 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002222 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
2223 object_field_get_with_read_barrier
2224 ? LocationSummary::kCallOnSlowPath
2225 : LocationSummary::kNoCall);
Vladimir Marko70e97462016-08-09 11:04:26 +01002226 if (object_field_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01002227 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Roland Levillaind0b51832017-01-26 19:04:23 +00002228 // We need a temporary register for the read barrier marking slow
2229 // path in CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002230 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
2231 !Runtime::Current()->UseJitCompilation() &&
2232 !field_info.IsVolatile()) {
2233 // If link-time thunks for the Baker read barrier are enabled, for AOT
2234 // non-volatile loads we need a temporary only if the offset is too big.
2235 if (field_info.GetFieldOffset().Uint32Value() >= kReferenceLoadMinFarOffset) {
2236 locations->AddTemp(FixedTempLocation());
2237 }
2238 } else {
2239 locations->AddTemp(Location::RequiresRegister());
2240 }
Vladimir Marko70e97462016-08-09 11:04:26 +01002241 }
Alexandre Rames09a99962015-04-15 11:47:56 +01002242 locations->SetInAt(0, Location::RequiresRegister());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002243 if (DataType::IsFloatingPointType(instruction->GetType())) {
Alexandre Rames09a99962015-04-15 11:47:56 +01002244 locations->SetOut(Location::RequiresFpuRegister());
2245 } else {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002246 // The output overlaps for an object field get when read barriers
2247 // are enabled: we do not want the load to overwrite the object's
2248 // location, as we need it to emit the read barrier.
2249 locations->SetOut(
2250 Location::RequiresRegister(),
2251 object_field_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
Alexandre Rames09a99962015-04-15 11:47:56 +01002252 }
2253}
2254
2255void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction,
2256 const FieldInfo& field_info) {
2257 DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet());
Roland Levillain44015862016-01-22 11:47:17 +00002258 LocationSummary* locations = instruction->GetLocations();
2259 Location base_loc = locations->InAt(0);
2260 Location out = locations->Out();
2261 uint32_t offset = field_info.GetFieldOffset().Uint32Value();
Vladimir Marko61b92282017-10-11 13:23:17 +01002262 DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType()));
2263 DataType::Type load_type = instruction->GetType();
Alexandre Rames09a99962015-04-15 11:47:56 +01002264 MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), field_info.GetFieldOffset());
Alexandre Rames09a99962015-04-15 11:47:56 +01002265
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002266 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier &&
Vladimir Marko61b92282017-10-11 13:23:17 +01002267 load_type == DataType::Type::kReference) {
Roland Levillain44015862016-01-22 11:47:17 +00002268 // Object FieldGet with Baker's read barrier case.
Roland Levillain44015862016-01-22 11:47:17 +00002269 // /* HeapReference<Object> */ out = *(base + offset)
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002270 Register base = RegisterFrom(base_loc, DataType::Type::kReference);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002271 Location maybe_temp =
2272 (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location::NoLocation();
Roland Levillain44015862016-01-22 11:47:17 +00002273 // Note that potential implicit null checks are handled in this
2274 // CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier call.
2275 codegen_->GenerateFieldLoadWithBakerReadBarrier(
2276 instruction,
2277 out,
2278 base,
2279 offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002280 maybe_temp,
Roland Levillain44015862016-01-22 11:47:17 +00002281 /* needs_null_check */ true,
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00002282 field_info.IsVolatile());
Roland Levillain44015862016-01-22 11:47:17 +00002283 } else {
2284 // General case.
2285 if (field_info.IsVolatile()) {
Serban Constantinescu4a6a67c2016-01-27 09:19:56 +00002286 // Note that a potential implicit null check is handled in this
2287 // CodeGeneratorARM64::LoadAcquire call.
2288 // NB: LoadAcquire will record the pc info if needed.
2289 codegen_->LoadAcquire(
2290 instruction, OutputCPURegister(instruction), field, /* needs_null_check */ true);
Alexandre Rames09a99962015-04-15 11:47:56 +01002291 } else {
Artem Serov914d7a82017-02-07 14:33:49 +00002292 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
2293 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Vladimir Marko61b92282017-10-11 13:23:17 +01002294 codegen_->Load(load_type, OutputCPURegister(instruction), field);
Alexandre Rames09a99962015-04-15 11:47:56 +01002295 codegen_->MaybeRecordImplicitNullCheck(instruction);
Alexandre Rames09a99962015-04-15 11:47:56 +01002296 }
Vladimir Marko61b92282017-10-11 13:23:17 +01002297 if (load_type == DataType::Type::kReference) {
Roland Levillain44015862016-01-22 11:47:17 +00002298 // If read barriers are enabled, emit read barriers other than
2299 // Baker's using a slow path (and also unpoison the loaded
2300 // reference, if heap poisoning is enabled).
2301 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, base_loc, offset);
2302 }
Roland Levillain4d027112015-07-01 15:41:14 +01002303 }
Alexandre Rames09a99962015-04-15 11:47:56 +01002304}
2305
2306void LocationsBuilderARM64::HandleFieldSet(HInstruction* instruction) {
2307 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002308 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Alexandre Rames09a99962015-04-15 11:47:56 +01002309 locations->SetInAt(0, Location::RequiresRegister());
Alexandre Ramesbe919d92016-08-23 18:33:36 +01002310 if (IsConstantZeroBitPattern(instruction->InputAt(1))) {
2311 locations->SetInAt(1, Location::ConstantLocation(instruction->InputAt(1)->AsConstant()));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002312 } else if (DataType::IsFloatingPointType(instruction->InputAt(1)->GetType())) {
Alexandre Rames09a99962015-04-15 11:47:56 +01002313 locations->SetInAt(1, Location::RequiresFpuRegister());
2314 } else {
2315 locations->SetInAt(1, Location::RequiresRegister());
2316 }
2317}
2318
2319void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction,
Nicolas Geoffray07276db2015-05-18 14:22:09 +01002320 const FieldInfo& field_info,
2321 bool value_can_be_null) {
Alexandre Rames09a99962015-04-15 11:47:56 +01002322 DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet());
2323
2324 Register obj = InputRegisterAt(instruction, 0);
Alexandre Ramesbe919d92016-08-23 18:33:36 +01002325 CPURegister value = InputCPURegisterOrZeroRegAt(instruction, 1);
Roland Levillain4d027112015-07-01 15:41:14 +01002326 CPURegister source = value;
Alexandre Rames09a99962015-04-15 11:47:56 +01002327 Offset offset = field_info.GetFieldOffset();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002328 DataType::Type field_type = field_info.GetFieldType();
Alexandre Rames09a99962015-04-15 11:47:56 +01002329
Roland Levillain4d027112015-07-01 15:41:14 +01002330 {
2331 // We use a block to end the scratch scope before the write barrier, thus
2332 // freeing the temporary registers so they can be used in `MarkGCCard`.
2333 UseScratchRegisterScope temps(GetVIXLAssembler());
2334
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002335 if (kPoisonHeapReferences && field_type == DataType::Type::kReference) {
Roland Levillain4d027112015-07-01 15:41:14 +01002336 DCHECK(value.IsW());
2337 Register temp = temps.AcquireW();
2338 __ Mov(temp, value.W());
2339 GetAssembler()->PoisonHeapReference(temp.W());
2340 source = temp;
Alexandre Rames09a99962015-04-15 11:47:56 +01002341 }
Roland Levillain4d027112015-07-01 15:41:14 +01002342
2343 if (field_info.IsVolatile()) {
Artem Serov914d7a82017-02-07 14:33:49 +00002344 codegen_->StoreRelease(
2345 instruction, field_type, source, HeapOperand(obj, offset), /* needs_null_check */ true);
Roland Levillain4d027112015-07-01 15:41:14 +01002346 } else {
Artem Serov914d7a82017-02-07 14:33:49 +00002347 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
2348 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Roland Levillain4d027112015-07-01 15:41:14 +01002349 codegen_->Store(field_type, source, HeapOperand(obj, offset));
2350 codegen_->MaybeRecordImplicitNullCheck(instruction);
2351 }
Alexandre Rames09a99962015-04-15 11:47:56 +01002352 }
2353
2354 if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01002355 codegen_->MarkGCCard(obj, Register(value), value_can_be_null);
Alexandre Rames09a99962015-04-15 11:47:56 +01002356 }
2357}
2358
Alexandre Rames67555f72014-11-18 10:55:16 +00002359void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002360 DataType::Type type = instr->GetType();
Alexandre Rames5319def2014-10-23 10:03:10 +01002361
2362 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002363 case DataType::Type::kInt32:
2364 case DataType::Type::kInt64: {
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002365 Register dst = OutputRegister(instr);
2366 Register lhs = InputRegisterAt(instr, 0);
2367 Operand rhs = InputOperandAt(instr, 1);
Alexandre Rames5319def2014-10-23 10:03:10 +01002368 if (instr->IsAdd()) {
2369 __ Add(dst, lhs, rhs);
Alexandre Rames67555f72014-11-18 10:55:16 +00002370 } else if (instr->IsAnd()) {
2371 __ And(dst, lhs, rhs);
2372 } else if (instr->IsOr()) {
2373 __ Orr(dst, lhs, rhs);
2374 } else if (instr->IsSub()) {
Alexandre Rames5319def2014-10-23 10:03:10 +01002375 __ Sub(dst, lhs, rhs);
Scott Wakeling40a04bf2015-12-11 09:50:36 +00002376 } else if (instr->IsRor()) {
2377 if (rhs.IsImmediate()) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002378 uint32_t shift = rhs.GetImmediate() & (lhs.GetSizeInBits() - 1);
Scott Wakeling40a04bf2015-12-11 09:50:36 +00002379 __ Ror(dst, lhs, shift);
2380 } else {
2381 // Ensure shift distance is in the same size register as the result. If
2382 // we are rotating a long and the shift comes in a w register originally,
2383 // we don't need to sxtw for use as an x since the shift distances are
2384 // all & reg_bits - 1.
2385 __ Ror(dst, lhs, RegisterFrom(instr->GetLocations()->InAt(1), type));
2386 }
Alexandre Rames67555f72014-11-18 10:55:16 +00002387 } else {
2388 DCHECK(instr->IsXor());
2389 __ Eor(dst, lhs, rhs);
Alexandre Rames5319def2014-10-23 10:03:10 +01002390 }
2391 break;
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002392 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002393 case DataType::Type::kFloat32:
2394 case DataType::Type::kFloat64: {
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002395 FPRegister dst = OutputFPRegister(instr);
2396 FPRegister lhs = InputFPRegisterAt(instr, 0);
2397 FPRegister rhs = InputFPRegisterAt(instr, 1);
2398 if (instr->IsAdd()) {
2399 __ Fadd(dst, lhs, rhs);
Alexandre Rames67555f72014-11-18 10:55:16 +00002400 } else if (instr->IsSub()) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002401 __ Fsub(dst, lhs, rhs);
Alexandre Rames67555f72014-11-18 10:55:16 +00002402 } else {
2403 LOG(FATAL) << "Unexpected floating-point binary operation";
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002404 }
Alexandre Rames5319def2014-10-23 10:03:10 +01002405 break;
Alexandre Ramesa89086e2014-11-07 17:13:25 +00002406 }
Alexandre Rames5319def2014-10-23 10:03:10 +01002407 default:
Alexandre Rames67555f72014-11-18 10:55:16 +00002408 LOG(FATAL) << "Unexpected binary operation type " << type;
Alexandre Rames5319def2014-10-23 10:03:10 +01002409 }
2410}
2411
Serban Constantinescu02164b32014-11-13 14:05:07 +00002412void LocationsBuilderARM64::HandleShift(HBinaryOperation* instr) {
2413 DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr());
2414
Vladimir Markoca6fff82017-10-03 14:49:14 +01002415 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instr);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002416 DataType::Type type = instr->GetResultType();
Serban Constantinescu02164b32014-11-13 14:05:07 +00002417 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002418 case DataType::Type::kInt32:
2419 case DataType::Type::kInt64: {
Serban Constantinescu02164b32014-11-13 14:05:07 +00002420 locations->SetInAt(0, Location::RequiresRegister());
2421 locations->SetInAt(1, Location::RegisterOrConstant(instr->InputAt(1)));
Artem Serov87c97052016-09-23 13:34:31 +01002422 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Serban Constantinescu02164b32014-11-13 14:05:07 +00002423 break;
2424 }
2425 default:
2426 LOG(FATAL) << "Unexpected shift type " << type;
2427 }
2428}
2429
2430void InstructionCodeGeneratorARM64::HandleShift(HBinaryOperation* instr) {
2431 DCHECK(instr->IsShl() || instr->IsShr() || instr->IsUShr());
2432
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002433 DataType::Type type = instr->GetType();
Serban Constantinescu02164b32014-11-13 14:05:07 +00002434 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002435 case DataType::Type::kInt32:
2436 case DataType::Type::kInt64: {
Serban Constantinescu02164b32014-11-13 14:05:07 +00002437 Register dst = OutputRegister(instr);
2438 Register lhs = InputRegisterAt(instr, 0);
2439 Operand rhs = InputOperandAt(instr, 1);
2440 if (rhs.IsImmediate()) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002441 uint32_t shift_value = rhs.GetImmediate() &
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002442 (type == DataType::Type::kInt32 ? kMaxIntShiftDistance : kMaxLongShiftDistance);
Serban Constantinescu02164b32014-11-13 14:05:07 +00002443 if (instr->IsShl()) {
2444 __ Lsl(dst, lhs, shift_value);
2445 } else if (instr->IsShr()) {
2446 __ Asr(dst, lhs, shift_value);
2447 } else {
2448 __ Lsr(dst, lhs, shift_value);
2449 }
2450 } else {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002451 Register rhs_reg = dst.IsX() ? rhs.GetRegister().X() : rhs.GetRegister().W();
Serban Constantinescu02164b32014-11-13 14:05:07 +00002452
2453 if (instr->IsShl()) {
2454 __ Lsl(dst, lhs, rhs_reg);
2455 } else if (instr->IsShr()) {
2456 __ Asr(dst, lhs, rhs_reg);
2457 } else {
2458 __ Lsr(dst, lhs, rhs_reg);
2459 }
2460 }
2461 break;
2462 }
2463 default:
2464 LOG(FATAL) << "Unexpected shift operation type " << type;
2465 }
2466}
2467
Alexandre Rames5319def2014-10-23 10:03:10 +01002468void LocationsBuilderARM64::VisitAdd(HAdd* instruction) {
Alexandre Rames67555f72014-11-18 10:55:16 +00002469 HandleBinaryOp(instruction);
Alexandre Rames5319def2014-10-23 10:03:10 +01002470}
2471
2472void InstructionCodeGeneratorARM64::VisitAdd(HAdd* instruction) {
Alexandre Rames67555f72014-11-18 10:55:16 +00002473 HandleBinaryOp(instruction);
2474}
2475
2476void LocationsBuilderARM64::VisitAnd(HAnd* instruction) {
2477 HandleBinaryOp(instruction);
2478}
2479
2480void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) {
2481 HandleBinaryOp(instruction);
Alexandre Rames5319def2014-10-23 10:03:10 +01002482}
2483
Artem Serov7fc63502016-02-09 17:15:29 +00002484void LocationsBuilderARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instr) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002485 DCHECK(DataType::IsIntegralType(instr->GetType())) << instr->GetType();
Vladimir Markoca6fff82017-10-03 14:49:14 +01002486 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instr);
Kevin Brodsky9ff0d202016-01-11 13:43:31 +00002487 locations->SetInAt(0, Location::RequiresRegister());
2488 // There is no immediate variant of negated bitwise instructions in AArch64.
2489 locations->SetInAt(1, Location::RequiresRegister());
2490 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2491}
2492
Artem Serov7fc63502016-02-09 17:15:29 +00002493void InstructionCodeGeneratorARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRight* instr) {
Kevin Brodsky9ff0d202016-01-11 13:43:31 +00002494 Register dst = OutputRegister(instr);
2495 Register lhs = InputRegisterAt(instr, 0);
2496 Register rhs = InputRegisterAt(instr, 1);
2497
2498 switch (instr->GetOpKind()) {
2499 case HInstruction::kAnd:
2500 __ Bic(dst, lhs, rhs);
2501 break;
2502 case HInstruction::kOr:
2503 __ Orn(dst, lhs, rhs);
2504 break;
2505 case HInstruction::kXor:
2506 __ Eon(dst, lhs, rhs);
2507 break;
2508 default:
2509 LOG(FATAL) << "Unreachable";
2510 }
2511}
2512
Anton Kirilov74234da2017-01-13 14:42:47 +00002513void LocationsBuilderARM64::VisitDataProcWithShifterOp(
2514 HDataProcWithShifterOp* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002515 DCHECK(instruction->GetType() == DataType::Type::kInt32 ||
2516 instruction->GetType() == DataType::Type::kInt64);
Alexandre Rames8626b742015-11-25 16:28:08 +00002517 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002518 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Alexandre Rames8626b742015-11-25 16:28:08 +00002519 if (instruction->GetInstrKind() == HInstruction::kNeg) {
2520 locations->SetInAt(0, Location::ConstantLocation(instruction->InputAt(0)->AsConstant()));
2521 } else {
2522 locations->SetInAt(0, Location::RequiresRegister());
2523 }
2524 locations->SetInAt(1, Location::RequiresRegister());
2525 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2526}
2527
Anton Kirilov74234da2017-01-13 14:42:47 +00002528void InstructionCodeGeneratorARM64::VisitDataProcWithShifterOp(
2529 HDataProcWithShifterOp* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002530 DataType::Type type = instruction->GetType();
Alexandre Rames8626b742015-11-25 16:28:08 +00002531 HInstruction::InstructionKind kind = instruction->GetInstrKind();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002532 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Alexandre Rames8626b742015-11-25 16:28:08 +00002533 Register out = OutputRegister(instruction);
2534 Register left;
2535 if (kind != HInstruction::kNeg) {
2536 left = InputRegisterAt(instruction, 0);
2537 }
Anton Kirilov74234da2017-01-13 14:42:47 +00002538 // If this `HDataProcWithShifterOp` was created by merging a type conversion as the
Alexandre Rames8626b742015-11-25 16:28:08 +00002539 // shifter operand operation, the IR generating `right_reg` (input to the type
2540 // conversion) can have a different type from the current instruction's type,
2541 // so we manually indicate the type.
2542 Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type);
Alexandre Rames8626b742015-11-25 16:28:08 +00002543 Operand right_operand(0);
2544
Anton Kirilov74234da2017-01-13 14:42:47 +00002545 HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind();
2546 if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) {
Alexandre Rames8626b742015-11-25 16:28:08 +00002547 right_operand = Operand(right_reg, helpers::ExtendFromOpKind(op_kind));
2548 } else {
Anton Kirilov74234da2017-01-13 14:42:47 +00002549 right_operand = Operand(right_reg,
2550 helpers::ShiftFromOpKind(op_kind),
2551 instruction->GetShiftAmount());
Alexandre Rames8626b742015-11-25 16:28:08 +00002552 }
2553
2554 // Logical binary operations do not support extension operations in the
2555 // operand. Note that VIXL would still manage if it was passed by generating
2556 // the extension as a separate instruction.
2557 // `HNeg` also does not support extension. See comments in `ShifterOperandSupportsExtension()`.
2558 DCHECK(!right_operand.IsExtendedRegister() ||
2559 (kind != HInstruction::kAnd && kind != HInstruction::kOr && kind != HInstruction::kXor &&
2560 kind != HInstruction::kNeg));
2561 switch (kind) {
2562 case HInstruction::kAdd:
2563 __ Add(out, left, right_operand);
2564 break;
2565 case HInstruction::kAnd:
2566 __ And(out, left, right_operand);
2567 break;
2568 case HInstruction::kNeg:
Roland Levillain1a653882016-03-18 18:05:57 +00002569 DCHECK(instruction->InputAt(0)->AsConstant()->IsArithmeticZero());
Alexandre Rames8626b742015-11-25 16:28:08 +00002570 __ Neg(out, right_operand);
2571 break;
2572 case HInstruction::kOr:
2573 __ Orr(out, left, right_operand);
2574 break;
2575 case HInstruction::kSub:
2576 __ Sub(out, left, right_operand);
2577 break;
2578 case HInstruction::kXor:
2579 __ Eor(out, left, right_operand);
2580 break;
2581 default:
2582 LOG(FATAL) << "Unexpected operation kind: " << kind;
2583 UNREACHABLE();
2584 }
2585}
2586
Artem Serov328429f2016-07-06 16:23:04 +01002587void LocationsBuilderARM64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Alexandre Ramese6dbf482015-10-19 10:10:41 +01002588 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002589 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Alexandre Ramese6dbf482015-10-19 10:10:41 +01002590 locations->SetInAt(0, Location::RequiresRegister());
2591 locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->GetOffset(), instruction));
Artem Serov87c97052016-09-23 13:34:31 +01002592 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Alexandre Ramese6dbf482015-10-19 10:10:41 +01002593}
2594
Roland Levillain19c54192016-11-04 13:44:09 +00002595void InstructionCodeGeneratorARM64::VisitIntermediateAddress(HIntermediateAddress* instruction) {
Alexandre Ramese6dbf482015-10-19 10:10:41 +01002596 __ Add(OutputRegister(instruction),
2597 InputRegisterAt(instruction, 0),
2598 Operand(InputOperandAt(instruction, 1)));
2599}
2600
Artem Serove1811ed2017-04-27 16:50:47 +01002601void LocationsBuilderARM64::VisitIntermediateAddressIndex(HIntermediateAddressIndex* instruction) {
2602 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002603 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Artem Serove1811ed2017-04-27 16:50:47 +01002604
2605 HIntConstant* shift = instruction->GetShift()->AsIntConstant();
2606
2607 locations->SetInAt(0, Location::RequiresRegister());
2608 // For byte case we don't need to shift the index variable so we can encode the data offset into
2609 // ADD instruction. For other cases we prefer the data_offset to be in register; that will hoist
2610 // data offset constant generation out of the loop and reduce the critical path length in the
2611 // loop.
2612 locations->SetInAt(1, shift->GetValue() == 0
2613 ? Location::ConstantLocation(instruction->GetOffset()->AsIntConstant())
2614 : Location::RequiresRegister());
2615 locations->SetInAt(2, Location::ConstantLocation(shift));
2616 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2617}
2618
2619void InstructionCodeGeneratorARM64::VisitIntermediateAddressIndex(
2620 HIntermediateAddressIndex* instruction) {
2621 Register index_reg = InputRegisterAt(instruction, 0);
2622 uint32_t shift = Int64ConstantFrom(instruction->GetLocations()->InAt(2));
2623 uint32_t offset = instruction->GetOffset()->AsIntConstant()->GetValue();
2624
2625 if (shift == 0) {
2626 __ Add(OutputRegister(instruction), index_reg, offset);
2627 } else {
2628 Register offset_reg = InputRegisterAt(instruction, 1);
2629 __ Add(OutputRegister(instruction), offset_reg, Operand(index_reg, LSL, shift));
2630 }
2631}
2632
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03002633void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
Alexandre Rames418318f2015-11-20 15:55:47 +00002634 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002635 new (GetGraph()->GetAllocator()) LocationSummary(instr, LocationSummary::kNoCall);
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03002636 HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex);
2637 if (instr->GetOpKind() == HInstruction::kSub &&
2638 accumulator->IsConstant() &&
Roland Levillain1a653882016-03-18 18:05:57 +00002639 accumulator->AsConstant()->IsArithmeticZero()) {
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03002640 // Don't allocate register for Mneg instruction.
2641 } else {
2642 locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
2643 Location::RequiresRegister());
2644 }
2645 locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
2646 locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
Alexandre Rames418318f2015-11-20 15:55:47 +00002647 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
2648}
2649
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03002650void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
Alexandre Rames418318f2015-11-20 15:55:47 +00002651 Register res = OutputRegister(instr);
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03002652 Register mul_left = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
2653 Register mul_right = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
Alexandre Rames418318f2015-11-20 15:55:47 +00002654
2655 // Avoid emitting code that could trigger Cortex A53's erratum 835769.
2656 // This fixup should be carried out for all multiply-accumulate instructions:
2657 // madd, msub, smaddl, smsubl, umaddl and umsubl.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002658 if (instr->GetType() == DataType::Type::kInt64 &&
Alexandre Rames418318f2015-11-20 15:55:47 +00002659 codegen_->GetInstructionSetFeatures().NeedFixCortexA53_835769()) {
2660 MacroAssembler* masm = down_cast<CodeGeneratorARM64*>(codegen_)->GetVIXLAssembler();
Scott Wakeling97c72b72016-06-24 16:19:36 +01002661 vixl::aarch64::Instruction* prev =
2662 masm->GetCursorAddress<vixl::aarch64::Instruction*>() - kInstructionSize;
Alexandre Rames418318f2015-11-20 15:55:47 +00002663 if (prev->IsLoadOrStore()) {
2664 // Make sure we emit only exactly one nop.
Artem Serov914d7a82017-02-07 14:33:49 +00002665 ExactAssemblyScope scope(masm, kInstructionSize, CodeBufferCheckScope::kExactSize);
Alexandre Rames418318f2015-11-20 15:55:47 +00002666 __ nop();
2667 }
2668 }
2669
2670 if (instr->GetOpKind() == HInstruction::kAdd) {
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03002671 Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
Alexandre Rames418318f2015-11-20 15:55:47 +00002672 __ Madd(res, mul_left, mul_right, accumulator);
2673 } else {
2674 DCHECK(instr->GetOpKind() == HInstruction::kSub);
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03002675 HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex);
Roland Levillain1a653882016-03-18 18:05:57 +00002676 if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsArithmeticZero()) {
Artem Udovichenko4a0dad62016-01-26 12:28:31 +03002677 __ Mneg(res, mul_left, mul_right);
2678 } else {
2679 Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
2680 __ Msub(res, mul_left, mul_right, accumulator);
2681 }
Alexandre Rames418318f2015-11-20 15:55:47 +00002682 }
2683}
2684
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002685void LocationsBuilderARM64::VisitArrayGet(HArrayGet* instruction) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002686 bool object_array_get_with_read_barrier =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002687 kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002688 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01002689 new (GetGraph()->GetAllocator()) LocationSummary(instruction,
2690 object_array_get_with_read_barrier
2691 ? LocationSummary::kCallOnSlowPath
2692 : LocationSummary::kNoCall);
Vladimir Marko70e97462016-08-09 11:04:26 +01002693 if (object_array_get_with_read_barrier && kUseBakerReadBarrier) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01002694 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Roland Levillain54f869e2017-03-06 13:54:11 +00002695 // We need a temporary register for the read barrier marking slow
2696 // path in CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002697 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
2698 !Runtime::Current()->UseJitCompilation() &&
2699 instruction->GetIndex()->IsConstant()) {
2700 // Array loads with constant index are treated as field loads.
2701 // If link-time thunks for the Baker read barrier are enabled, for AOT
2702 // constant index loads we need a temporary only if the offset is too big.
2703 uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
2704 uint32_t index = instruction->GetIndex()->AsIntConstant()->GetValue();
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002705 offset += index << DataType::SizeShift(DataType::Type::kReference);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002706 if (offset >= kReferenceLoadMinFarOffset) {
2707 locations->AddTemp(FixedTempLocation());
2708 }
2709 } else {
2710 locations->AddTemp(Location::RequiresRegister());
2711 }
Vladimir Marko70e97462016-08-09 11:04:26 +01002712 }
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002713 locations->SetInAt(0, Location::RequiresRegister());
2714 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002715 if (DataType::IsFloatingPointType(instruction->GetType())) {
Alexandre Rames88c13cd2015-04-14 17:35:39 +01002716 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
2717 } else {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002718 // The output overlaps in the case of an object array get with
2719 // read barriers enabled: we do not want the move to overwrite the
2720 // array's location, as we need it to emit the read barrier.
2721 locations->SetOut(
2722 Location::RequiresRegister(),
2723 object_array_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap);
Alexandre Rames88c13cd2015-04-14 17:35:39 +01002724 }
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002725}
2726
2727void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002728 DataType::Type type = instruction->GetType();
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002729 Register obj = InputRegisterAt(instruction, 0);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002730 LocationSummary* locations = instruction->GetLocations();
2731 Location index = locations->InAt(1);
Roland Levillain44015862016-01-22 11:47:17 +00002732 Location out = locations->Out();
Vladimir Marko87f3fcb2016-04-28 15:52:11 +01002733 uint32_t offset = CodeGenerator::GetArrayDataOffset(instruction);
jessicahandojo05765752016-09-09 19:01:32 -07002734 const bool maybe_compressed_char_at = mirror::kUseStringCompression &&
2735 instruction->IsStringCharAt();
Alexandre Ramesd921d642015-04-16 15:07:16 +01002736 MacroAssembler* masm = GetVIXLAssembler();
2737 UseScratchRegisterScope temps(masm);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002738
Roland Levillain19c54192016-11-04 13:44:09 +00002739 // The read barrier instrumentation of object ArrayGet instructions
2740 // does not support the HIntermediateAddress instruction.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002741 DCHECK(!((type == DataType::Type::kReference) &&
Roland Levillain19c54192016-11-04 13:44:09 +00002742 instruction->GetArray()->IsIntermediateAddress() &&
2743 kEmitCompilerReadBarrier));
2744
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002745 if (type == DataType::Type::kReference && kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
Roland Levillain44015862016-01-22 11:47:17 +00002746 // Object ArrayGet with Baker's read barrier case.
Roland Levillain44015862016-01-22 11:47:17 +00002747 // Note that a potential implicit null check is handled in the
2748 // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call.
Vladimir Marko66d691d2017-04-07 17:53:39 +01002749 DCHECK(!instruction->CanDoImplicitNullCheckOn(instruction->InputAt(0)));
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002750 if (index.IsConstant()) {
2751 // Array load with a constant index can be treated as a field load.
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002752 offset += Int64ConstantFrom(index) << DataType::SizeShift(type);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002753 Location maybe_temp =
2754 (locations->GetTempCount() != 0) ? locations->GetTemp(0) : Location::NoLocation();
2755 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
2756 out,
2757 obj.W(),
2758 offset,
2759 maybe_temp,
Vladimir Marko66d691d2017-04-07 17:53:39 +01002760 /* needs_null_check */ false,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002761 /* use_load_acquire */ false);
2762 } else {
2763 Register temp = WRegisterFrom(locations->GetTemp(0));
2764 codegen_->GenerateArrayLoadWithBakerReadBarrier(
Vladimir Marko66d691d2017-04-07 17:53:39 +01002765 instruction, out, obj.W(), offset, index, temp, /* needs_null_check */ false);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00002766 }
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002767 } else {
Roland Levillain44015862016-01-22 11:47:17 +00002768 // General case.
2769 MemOperand source = HeapOperand(obj);
jessicahandojo05765752016-09-09 19:01:32 -07002770 Register length;
2771 if (maybe_compressed_char_at) {
2772 uint32_t count_offset = mirror::String::CountOffset().Uint32Value();
2773 length = temps.AcquireW();
Artem Serov914d7a82017-02-07 14:33:49 +00002774 {
2775 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
2776 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
2777
2778 if (instruction->GetArray()->IsIntermediateAddress()) {
2779 DCHECK_LT(count_offset, offset);
2780 int64_t adjusted_offset =
2781 static_cast<int64_t>(count_offset) - static_cast<int64_t>(offset);
2782 // Note that `adjusted_offset` is negative, so this will be a LDUR.
2783 __ Ldr(length, MemOperand(obj.X(), adjusted_offset));
2784 } else {
2785 __ Ldr(length, HeapOperand(obj, count_offset));
2786 }
2787 codegen_->MaybeRecordImplicitNullCheck(instruction);
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002788 }
jessicahandojo05765752016-09-09 19:01:32 -07002789 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002790 if (index.IsConstant()) {
jessicahandojo05765752016-09-09 19:01:32 -07002791 if (maybe_compressed_char_at) {
2792 vixl::aarch64::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002793 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2794 "Expecting 0=compressed, 1=uncompressed");
2795 __ Tbnz(length.W(), 0, &uncompressed_load);
jessicahandojo05765752016-09-09 19:01:32 -07002796 __ Ldrb(Register(OutputCPURegister(instruction)),
2797 HeapOperand(obj, offset + Int64ConstantFrom(index)));
2798 __ B(&done);
2799 __ Bind(&uncompressed_load);
2800 __ Ldrh(Register(OutputCPURegister(instruction)),
2801 HeapOperand(obj, offset + (Int64ConstantFrom(index) << 1)));
2802 __ Bind(&done);
2803 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002804 offset += Int64ConstantFrom(index) << DataType::SizeShift(type);
jessicahandojo05765752016-09-09 19:01:32 -07002805 source = HeapOperand(obj, offset);
2806 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002807 } else {
Roland Levillain44015862016-01-22 11:47:17 +00002808 Register temp = temps.AcquireSameSizeAs(obj);
Artem Serov328429f2016-07-06 16:23:04 +01002809 if (instruction->GetArray()->IsIntermediateAddress()) {
Roland Levillain44015862016-01-22 11:47:17 +00002810 // We do not need to compute the intermediate address from the array: the
2811 // input instruction has done it already. See the comment in
Artem Serov328429f2016-07-06 16:23:04 +01002812 // `TryExtractArrayAccessAddress()`.
Roland Levillain44015862016-01-22 11:47:17 +00002813 if (kIsDebugBuild) {
Artem Serov328429f2016-07-06 16:23:04 +01002814 HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress();
Roland Levillain44015862016-01-22 11:47:17 +00002815 DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset);
2816 }
2817 temp = obj;
2818 } else {
2819 __ Add(temp, obj, offset);
2820 }
jessicahandojo05765752016-09-09 19:01:32 -07002821 if (maybe_compressed_char_at) {
2822 vixl::aarch64::Label uncompressed_load, done;
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002823 static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u,
2824 "Expecting 0=compressed, 1=uncompressed");
2825 __ Tbnz(length.W(), 0, &uncompressed_load);
jessicahandojo05765752016-09-09 19:01:32 -07002826 __ Ldrb(Register(OutputCPURegister(instruction)),
2827 HeapOperand(temp, XRegisterFrom(index), LSL, 0));
2828 __ B(&done);
2829 __ Bind(&uncompressed_load);
2830 __ Ldrh(Register(OutputCPURegister(instruction)),
2831 HeapOperand(temp, XRegisterFrom(index), LSL, 1));
2832 __ Bind(&done);
2833 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002834 source = HeapOperand(temp, XRegisterFrom(index), LSL, DataType::SizeShift(type));
jessicahandojo05765752016-09-09 19:01:32 -07002835 }
Roland Levillain44015862016-01-22 11:47:17 +00002836 }
jessicahandojo05765752016-09-09 19:01:32 -07002837 if (!maybe_compressed_char_at) {
Artem Serov914d7a82017-02-07 14:33:49 +00002838 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
2839 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
jessicahandojo05765752016-09-09 19:01:32 -07002840 codegen_->Load(type, OutputCPURegister(instruction), source);
2841 codegen_->MaybeRecordImplicitNullCheck(instruction);
2842 }
Roland Levillain44015862016-01-22 11:47:17 +00002843
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002844 if (type == DataType::Type::kReference) {
Roland Levillain44015862016-01-22 11:47:17 +00002845 static_assert(
2846 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
2847 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
2848 Location obj_loc = locations->InAt(0);
2849 if (index.IsConstant()) {
2850 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, obj_loc, offset);
2851 } else {
2852 codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, obj_loc, offset, index);
2853 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002854 }
Roland Levillain4d027112015-07-01 15:41:14 +01002855 }
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002856}
2857
Alexandre Rames5319def2014-10-23 10:03:10 +01002858void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01002859 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Alexandre Rames5319def2014-10-23 10:03:10 +01002860 locations->SetInAt(0, Location::RequiresRegister());
Alexandre Ramesfb4e5fa2014-11-06 12:41:16 +00002861 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Alexandre Rames5319def2014-10-23 10:03:10 +01002862}
2863
2864void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) {
Vladimir Markodce016e2016-04-28 13:10:02 +01002865 uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction);
jessicahandojo05765752016-09-09 19:01:32 -07002866 vixl::aarch64::Register out = OutputRegister(instruction);
Artem Serov914d7a82017-02-07 14:33:49 +00002867 {
2868 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
2869 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
2870 __ Ldr(out, HeapOperand(InputRegisterAt(instruction, 0), offset));
2871 codegen_->MaybeRecordImplicitNullCheck(instruction);
2872 }
jessicahandojo05765752016-09-09 19:01:32 -07002873 // Mask out compression flag from String's array length.
2874 if (mirror::kUseStringCompression && instruction->IsStringLength()) {
Vladimir Markofdaf0f42016-10-13 19:29:53 +01002875 __ Lsr(out.W(), out.W(), 1u);
jessicahandojo05765752016-09-09 19:01:32 -07002876 }
Alexandre Rames5319def2014-10-23 10:03:10 +01002877}
2878
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002879void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002880 DataType::Type value_type = instruction->GetComponentType();
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002881
2882 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
Vladimir Markoca6fff82017-10-03 14:49:14 +01002883 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002884 instruction,
Vladimir Marko8d49fd72016-08-25 15:20:47 +01002885 may_need_runtime_call_for_type_check ?
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002886 LocationSummary::kCallOnSlowPath :
2887 LocationSummary::kNoCall);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002888 locations->SetInAt(0, Location::RequiresRegister());
2889 locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1)));
Alexandre Ramesbe919d92016-08-23 18:33:36 +01002890 if (IsConstantZeroBitPattern(instruction->InputAt(2))) {
2891 locations->SetInAt(2, Location::ConstantLocation(instruction->InputAt(2)->AsConstant()));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002892 } else if (DataType::IsFloatingPointType(value_type)) {
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002893 locations->SetInAt(2, Location::RequiresFpuRegister());
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002894 } else {
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002895 locations->SetInAt(2, Location::RequiresRegister());
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002896 }
2897}
2898
2899void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002900 DataType::Type value_type = instruction->GetComponentType();
Alexandre Rames97833a02015-04-16 15:07:12 +01002901 LocationSummary* locations = instruction->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002902 bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002903 bool needs_write_barrier =
2904 CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());
Alexandre Rames97833a02015-04-16 15:07:12 +01002905
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002906 Register array = InputRegisterAt(instruction, 0);
Alexandre Ramesbe919d92016-08-23 18:33:36 +01002907 CPURegister value = InputCPURegisterOrZeroRegAt(instruction, 2);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002908 CPURegister source = value;
2909 Location index = locations->InAt(1);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002910 size_t offset = mirror::Array::DataOffset(DataType::Size(value_type)).Uint32Value();
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002911 MemOperand destination = HeapOperand(array);
2912 MacroAssembler* masm = GetVIXLAssembler();
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002913
2914 if (!needs_write_barrier) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002915 DCHECK(!may_need_runtime_call_for_type_check);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002916 if (index.IsConstant()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002917 offset += Int64ConstantFrom(index) << DataType::SizeShift(value_type);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002918 destination = HeapOperand(array, offset);
2919 } else {
2920 UseScratchRegisterScope temps(masm);
2921 Register temp = temps.AcquireSameSizeAs(array);
Artem Serov328429f2016-07-06 16:23:04 +01002922 if (instruction->GetArray()->IsIntermediateAddress()) {
Alexandre Ramese6dbf482015-10-19 10:10:41 +01002923 // We do not need to compute the intermediate address from the array: the
2924 // input instruction has done it already. See the comment in
Artem Serov328429f2016-07-06 16:23:04 +01002925 // `TryExtractArrayAccessAddress()`.
Alexandre Ramese6dbf482015-10-19 10:10:41 +01002926 if (kIsDebugBuild) {
Artem Serov328429f2016-07-06 16:23:04 +01002927 HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress();
Alexandre Ramese6dbf482015-10-19 10:10:41 +01002928 DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset);
2929 }
2930 temp = array;
2931 } else {
2932 __ Add(temp, array, offset);
2933 }
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002934 destination = HeapOperand(temp,
2935 XRegisterFrom(index),
2936 LSL,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002937 DataType::SizeShift(value_type));
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002938 }
Artem Serov914d7a82017-02-07 14:33:49 +00002939 {
2940 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
2941 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
2942 codegen_->Store(value_type, value, destination);
2943 codegen_->MaybeRecordImplicitNullCheck(instruction);
2944 }
Alexandre Ramesfc19de82014-11-07 17:13:31 +00002945 } else {
Artem Serov328429f2016-07-06 16:23:04 +01002946 DCHECK(!instruction->GetArray()->IsIntermediateAddress());
Scott Wakeling97c72b72016-06-24 16:19:36 +01002947 vixl::aarch64::Label done;
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002948 SlowPathCodeARM64* slow_path = nullptr;
Alexandre Rames97833a02015-04-16 15:07:12 +01002949 {
2950 // We use a block to end the scratch scope before the write barrier, thus
2951 // freeing the temporary registers so they can be used in `MarkGCCard`.
2952 UseScratchRegisterScope temps(masm);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002953 Register temp = temps.AcquireSameSizeAs(array);
Alexandre Rames97833a02015-04-16 15:07:12 +01002954 if (index.IsConstant()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002955 offset += Int64ConstantFrom(index) << DataType::SizeShift(value_type);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002956 destination = HeapOperand(array, offset);
Alexandre Rames97833a02015-04-16 15:07:12 +01002957 } else {
Alexandre Rames82000b02015-07-07 11:34:16 +01002958 destination = HeapOperand(temp,
2959 XRegisterFrom(index),
2960 LSL,
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01002961 DataType::SizeShift(value_type));
Alexandre Rames97833a02015-04-16 15:07:12 +01002962 }
2963
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002964 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
2965 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
2966 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
2967
Roland Levillain22ccc3a2015-11-24 13:10:05 +00002968 if (may_need_runtime_call_for_type_check) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01002969 slow_path = new (codegen_->GetScopedAllocator()) ArraySetSlowPathARM64(instruction);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002970 codegen_->AddSlowPath(slow_path);
2971 if (instruction->GetValueCanBeNull()) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01002972 vixl::aarch64::Label non_zero;
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002973 __ Cbnz(Register(value), &non_zero);
2974 if (!index.IsConstant()) {
2975 __ Add(temp, array, offset);
2976 }
Artem Serov914d7a82017-02-07 14:33:49 +00002977 {
2978 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools
2979 // emitted.
2980 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
2981 __ Str(wzr, destination);
2982 codegen_->MaybeRecordImplicitNullCheck(instruction);
2983 }
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01002984 __ B(&done);
2985 __ Bind(&non_zero);
2986 }
2987
Roland Levillain9d6e1f82016-09-05 15:57:33 +01002988 // Note that when Baker read barriers are enabled, the type
2989 // checks are performed without read barriers. This is fine,
2990 // even in the case where a class object is in the from-space
2991 // after the flip, as a comparison involving such a type would
2992 // not produce a false positive; it may of course produce a
2993 // false negative, in which case we would take the ArraySet
2994 // slow path.
Roland Levillain16d9f942016-08-25 17:27:56 +01002995
Roland Levillain9d6e1f82016-09-05 15:57:33 +01002996 Register temp2 = temps.AcquireSameSizeAs(array);
2997 // /* HeapReference<Class> */ temp = array->klass_
Artem Serov914d7a82017-02-07 14:33:49 +00002998 {
2999 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
3000 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
3001 __ Ldr(temp, HeapOperand(array, class_offset));
3002 codegen_->MaybeRecordImplicitNullCheck(instruction);
3003 }
Roland Levillain9d6e1f82016-09-05 15:57:33 +01003004 GetAssembler()->MaybeUnpoisonHeapReference(temp);
Roland Levillain16d9f942016-08-25 17:27:56 +01003005
Roland Levillain9d6e1f82016-09-05 15:57:33 +01003006 // /* HeapReference<Class> */ temp = temp->component_type_
3007 __ Ldr(temp, HeapOperand(temp, component_offset));
3008 // /* HeapReference<Class> */ temp2 = value->klass_
3009 __ Ldr(temp2, HeapOperand(Register(value), class_offset));
3010 // If heap poisoning is enabled, no need to unpoison `temp`
3011 // nor `temp2`, as we are comparing two poisoned references.
3012 __ Cmp(temp, temp2);
3013 temps.Release(temp2);
Roland Levillain16d9f942016-08-25 17:27:56 +01003014
Roland Levillain9d6e1f82016-09-05 15:57:33 +01003015 if (instruction->StaticTypeOfArrayIsObjectArray()) {
3016 vixl::aarch64::Label do_put;
3017 __ B(eq, &do_put);
3018 // If heap poisoning is enabled, the `temp` reference has
3019 // not been unpoisoned yet; unpoison it now.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003020 GetAssembler()->MaybeUnpoisonHeapReference(temp);
3021
Roland Levillain9d6e1f82016-09-05 15:57:33 +01003022 // /* HeapReference<Class> */ temp = temp->super_class_
3023 __ Ldr(temp, HeapOperand(temp, super_offset));
3024 // If heap poisoning is enabled, no need to unpoison
3025 // `temp`, as we are comparing against null below.
3026 __ Cbnz(temp, slow_path->GetEntryLabel());
3027 __ Bind(&do_put);
3028 } else {
3029 __ B(ne, slow_path->GetEntryLabel());
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01003030 }
3031 }
3032
3033 if (kPoisonHeapReferences) {
Nicolas Geoffraya8a0fe22015-10-01 15:50:27 +01003034 Register temp2 = temps.AcquireSameSizeAs(array);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01003035 DCHECK(value.IsW());
Nicolas Geoffraya8a0fe22015-10-01 15:50:27 +01003036 __ Mov(temp2, value.W());
3037 GetAssembler()->PoisonHeapReference(temp2);
3038 source = temp2;
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01003039 }
3040
3041 if (!index.IsConstant()) {
3042 __ Add(temp, array, offset);
Vladimir Markod1ef8732017-04-18 13:55:13 +01003043 } else {
3044 // We no longer need the `temp` here so release it as the store below may
3045 // need a scratch register (if the constant index makes the offset too large)
3046 // and the poisoned `source` could be using the other scratch register.
3047 temps.Release(temp);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01003048 }
Artem Serov914d7a82017-02-07 14:33:49 +00003049 {
3050 // Ensure that between store and MaybeRecordImplicitNullCheck there are no pools emitted.
3051 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
3052 __ Str(source, destination);
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01003053
Artem Serov914d7a82017-02-07 14:33:49 +00003054 if (!may_need_runtime_call_for_type_check) {
3055 codegen_->MaybeRecordImplicitNullCheck(instruction);
3056 }
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01003057 }
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003058 }
Nicolas Geoffraye0395dd2015-09-25 11:04:45 +01003059
3060 codegen_->MarkGCCard(array, value.W(), instruction->GetValueCanBeNull());
3061
3062 if (done.IsLinked()) {
3063 __ Bind(&done);
3064 }
3065
3066 if (slow_path != nullptr) {
3067 __ Bind(slow_path->GetExitLabel());
Alexandre Rames97833a02015-04-16 15:07:12 +01003068 }
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003069 }
3070}
3071
Alexandre Rames67555f72014-11-18 10:55:16 +00003072void LocationsBuilderARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01003073 RegisterSet caller_saves = RegisterSet::Empty();
3074 InvokeRuntimeCallingConvention calling_convention;
3075 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
3076 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(1).GetCode()));
3077 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction, caller_saves);
Alexandre Rames67555f72014-11-18 10:55:16 +00003078 locations->SetInAt(0, Location::RequiresRegister());
Serban Constantinescu760d8ef2015-03-28 18:09:56 +00003079 locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
Alexandre Rames67555f72014-11-18 10:55:16 +00003080}
3081
3082void InstructionCodeGeneratorARM64::VisitBoundsCheck(HBoundsCheck* instruction) {
Serban Constantinescu5a6cc492015-08-13 15:20:25 +01003083 BoundsCheckSlowPathARM64* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01003084 new (codegen_->GetScopedAllocator()) BoundsCheckSlowPathARM64(instruction);
Alexandre Rames67555f72014-11-18 10:55:16 +00003085 codegen_->AddSlowPath(slow_path);
Alexandre Rames67555f72014-11-18 10:55:16 +00003086 __ Cmp(InputRegisterAt(instruction, 0), InputOperandAt(instruction, 1));
3087 __ B(slow_path->GetEntryLabel(), hs);
3088}
3089
Alexandre Rames67555f72014-11-18 10:55:16 +00003090void LocationsBuilderARM64::VisitClinitCheck(HClinitCheck* check) {
3091 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003092 new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
Alexandre Rames67555f72014-11-18 10:55:16 +00003093 locations->SetInAt(0, Location::RequiresRegister());
3094 if (check->HasUses()) {
3095 locations->SetOut(Location::SameAsFirstInput());
3096 }
3097}
3098
3099void InstructionCodeGeneratorARM64::VisitClinitCheck(HClinitCheck* check) {
3100 // We assume the class is not null.
Vladimir Marko174b2e22017-10-12 13:34:49 +01003101 SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathARM64(
Alexandre Rames67555f72014-11-18 10:55:16 +00003102 check->GetLoadClass(), check, check->GetDexPc(), true);
3103 codegen_->AddSlowPath(slow_path);
3104 GenerateClassInitializationCheck(slow_path, InputRegisterAt(check, 0));
3105}
3106
Roland Levillain1a653882016-03-18 18:05:57 +00003107static bool IsFloatingPointZeroConstant(HInstruction* inst) {
3108 return (inst->IsFloatConstant() && (inst->AsFloatConstant()->IsArithmeticZero()))
3109 || (inst->IsDoubleConstant() && (inst->AsDoubleConstant()->IsArithmeticZero()));
3110}
3111
3112void InstructionCodeGeneratorARM64::GenerateFcmp(HInstruction* instruction) {
3113 FPRegister lhs_reg = InputFPRegisterAt(instruction, 0);
3114 Location rhs_loc = instruction->GetLocations()->InAt(1);
3115 if (rhs_loc.IsConstant()) {
3116 // 0.0 is the only immediate that can be encoded directly in
3117 // an FCMP instruction.
3118 //
3119 // Both the JLS (section 15.20.1) and the JVMS (section 6.5)
3120 // specify that in a floating-point comparison, positive zero
3121 // and negative zero are considered equal, so we can use the
3122 // literal 0.0 for both cases here.
3123 //
3124 // Note however that some methods (Float.equal, Float.compare,
3125 // Float.compareTo, Double.equal, Double.compare,
3126 // Double.compareTo, Math.max, Math.min, StrictMath.max,
3127 // StrictMath.min) consider 0.0 to be (strictly) greater than
3128 // -0.0. So if we ever translate calls to these methods into a
3129 // HCompare instruction, we must handle the -0.0 case with
3130 // care here.
3131 DCHECK(IsFloatingPointZeroConstant(rhs_loc.GetConstant()));
3132 __ Fcmp(lhs_reg, 0.0);
3133 } else {
3134 __ Fcmp(lhs_reg, InputFPRegisterAt(instruction, 1));
3135 }
Roland Levillain7f63c522015-07-13 15:54:55 +00003136}
3137
Serban Constantinescu02164b32014-11-13 14:05:07 +00003138void LocationsBuilderARM64::VisitCompare(HCompare* compare) {
Alexandre Rames5319def2014-10-23 10:03:10 +01003139 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003140 new (GetGraph()->GetAllocator()) LocationSummary(compare, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003141 DataType::Type in_type = compare->InputAt(0)->GetType();
Alexandre Rames5319def2014-10-23 10:03:10 +01003142 switch (in_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003143 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003144 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003145 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003146 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003147 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003148 case DataType::Type::kInt32:
3149 case DataType::Type::kInt64: {
Serban Constantinescu02164b32014-11-13 14:05:07 +00003150 locations->SetInAt(0, Location::RequiresRegister());
Serban Constantinescu2d35d9d2015-02-22 22:08:01 +00003151 locations->SetInAt(1, ARM64EncodableConstantOrRegister(compare->InputAt(1), compare));
Serban Constantinescu02164b32014-11-13 14:05:07 +00003152 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3153 break;
3154 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003155 case DataType::Type::kFloat32:
3156 case DataType::Type::kFloat64: {
Serban Constantinescu02164b32014-11-13 14:05:07 +00003157 locations->SetInAt(0, Location::RequiresFpuRegister());
Roland Levillain7f63c522015-07-13 15:54:55 +00003158 locations->SetInAt(1,
3159 IsFloatingPointZeroConstant(compare->InputAt(1))
3160 ? Location::ConstantLocation(compare->InputAt(1)->AsConstant())
3161 : Location::RequiresFpuRegister());
Serban Constantinescu02164b32014-11-13 14:05:07 +00003162 locations->SetOut(Location::RequiresRegister());
3163 break;
3164 }
3165 default:
3166 LOG(FATAL) << "Unexpected type for compare operation " << in_type;
3167 }
3168}
3169
3170void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003171 DataType::Type in_type = compare->InputAt(0)->GetType();
Serban Constantinescu02164b32014-11-13 14:05:07 +00003172
3173 // 0 if: left == right
3174 // 1 if: left > right
3175 // -1 if: left < right
3176 switch (in_type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003177 case DataType::Type::kBool:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003178 case DataType::Type::kUint8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003179 case DataType::Type::kInt8:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003180 case DataType::Type::kUint16:
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01003181 case DataType::Type::kInt16:
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003182 case DataType::Type::kInt32:
3183 case DataType::Type::kInt64: {
Serban Constantinescu02164b32014-11-13 14:05:07 +00003184 Register result = OutputRegister(compare);
3185 Register left = InputRegisterAt(compare, 0);
3186 Operand right = InputOperandAt(compare, 1);
Serban Constantinescu02164b32014-11-13 14:05:07 +00003187 __ Cmp(left, right);
Aart Bika19616e2016-02-01 18:57:58 -08003188 __ Cset(result, ne); // result == +1 if NE or 0 otherwise
3189 __ Cneg(result, result, lt); // result == -1 if LT or unchanged otherwise
Serban Constantinescu02164b32014-11-13 14:05:07 +00003190 break;
3191 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003192 case DataType::Type::kFloat32:
3193 case DataType::Type::kFloat64: {
Serban Constantinescu02164b32014-11-13 14:05:07 +00003194 Register result = OutputRegister(compare);
Roland Levillain1a653882016-03-18 18:05:57 +00003195 GenerateFcmp(compare);
Vladimir Markod6e069b2016-01-18 11:11:01 +00003196 __ Cset(result, ne);
3197 __ Cneg(result, result, ARM64FPCondition(kCondLT, compare->IsGtBias()));
Alexandre Rames5319def2014-10-23 10:03:10 +01003198 break;
3199 }
3200 default:
3201 LOG(FATAL) << "Unimplemented compare type " << in_type;
3202 }
3203}
3204
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00003205void LocationsBuilderARM64::HandleCondition(HCondition* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003206 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Roland Levillain7f63c522015-07-13 15:54:55 +00003207
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003208 if (DataType::IsFloatingPointType(instruction->InputAt(0)->GetType())) {
Roland Levillain7f63c522015-07-13 15:54:55 +00003209 locations->SetInAt(0, Location::RequiresFpuRegister());
3210 locations->SetInAt(1,
3211 IsFloatingPointZeroConstant(instruction->InputAt(1))
3212 ? Location::ConstantLocation(instruction->InputAt(1)->AsConstant())
3213 : Location::RequiresFpuRegister());
3214 } else {
3215 // Integer cases.
3216 locations->SetInAt(0, Location::RequiresRegister());
3217 locations->SetInAt(1, ARM64EncodableConstantOrRegister(instruction->InputAt(1), instruction));
3218 }
3219
David Brazdilb3e773e2016-01-26 11:28:37 +00003220 if (!instruction->IsEmittedAtUseSite()) {
Alexandre Ramesfb4e5fa2014-11-06 12:41:16 +00003221 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Alexandre Rames5319def2014-10-23 10:03:10 +01003222 }
3223}
3224
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00003225void InstructionCodeGeneratorARM64::HandleCondition(HCondition* instruction) {
David Brazdilb3e773e2016-01-26 11:28:37 +00003226 if (instruction->IsEmittedAtUseSite()) {
Alexandre Rames5319def2014-10-23 10:03:10 +01003227 return;
3228 }
3229
3230 LocationSummary* locations = instruction->GetLocations();
Alexandre Rames5319def2014-10-23 10:03:10 +01003231 Register res = RegisterFrom(locations->Out(), instruction->GetType());
Roland Levillain7f63c522015-07-13 15:54:55 +00003232 IfCondition if_cond = instruction->GetCondition();
Alexandre Rames5319def2014-10-23 10:03:10 +01003233
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003234 if (DataType::IsFloatingPointType(instruction->InputAt(0)->GetType())) {
Roland Levillain1a653882016-03-18 18:05:57 +00003235 GenerateFcmp(instruction);
Vladimir Markod6e069b2016-01-18 11:11:01 +00003236 __ Cset(res, ARM64FPCondition(if_cond, instruction->IsGtBias()));
Roland Levillain7f63c522015-07-13 15:54:55 +00003237 } else {
3238 // Integer cases.
3239 Register lhs = InputRegisterAt(instruction, 0);
3240 Operand rhs = InputOperandAt(instruction, 1);
3241 __ Cmp(lhs, rhs);
Vladimir Markod6e069b2016-01-18 11:11:01 +00003242 __ Cset(res, ARM64Condition(if_cond));
Roland Levillain7f63c522015-07-13 15:54:55 +00003243 }
Alexandre Rames5319def2014-10-23 10:03:10 +01003244}
3245
3246#define FOR_EACH_CONDITION_INSTRUCTION(M) \
3247 M(Equal) \
3248 M(NotEqual) \
3249 M(LessThan) \
3250 M(LessThanOrEqual) \
3251 M(GreaterThan) \
Aart Bike9f37602015-10-09 11:15:55 -07003252 M(GreaterThanOrEqual) \
3253 M(Below) \
3254 M(BelowOrEqual) \
3255 M(Above) \
3256 M(AboveOrEqual)
Alexandre Rames5319def2014-10-23 10:03:10 +01003257#define DEFINE_CONDITION_VISITORS(Name) \
Vladimir Marko5f7b58e2015-11-23 19:49:34 +00003258void LocationsBuilderARM64::Visit##Name(H##Name* comp) { HandleCondition(comp); } \
3259void InstructionCodeGeneratorARM64::Visit##Name(H##Name* comp) { HandleCondition(comp); }
Alexandre Rames5319def2014-10-23 10:03:10 +01003260FOR_EACH_CONDITION_INSTRUCTION(DEFINE_CONDITION_VISITORS)
Alexandre Rames67555f72014-11-18 10:55:16 +00003261#undef DEFINE_CONDITION_VISITORS
Alexandre Rames5319def2014-10-23 10:03:10 +01003262#undef FOR_EACH_CONDITION_INSTRUCTION
3263
Zheng Xuc6667102015-05-15 16:08:45 +08003264void InstructionCodeGeneratorARM64::DivRemOneOrMinusOne(HBinaryOperation* instruction) {
3265 DCHECK(instruction->IsDiv() || instruction->IsRem());
3266
3267 LocationSummary* locations = instruction->GetLocations();
3268 Location second = locations->InAt(1);
3269 DCHECK(second.IsConstant());
3270
3271 Register out = OutputRegister(instruction);
3272 Register dividend = InputRegisterAt(instruction, 0);
3273 int64_t imm = Int64FromConstant(second.GetConstant());
3274 DCHECK(imm == 1 || imm == -1);
3275
3276 if (instruction->IsRem()) {
3277 __ Mov(out, 0);
3278 } else {
3279 if (imm == 1) {
3280 __ Mov(out, dividend);
3281 } else {
3282 __ Neg(out, dividend);
3283 }
3284 }
3285}
3286
3287void InstructionCodeGeneratorARM64::DivRemByPowerOfTwo(HBinaryOperation* instruction) {
3288 DCHECK(instruction->IsDiv() || instruction->IsRem());
3289
3290 LocationSummary* locations = instruction->GetLocations();
3291 Location second = locations->InAt(1);
3292 DCHECK(second.IsConstant());
3293
3294 Register out = OutputRegister(instruction);
3295 Register dividend = InputRegisterAt(instruction, 0);
3296 int64_t imm = Int64FromConstant(second.GetConstant());
Nicolas Geoffray68f62892016-01-04 08:39:49 +00003297 uint64_t abs_imm = static_cast<uint64_t>(AbsOrMin(imm));
Zheng Xuc6667102015-05-15 16:08:45 +08003298 int ctz_imm = CTZ(abs_imm);
3299
3300 UseScratchRegisterScope temps(GetVIXLAssembler());
3301 Register temp = temps.AcquireSameSizeAs(out);
3302
3303 if (instruction->IsDiv()) {
3304 __ Add(temp, dividend, abs_imm - 1);
3305 __ Cmp(dividend, 0);
3306 __ Csel(out, temp, dividend, lt);
3307 if (imm > 0) {
3308 __ Asr(out, out, ctz_imm);
3309 } else {
3310 __ Neg(out, Operand(out, ASR, ctz_imm));
3311 }
3312 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003313 int bits = instruction->GetResultType() == DataType::Type::kInt32 ? 32 : 64;
Zheng Xuc6667102015-05-15 16:08:45 +08003314 __ Asr(temp, dividend, bits - 1);
3315 __ Lsr(temp, temp, bits - ctz_imm);
3316 __ Add(out, dividend, temp);
3317 __ And(out, out, abs_imm - 1);
3318 __ Sub(out, out, temp);
3319 }
3320}
3321
3322void InstructionCodeGeneratorARM64::GenerateDivRemWithAnyConstant(HBinaryOperation* instruction) {
3323 DCHECK(instruction->IsDiv() || instruction->IsRem());
3324
3325 LocationSummary* locations = instruction->GetLocations();
3326 Location second = locations->InAt(1);
3327 DCHECK(second.IsConstant());
3328
3329 Register out = OutputRegister(instruction);
3330 Register dividend = InputRegisterAt(instruction, 0);
3331 int64_t imm = Int64FromConstant(second.GetConstant());
3332
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003333 DataType::Type type = instruction->GetResultType();
3334 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Zheng Xuc6667102015-05-15 16:08:45 +08003335
3336 int64_t magic;
3337 int shift;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003338 CalculateMagicAndShiftForDivRem(
3339 imm, type == DataType::Type::kInt64 /* is_long */, &magic, &shift);
Zheng Xuc6667102015-05-15 16:08:45 +08003340
3341 UseScratchRegisterScope temps(GetVIXLAssembler());
3342 Register temp = temps.AcquireSameSizeAs(out);
3343
3344 // temp = get_high(dividend * magic)
3345 __ Mov(temp, magic);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003346 if (type == DataType::Type::kInt64) {
Zheng Xuc6667102015-05-15 16:08:45 +08003347 __ Smulh(temp, dividend, temp);
3348 } else {
3349 __ Smull(temp.X(), dividend, temp);
3350 __ Lsr(temp.X(), temp.X(), 32);
3351 }
3352
3353 if (imm > 0 && magic < 0) {
3354 __ Add(temp, temp, dividend);
3355 } else if (imm < 0 && magic > 0) {
3356 __ Sub(temp, temp, dividend);
3357 }
3358
3359 if (shift != 0) {
3360 __ Asr(temp, temp, shift);
3361 }
3362
3363 if (instruction->IsDiv()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003364 __ Sub(out, temp, Operand(temp, ASR, type == DataType::Type::kInt64 ? 63 : 31));
Zheng Xuc6667102015-05-15 16:08:45 +08003365 } else {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003366 __ Sub(temp, temp, Operand(temp, ASR, type == DataType::Type::kInt64 ? 63 : 31));
Zheng Xuc6667102015-05-15 16:08:45 +08003367 // TODO: Strength reduction for msub.
3368 Register temp_imm = temps.AcquireSameSizeAs(out);
3369 __ Mov(temp_imm, imm);
3370 __ Msub(out, temp, temp_imm, dividend);
3371 }
3372}
3373
3374void InstructionCodeGeneratorARM64::GenerateDivRemIntegral(HBinaryOperation* instruction) {
3375 DCHECK(instruction->IsDiv() || instruction->IsRem());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003376 DataType::Type type = instruction->GetResultType();
3377 DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64);
Zheng Xuc6667102015-05-15 16:08:45 +08003378
3379 LocationSummary* locations = instruction->GetLocations();
3380 Register out = OutputRegister(instruction);
3381 Location second = locations->InAt(1);
3382
3383 if (second.IsConstant()) {
3384 int64_t imm = Int64FromConstant(second.GetConstant());
3385
3386 if (imm == 0) {
3387 // Do not generate anything. DivZeroCheck would prevent any code to be executed.
3388 } else if (imm == 1 || imm == -1) {
3389 DivRemOneOrMinusOne(instruction);
Nicolas Geoffray68f62892016-01-04 08:39:49 +00003390 } else if (IsPowerOfTwo(AbsOrMin(imm))) {
Zheng Xuc6667102015-05-15 16:08:45 +08003391 DivRemByPowerOfTwo(instruction);
3392 } else {
3393 DCHECK(imm <= -2 || imm >= 2);
3394 GenerateDivRemWithAnyConstant(instruction);
3395 }
3396 } else {
3397 Register dividend = InputRegisterAt(instruction, 0);
3398 Register divisor = InputRegisterAt(instruction, 1);
3399 if (instruction->IsDiv()) {
3400 __ Sdiv(out, dividend, divisor);
3401 } else {
3402 UseScratchRegisterScope temps(GetVIXLAssembler());
3403 Register temp = temps.AcquireSameSizeAs(out);
3404 __ Sdiv(temp, dividend, divisor);
3405 __ Msub(out, temp, divisor, dividend);
3406 }
3407 }
3408}
3409
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003410void LocationsBuilderARM64::VisitDiv(HDiv* div) {
3411 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003412 new (GetGraph()->GetAllocator()) LocationSummary(div, LocationSummary::kNoCall);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003413 switch (div->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003414 case DataType::Type::kInt32:
3415 case DataType::Type::kInt64:
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003416 locations->SetInAt(0, Location::RequiresRegister());
Zheng Xuc6667102015-05-15 16:08:45 +08003417 locations->SetInAt(1, Location::RegisterOrConstant(div->InputAt(1)));
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003418 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
3419 break;
3420
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003421 case DataType::Type::kFloat32:
3422 case DataType::Type::kFloat64:
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003423 locations->SetInAt(0, Location::RequiresFpuRegister());
3424 locations->SetInAt(1, Location::RequiresFpuRegister());
3425 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
3426 break;
3427
3428 default:
3429 LOG(FATAL) << "Unexpected div type " << div->GetResultType();
3430 }
3431}
3432
3433void InstructionCodeGeneratorARM64::VisitDiv(HDiv* div) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003434 DataType::Type type = div->GetResultType();
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003435 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003436 case DataType::Type::kInt32:
3437 case DataType::Type::kInt64:
Zheng Xuc6667102015-05-15 16:08:45 +08003438 GenerateDivRemIntegral(div);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003439 break;
3440
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003441 case DataType::Type::kFloat32:
3442 case DataType::Type::kFloat64:
Alexandre Ramesfc19de82014-11-07 17:13:31 +00003443 __ Fdiv(OutputFPRegister(div), InputFPRegisterAt(div, 0), InputFPRegisterAt(div, 1));
3444 break;
3445
3446 default:
3447 LOG(FATAL) << "Unexpected div type " << type;
3448 }
3449}
3450
Alexandre Rames67555f72014-11-18 10:55:16 +00003451void LocationsBuilderARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01003452 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
Alexandre Rames67555f72014-11-18 10:55:16 +00003453 locations->SetInAt(0, Location::RegisterOrConstant(instruction->InputAt(0)));
Alexandre Rames67555f72014-11-18 10:55:16 +00003454}
3455
3456void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction) {
3457 SlowPathCodeARM64* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01003458 new (codegen_->GetScopedAllocator()) DivZeroCheckSlowPathARM64(instruction);
Alexandre Rames67555f72014-11-18 10:55:16 +00003459 codegen_->AddSlowPath(slow_path);
3460 Location value = instruction->GetLocations()->InAt(0);
3461
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003462 DataType::Type type = instruction->GetType();
Alexandre Rames3e69f162014-12-10 10:36:50 +00003463
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003464 if (!DataType::IsIntegralType(type)) {
Nicolas Geoffraye5671612016-03-16 11:03:54 +00003465 LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
Alexandre Rames3e69f162014-12-10 10:36:50 +00003466 return;
3467 }
3468
Alexandre Rames67555f72014-11-18 10:55:16 +00003469 if (value.IsConstant()) {
3470 int64_t divisor = Int64ConstantFrom(value);
3471 if (divisor == 0) {
3472 __ B(slow_path->GetEntryLabel());
3473 } else {
Alexandre Rames3e69f162014-12-10 10:36:50 +00003474 // A division by a non-null constant is valid. We don't need to perform
3475 // any check, so simply fall through.
Alexandre Rames67555f72014-11-18 10:55:16 +00003476 }
3477 } else {
3478 __ Cbz(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
3479 }
3480}
3481
Alexandre Ramesa89086e2014-11-07 17:13:25 +00003482void LocationsBuilderARM64::VisitDoubleConstant(HDoubleConstant* constant) {
3483 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003484 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesa89086e2014-11-07 17:13:25 +00003485 locations->SetOut(Location::ConstantLocation(constant));
3486}
3487
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01003488void InstructionCodeGeneratorARM64::VisitDoubleConstant(
3489 HDoubleConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +00003490 // Will be generated at use site.
3491}
3492
Alexandre Rames5319def2014-10-23 10:03:10 +01003493void LocationsBuilderARM64::VisitExit(HExit* exit) {
3494 exit->SetLocations(nullptr);
3495}
3496
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01003497void InstructionCodeGeneratorARM64::VisitExit(HExit* exit ATTRIBUTE_UNUSED) {
Alexandre Rames5319def2014-10-23 10:03:10 +01003498}
3499
Alexandre Ramesa89086e2014-11-07 17:13:25 +00003500void LocationsBuilderARM64::VisitFloatConstant(HFloatConstant* constant) {
3501 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01003502 new (GetGraph()->GetAllocator()) LocationSummary(constant, LocationSummary::kNoCall);
Alexandre Ramesa89086e2014-11-07 17:13:25 +00003503 locations->SetOut(Location::ConstantLocation(constant));
3504}
3505
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01003506void InstructionCodeGeneratorARM64::VisitFloatConstant(HFloatConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Ramesa89086e2014-11-07 17:13:25 +00003507 // Will be generated at use site.
3508}
3509
David Brazdilfc6a86a2015-06-26 10:33:45 +00003510void InstructionCodeGeneratorARM64::HandleGoto(HInstruction* got, HBasicBlock* successor) {
Aart Bika8b8e9b2018-01-09 11:01:02 -08003511 if (successor->IsExitBlock()) {
3512 DCHECK(got->GetPrevious()->AlwaysThrows());
3513 return; // no code needed
3514 }
3515
Serban Constantinescu02164b32014-11-13 14:05:07 +00003516 HBasicBlock* block = got->GetBlock();
3517 HInstruction* previous = got->GetPrevious();
3518 HLoopInformation* info = block->GetLoopInformation();
3519
David Brazdil46e2a392015-03-16 17:31:52 +00003520 if (info != nullptr && info->IsBackEdge(*block) && info->HasSuspendCheck()) {
Nicolas Geoffray8d728322018-01-18 22:44:32 +00003521 if (codegen_->GetCompilerOptions().CountHotnessInCompiledCode()) {
3522 UseScratchRegisterScope temps(GetVIXLAssembler());
3523 Register temp1 = temps.AcquireX();
3524 Register temp2 = temps.AcquireX();
3525 __ Ldr(temp1, MemOperand(sp, 0));
3526 __ Ldrh(temp2, MemOperand(temp1, ArtMethod::HotnessCountOffset().Int32Value()));
3527 __ Add(temp2, temp2, 1);
3528 __ Strh(temp2, MemOperand(temp1, ArtMethod::HotnessCountOffset().Int32Value()));
3529 }
Serban Constantinescu02164b32014-11-13 14:05:07 +00003530 GenerateSuspendCheck(info->GetSuspendCheck(), successor);
3531 return;
3532 }
3533 if (block->IsEntryBlock() && (previous != nullptr) && previous->IsSuspendCheck()) {
3534 GenerateSuspendCheck(previous->AsSuspendCheck(), nullptr);
Roland Levillain2b03a1f2017-06-06 16:09:59 +01003535 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Serban Constantinescu02164b32014-11-13 14:05:07 +00003536 }
3537 if (!codegen_->GoesToNextBlock(block, successor)) {
Alexandre Rames5319def2014-10-23 10:03:10 +01003538 __ B(codegen_->GetLabelOf(successor));
3539 }
3540}
3541
David Brazdilfc6a86a2015-06-26 10:33:45 +00003542void LocationsBuilderARM64::VisitGoto(HGoto* got) {
3543 got->SetLocations(nullptr);
3544}
3545
3546void InstructionCodeGeneratorARM64::VisitGoto(HGoto* got) {
3547 HandleGoto(got, got->GetSuccessor());
3548}
3549
3550void LocationsBuilderARM64::VisitTryBoundary(HTryBoundary* try_boundary) {
3551 try_boundary->SetLocations(nullptr);
3552}
3553
3554void InstructionCodeGeneratorARM64::VisitTryBoundary(HTryBoundary* try_boundary) {
3555 HBasicBlock* successor = try_boundary->GetNormalFlowSuccessor();
3556 if (!successor->IsExitBlock()) {
3557 HandleGoto(try_boundary, successor);
3558 }
3559}
3560
Mingyao Yangd43b3ac2015-04-01 14:03:04 -07003561void InstructionCodeGeneratorARM64::GenerateTestAndBranch(HInstruction* instruction,
David Brazdil0debae72015-11-12 18:37:00 +00003562 size_t condition_input_index,
Scott Wakeling97c72b72016-06-24 16:19:36 +01003563 vixl::aarch64::Label* true_target,
3564 vixl::aarch64::Label* false_target) {
David Brazdil0debae72015-11-12 18:37:00 +00003565 HInstruction* cond = instruction->InputAt(condition_input_index);
Alexandre Rames5319def2014-10-23 10:03:10 +01003566
David Brazdil0debae72015-11-12 18:37:00 +00003567 if (true_target == nullptr && false_target == nullptr) {
3568 // Nothing to do. The code always falls through.
3569 return;
3570 } else if (cond->IsIntConstant()) {
Roland Levillain1a653882016-03-18 18:05:57 +00003571 // Constant condition, statically compared against "true" (integer value 1).
3572 if (cond->AsIntConstant()->IsTrue()) {
David Brazdil0debae72015-11-12 18:37:00 +00003573 if (true_target != nullptr) {
3574 __ B(true_target);
Serban Constantinescu02164b32014-11-13 14:05:07 +00003575 }
Serban Constantinescu02164b32014-11-13 14:05:07 +00003576 } else {
Roland Levillain1a653882016-03-18 18:05:57 +00003577 DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
David Brazdil0debae72015-11-12 18:37:00 +00003578 if (false_target != nullptr) {
3579 __ B(false_target);
3580 }
Serban Constantinescu02164b32014-11-13 14:05:07 +00003581 }
David Brazdil0debae72015-11-12 18:37:00 +00003582 return;
3583 }
3584
3585 // The following code generates these patterns:
3586 // (1) true_target == nullptr && false_target != nullptr
3587 // - opposite condition true => branch to false_target
3588 // (2) true_target != nullptr && false_target == nullptr
3589 // - condition true => branch to true_target
3590 // (3) true_target != nullptr && false_target != nullptr
3591 // - condition true => branch to true_target
3592 // - branch to false_target
3593 if (IsBooleanValueOrMaterializedCondition(cond)) {
Alexandre Rames5319def2014-10-23 10:03:10 +01003594 // The condition instruction has been materialized, compare the output to 0.
David Brazdil0debae72015-11-12 18:37:00 +00003595 Location cond_val = instruction->GetLocations()->InAt(condition_input_index);
Alexandre Rames5319def2014-10-23 10:03:10 +01003596 DCHECK(cond_val.IsRegister());
David Brazdil0debae72015-11-12 18:37:00 +00003597 if (true_target == nullptr) {
3598 __ Cbz(InputRegisterAt(instruction, condition_input_index), false_target);
3599 } else {
3600 __ Cbnz(InputRegisterAt(instruction, condition_input_index), true_target);
3601 }
Alexandre Rames5319def2014-10-23 10:03:10 +01003602 } else {
3603 // The condition instruction has not been materialized, use its inputs as
3604 // the comparison and its condition as the branch condition.
David Brazdil0debae72015-11-12 18:37:00 +00003605 HCondition* condition = cond->AsCondition();
Roland Levillain7f63c522015-07-13 15:54:55 +00003606
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003607 DataType::Type type = condition->InputAt(0)->GetType();
3608 if (DataType::IsFloatingPointType(type)) {
Roland Levillain1a653882016-03-18 18:05:57 +00003609 GenerateFcmp(condition);
David Brazdil0debae72015-11-12 18:37:00 +00003610 if (true_target == nullptr) {
Vladimir Markod6e069b2016-01-18 11:11:01 +00003611 IfCondition opposite_condition = condition->GetOppositeCondition();
3612 __ B(ARM64FPCondition(opposite_condition, condition->IsGtBias()), false_target);
David Brazdil0debae72015-11-12 18:37:00 +00003613 } else {
Vladimir Markod6e069b2016-01-18 11:11:01 +00003614 __ B(ARM64FPCondition(condition->GetCondition(), condition->IsGtBias()), true_target);
David Brazdil0debae72015-11-12 18:37:00 +00003615 }
Alexandre Rames5319def2014-10-23 10:03:10 +01003616 } else {
Roland Levillain7f63c522015-07-13 15:54:55 +00003617 // Integer cases.
3618 Register lhs = InputRegisterAt(condition, 0);
3619 Operand rhs = InputOperandAt(condition, 1);
David Brazdil0debae72015-11-12 18:37:00 +00003620
3621 Condition arm64_cond;
Scott Wakeling97c72b72016-06-24 16:19:36 +01003622 vixl::aarch64::Label* non_fallthrough_target;
David Brazdil0debae72015-11-12 18:37:00 +00003623 if (true_target == nullptr) {
3624 arm64_cond = ARM64Condition(condition->GetOppositeCondition());
3625 non_fallthrough_target = false_target;
3626 } else {
3627 arm64_cond = ARM64Condition(condition->GetCondition());
3628 non_fallthrough_target = true_target;
3629 }
3630
Aart Bik086d27e2016-01-20 17:02:00 -08003631 if ((arm64_cond == eq || arm64_cond == ne || arm64_cond == lt || arm64_cond == ge) &&
Scott Wakeling97c72b72016-06-24 16:19:36 +01003632 rhs.IsImmediate() && (rhs.GetImmediate() == 0)) {
Roland Levillain7f63c522015-07-13 15:54:55 +00003633 switch (arm64_cond) {
3634 case eq:
David Brazdil0debae72015-11-12 18:37:00 +00003635 __ Cbz(lhs, non_fallthrough_target);
Roland Levillain7f63c522015-07-13 15:54:55 +00003636 break;
3637 case ne:
David Brazdil0debae72015-11-12 18:37:00 +00003638 __ Cbnz(lhs, non_fallthrough_target);
Roland Levillain7f63c522015-07-13 15:54:55 +00003639 break;
3640 case lt:
3641 // Test the sign bit and branch accordingly.
David Brazdil0debae72015-11-12 18:37:00 +00003642 __ Tbnz(lhs, (lhs.IsX() ? kXRegSize : kWRegSize) - 1, non_fallthrough_target);
Roland Levillain7f63c522015-07-13 15:54:55 +00003643 break;
3644 case ge:
3645 // Test the sign bit and branch accordingly.
David Brazdil0debae72015-11-12 18:37:00 +00003646 __ Tbz(lhs, (lhs.IsX() ? kXRegSize : kWRegSize) - 1, non_fallthrough_target);
Roland Levillain7f63c522015-07-13 15:54:55 +00003647 break;
3648 default:
3649 // Without the `static_cast` the compiler throws an error for
3650 // `-Werror=sign-promo`.
3651 LOG(FATAL) << "Unexpected condition: " << static_cast<int>(arm64_cond);
3652 }
3653 } else {
3654 __ Cmp(lhs, rhs);
David Brazdil0debae72015-11-12 18:37:00 +00003655 __ B(arm64_cond, non_fallthrough_target);
Roland Levillain7f63c522015-07-13 15:54:55 +00003656 }
Alexandre Rames5319def2014-10-23 10:03:10 +01003657 }
3658 }
David Brazdil0debae72015-11-12 18:37:00 +00003659
3660 // If neither branch falls through (case 3), the conditional branch to `true_target`
3661 // was already emitted (case 2) and we need to emit a jump to `false_target`.
3662 if (true_target != nullptr && false_target != nullptr) {
Alexandre Rames5319def2014-10-23 10:03:10 +01003663 __ B(false_target);
3664 }
3665}
3666
Mingyao Yangd43b3ac2015-04-01 14:03:04 -07003667void LocationsBuilderARM64::VisitIf(HIf* if_instr) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003668 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(if_instr);
David Brazdil0debae72015-11-12 18:37:00 +00003669 if (IsBooleanValueOrMaterializedCondition(if_instr->InputAt(0))) {
Mingyao Yangd43b3ac2015-04-01 14:03:04 -07003670 locations->SetInAt(0, Location::RequiresRegister());
3671 }
3672}
3673
3674void InstructionCodeGeneratorARM64::VisitIf(HIf* if_instr) {
David Brazdil0debae72015-11-12 18:37:00 +00003675 HBasicBlock* true_successor = if_instr->IfTrueSuccessor();
3676 HBasicBlock* false_successor = if_instr->IfFalseSuccessor();
Scott Wakeling97c72b72016-06-24 16:19:36 +01003677 vixl::aarch64::Label* true_target = codegen_->GetLabelOf(true_successor);
3678 if (codegen_->GoesToNextBlock(if_instr->GetBlock(), true_successor)) {
3679 true_target = nullptr;
3680 }
3681 vixl::aarch64::Label* false_target = codegen_->GetLabelOf(false_successor);
3682 if (codegen_->GoesToNextBlock(if_instr->GetBlock(), false_successor)) {
3683 false_target = nullptr;
3684 }
David Brazdil0debae72015-11-12 18:37:00 +00003685 GenerateTestAndBranch(if_instr, /* condition_input_index */ 0, true_target, false_target);
Mingyao Yangd43b3ac2015-04-01 14:03:04 -07003686}
3687
3688void LocationsBuilderARM64::VisitDeoptimize(HDeoptimize* deoptimize) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003689 LocationSummary* locations = new (GetGraph()->GetAllocator())
Mingyao Yangd43b3ac2015-04-01 14:03:04 -07003690 LocationSummary(deoptimize, LocationSummary::kCallOnSlowPath);
Nicolas Geoffray4e92c3c2017-05-08 09:34:26 +01003691 InvokeRuntimeCallingConvention calling_convention;
3692 RegisterSet caller_saves = RegisterSet::Empty();
3693 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
3694 locations->SetCustomSlowPathCallerSaves(caller_saves);
David Brazdil0debae72015-11-12 18:37:00 +00003695 if (IsBooleanValueOrMaterializedCondition(deoptimize->InputAt(0))) {
Mingyao Yangd43b3ac2015-04-01 14:03:04 -07003696 locations->SetInAt(0, Location::RequiresRegister());
3697 }
3698}
3699
3700void InstructionCodeGeneratorARM64::VisitDeoptimize(HDeoptimize* deoptimize) {
Aart Bik42249c32016-01-07 15:33:50 -08003701 SlowPathCodeARM64* slow_path =
3702 deopt_slow_paths_.NewSlowPath<DeoptimizationSlowPathARM64>(deoptimize);
David Brazdil0debae72015-11-12 18:37:00 +00003703 GenerateTestAndBranch(deoptimize,
3704 /* condition_input_index */ 0,
3705 slow_path->GetEntryLabel(),
3706 /* false_target */ nullptr);
Mingyao Yangd43b3ac2015-04-01 14:03:04 -07003707}
3708
Mingyao Yang063fc772016-08-02 11:02:54 -07003709void LocationsBuilderARM64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003710 LocationSummary* locations = new (GetGraph()->GetAllocator())
Mingyao Yang063fc772016-08-02 11:02:54 -07003711 LocationSummary(flag, LocationSummary::kNoCall);
3712 locations->SetOut(Location::RequiresRegister());
3713}
3714
3715void InstructionCodeGeneratorARM64::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
3716 __ Ldr(OutputRegister(flag),
3717 MemOperand(sp, codegen_->GetStackOffsetOfShouldDeoptimizeFlag()));
3718}
3719
David Brazdilc0b601b2016-02-08 14:20:45 +00003720static inline bool IsConditionOnFloatingPointValues(HInstruction* condition) {
3721 return condition->IsCondition() &&
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003722 DataType::IsFloatingPointType(condition->InputAt(0)->GetType());
David Brazdilc0b601b2016-02-08 14:20:45 +00003723}
3724
Alexandre Rames880f1192016-06-13 16:04:50 +01003725static inline Condition GetConditionForSelect(HCondition* condition) {
3726 IfCondition cond = condition->AsCondition()->GetCondition();
David Brazdilc0b601b2016-02-08 14:20:45 +00003727 return IsConditionOnFloatingPointValues(condition) ? ARM64FPCondition(cond, condition->IsGtBias())
3728 : ARM64Condition(cond);
3729}
3730
David Brazdil74eb1b22015-12-14 11:44:01 +00003731void LocationsBuilderARM64::VisitSelect(HSelect* select) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003732 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(select);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003733 if (DataType::IsFloatingPointType(select->GetType())) {
Alexandre Rames880f1192016-06-13 16:04:50 +01003734 locations->SetInAt(0, Location::RequiresFpuRegister());
3735 locations->SetInAt(1, Location::RequiresFpuRegister());
Donghui Bai426b49c2016-11-08 14:55:38 +08003736 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
Alexandre Rames880f1192016-06-13 16:04:50 +01003737 } else {
3738 HConstant* cst_true_value = select->GetTrueValue()->AsConstant();
3739 HConstant* cst_false_value = select->GetFalseValue()->AsConstant();
3740 bool is_true_value_constant = cst_true_value != nullptr;
3741 bool is_false_value_constant = cst_false_value != nullptr;
3742 // Ask VIXL whether we should synthesize constants in registers.
3743 // We give an arbitrary register to VIXL when dealing with non-constant inputs.
3744 Operand true_op = is_true_value_constant ?
3745 Operand(Int64FromConstant(cst_true_value)) : Operand(x1);
3746 Operand false_op = is_false_value_constant ?
3747 Operand(Int64FromConstant(cst_false_value)) : Operand(x2);
3748 bool true_value_in_register = false;
3749 bool false_value_in_register = false;
3750 MacroAssembler::GetCselSynthesisInformation(
3751 x0, true_op, false_op, &true_value_in_register, &false_value_in_register);
3752 true_value_in_register |= !is_true_value_constant;
3753 false_value_in_register |= !is_false_value_constant;
3754
3755 locations->SetInAt(1, true_value_in_register ? Location::RequiresRegister()
3756 : Location::ConstantLocation(cst_true_value));
3757 locations->SetInAt(0, false_value_in_register ? Location::RequiresRegister()
3758 : Location::ConstantLocation(cst_false_value));
Donghui Bai426b49c2016-11-08 14:55:38 +08003759 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
David Brazdil74eb1b22015-12-14 11:44:01 +00003760 }
Alexandre Rames880f1192016-06-13 16:04:50 +01003761
David Brazdil74eb1b22015-12-14 11:44:01 +00003762 if (IsBooleanValueOrMaterializedCondition(select->GetCondition())) {
3763 locations->SetInAt(2, Location::RequiresRegister());
3764 }
David Brazdil74eb1b22015-12-14 11:44:01 +00003765}
3766
3767void InstructionCodeGeneratorARM64::VisitSelect(HSelect* select) {
David Brazdilc0b601b2016-02-08 14:20:45 +00003768 HInstruction* cond = select->GetCondition();
David Brazdilc0b601b2016-02-08 14:20:45 +00003769 Condition csel_cond;
3770
3771 if (IsBooleanValueOrMaterializedCondition(cond)) {
3772 if (cond->IsCondition() && cond->GetNext() == select) {
Alexandre Rames880f1192016-06-13 16:04:50 +01003773 // Use the condition flags set by the previous instruction.
3774 csel_cond = GetConditionForSelect(cond->AsCondition());
David Brazdilc0b601b2016-02-08 14:20:45 +00003775 } else {
3776 __ Cmp(InputRegisterAt(select, 2), 0);
Alexandre Rames880f1192016-06-13 16:04:50 +01003777 csel_cond = ne;
David Brazdilc0b601b2016-02-08 14:20:45 +00003778 }
3779 } else if (IsConditionOnFloatingPointValues(cond)) {
Roland Levillain1a653882016-03-18 18:05:57 +00003780 GenerateFcmp(cond);
Alexandre Rames880f1192016-06-13 16:04:50 +01003781 csel_cond = GetConditionForSelect(cond->AsCondition());
David Brazdilc0b601b2016-02-08 14:20:45 +00003782 } else {
3783 __ Cmp(InputRegisterAt(cond, 0), InputOperandAt(cond, 1));
Alexandre Rames880f1192016-06-13 16:04:50 +01003784 csel_cond = GetConditionForSelect(cond->AsCondition());
David Brazdilc0b601b2016-02-08 14:20:45 +00003785 }
3786
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01003787 if (DataType::IsFloatingPointType(select->GetType())) {
Alexandre Rames880f1192016-06-13 16:04:50 +01003788 __ Fcsel(OutputFPRegister(select),
3789 InputFPRegisterAt(select, 1),
3790 InputFPRegisterAt(select, 0),
3791 csel_cond);
3792 } else {
3793 __ Csel(OutputRegister(select),
3794 InputOperandAt(select, 1),
3795 InputOperandAt(select, 0),
3796 csel_cond);
David Brazdilc0b601b2016-02-08 14:20:45 +00003797 }
David Brazdil74eb1b22015-12-14 11:44:01 +00003798}
3799
David Srbecky0cf44932015-12-09 14:09:59 +00003800void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01003801 new (GetGraph()->GetAllocator()) LocationSummary(info);
David Srbecky0cf44932015-12-09 14:09:59 +00003802}
3803
David Srbeckyd28f4a02016-03-14 17:14:24 +00003804void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo*) {
3805 // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile.
David Srbeckyc7098ff2016-02-09 14:30:11 +00003806}
3807
3808void CodeGeneratorARM64::GenerateNop() {
3809 __ Nop();
David Srbecky0cf44932015-12-09 14:09:59 +00003810}
3811
Alexandre Rames5319def2014-10-23 10:03:10 +01003812void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
Vladimir Markof4f2daa2017-03-20 18:26:59 +00003813 HandleFieldGet(instruction, instruction->GetFieldInfo());
Alexandre Rames5319def2014-10-23 10:03:10 +01003814}
3815
3816void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
Alexandre Rames09a99962015-04-15 11:47:56 +01003817 HandleFieldGet(instruction, instruction->GetFieldInfo());
Alexandre Rames5319def2014-10-23 10:03:10 +01003818}
3819
3820void LocationsBuilderARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
Alexandre Rames09a99962015-04-15 11:47:56 +01003821 HandleFieldSet(instruction);
Alexandre Rames5319def2014-10-23 10:03:10 +01003822}
3823
3824void InstructionCodeGeneratorARM64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01003825 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
Alexandre Rames5319def2014-10-23 10:03:10 +01003826}
3827
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07003828// Temp is used for read barrier.
3829static size_t NumberOfInstanceOfTemps(TypeCheckKind type_check_kind) {
3830 if (kEmitCompilerReadBarrier &&
Roland Levillain44015862016-01-22 11:47:17 +00003831 (kUseBakerReadBarrier ||
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07003832 type_check_kind == TypeCheckKind::kAbstractClassCheck ||
3833 type_check_kind == TypeCheckKind::kClassHierarchyCheck ||
3834 type_check_kind == TypeCheckKind::kArrayObjectCheck)) {
3835 return 1;
3836 }
3837 return 0;
3838}
3839
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08003840// Interface case has 3 temps, one for holding the number of interfaces, one for the current
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07003841// interface pointer, one for loading the current interface.
3842// The other checks have one temp for loading the object's class.
3843static size_t NumberOfCheckCastTemps(TypeCheckKind type_check_kind) {
3844 if (type_check_kind == TypeCheckKind::kInterfaceCheck) {
3845 return 3;
3846 }
3847 return 1 + NumberOfInstanceOfTemps(type_check_kind);
Roland Levillain44015862016-01-22 11:47:17 +00003848}
3849
Alexandre Rames67555f72014-11-18 10:55:16 +00003850void LocationsBuilderARM64::VisitInstanceOf(HInstanceOf* instruction) {
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003851 LocationSummary::CallKind call_kind = LocationSummary::kNoCall;
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003852 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Vladimir Marko70e97462016-08-09 11:04:26 +01003853 bool baker_read_barrier_slow_path = false;
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003854 switch (type_check_kind) {
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003855 case TypeCheckKind::kExactCheck:
3856 case TypeCheckKind::kAbstractClassCheck:
3857 case TypeCheckKind::kClassHierarchyCheck:
Vladimir Marko87584542017-12-12 17:47:52 +00003858 case TypeCheckKind::kArrayObjectCheck: {
3859 bool needs_read_barrier = CodeGenerator::InstanceOfNeedsReadBarrier(instruction);
3860 call_kind = needs_read_barrier ? LocationSummary::kCallOnSlowPath : LocationSummary::kNoCall;
3861 baker_read_barrier_slow_path = kUseBakerReadBarrier && needs_read_barrier;
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003862 break;
Vladimir Marko87584542017-12-12 17:47:52 +00003863 }
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003864 case TypeCheckKind::kArrayCheck:
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003865 case TypeCheckKind::kUnresolvedCheck:
3866 case TypeCheckKind::kInterfaceCheck:
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003867 call_kind = LocationSummary::kCallOnSlowPath;
3868 break;
3869 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003870
Vladimir Markoca6fff82017-10-03 14:49:14 +01003871 LocationSummary* locations =
3872 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Vladimir Marko70e97462016-08-09 11:04:26 +01003873 if (baker_read_barrier_slow_path) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01003874 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Vladimir Marko70e97462016-08-09 11:04:26 +01003875 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003876 locations->SetInAt(0, Location::RequiresRegister());
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003877 locations->SetInAt(1, Location::RequiresRegister());
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003878 // The "out" register is used as a temporary, so it overlaps with the inputs.
3879 // Note that TypeCheckSlowPathARM64 uses this register too.
3880 locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07003881 // Add temps if necessary for read barriers.
3882 locations->AddRegisterTemps(NumberOfInstanceOfTemps(type_check_kind));
Alexandre Rames67555f72014-11-18 10:55:16 +00003883}
3884
3885void InstructionCodeGeneratorARM64::VisitInstanceOf(HInstanceOf* instruction) {
Roland Levillain44015862016-01-22 11:47:17 +00003886 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Alexandre Rames67555f72014-11-18 10:55:16 +00003887 LocationSummary* locations = instruction->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003888 Location obj_loc = locations->InAt(0);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003889 Register obj = InputRegisterAt(instruction, 0);
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00003890 Register cls = InputRegisterAt(instruction, 1);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003891 Location out_loc = locations->Out();
Alexandre Rames67555f72014-11-18 10:55:16 +00003892 Register out = OutputRegister(instruction);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07003893 const size_t num_temps = NumberOfInstanceOfTemps(type_check_kind);
3894 DCHECK_LE(num_temps, 1u);
3895 Location maybe_temp_loc = (num_temps >= 1) ? locations->GetTemp(0) : Location::NoLocation();
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003896 uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
3897 uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
3898 uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
3899 uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
Alexandre Rames67555f72014-11-18 10:55:16 +00003900
Scott Wakeling97c72b72016-06-24 16:19:36 +01003901 vixl::aarch64::Label done, zero;
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003902 SlowPathCodeARM64* slow_path = nullptr;
Alexandre Rames67555f72014-11-18 10:55:16 +00003903
3904 // Return 0 if `obj` is null.
Guillaume "Vermeille" Sanchezaf888352015-04-20 14:41:30 +01003905 // Avoid null check if we know `obj` is not null.
3906 if (instruction->MustDoNullCheck()) {
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003907 __ Cbz(obj, &zero);
3908 }
3909
Roland Levillain44015862016-01-22 11:47:17 +00003910 switch (type_check_kind) {
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003911 case TypeCheckKind::kExactCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00003912 ReadBarrierOption read_barrier_option =
3913 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -08003914 // /* HeapReference<Class> */ out = obj->klass_
3915 GenerateReferenceLoadTwoRegisters(instruction,
3916 out_loc,
3917 obj_loc,
3918 class_offset,
3919 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00003920 read_barrier_option);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003921 __ Cmp(out, cls);
3922 __ Cset(out, eq);
3923 if (zero.IsLinked()) {
3924 __ B(&done);
3925 }
3926 break;
3927 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003928
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003929 case TypeCheckKind::kAbstractClassCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00003930 ReadBarrierOption read_barrier_option =
3931 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -08003932 // /* HeapReference<Class> */ out = obj->klass_
3933 GenerateReferenceLoadTwoRegisters(instruction,
3934 out_loc,
3935 obj_loc,
3936 class_offset,
3937 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00003938 read_barrier_option);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003939 // If the class is abstract, we eagerly fetch the super class of the
3940 // object to avoid doing a comparison we know will fail.
Scott Wakeling97c72b72016-06-24 16:19:36 +01003941 vixl::aarch64::Label loop, success;
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003942 __ Bind(&loop);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003943 // /* HeapReference<Class> */ out = out->super_class_
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08003944 GenerateReferenceLoadOneRegister(instruction,
3945 out_loc,
3946 super_offset,
3947 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00003948 read_barrier_option);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003949 // If `out` is null, we use it for the result, and jump to `done`.
3950 __ Cbz(out, &done);
3951 __ Cmp(out, cls);
3952 __ B(ne, &loop);
3953 __ Mov(out, 1);
3954 if (zero.IsLinked()) {
3955 __ B(&done);
3956 }
3957 break;
3958 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003959
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003960 case TypeCheckKind::kClassHierarchyCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00003961 ReadBarrierOption read_barrier_option =
3962 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -08003963 // /* HeapReference<Class> */ out = obj->klass_
3964 GenerateReferenceLoadTwoRegisters(instruction,
3965 out_loc,
3966 obj_loc,
3967 class_offset,
3968 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00003969 read_barrier_option);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003970 // Walk over the class hierarchy to find a match.
Scott Wakeling97c72b72016-06-24 16:19:36 +01003971 vixl::aarch64::Label loop, success;
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003972 __ Bind(&loop);
3973 __ Cmp(out, cls);
3974 __ B(eq, &success);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003975 // /* HeapReference<Class> */ out = out->super_class_
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08003976 GenerateReferenceLoadOneRegister(instruction,
3977 out_loc,
3978 super_offset,
3979 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00003980 read_barrier_option);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003981 __ Cbnz(out, &loop);
3982 // If `out` is null, we use it for the result, and jump to `done`.
3983 __ B(&done);
3984 __ Bind(&success);
3985 __ Mov(out, 1);
3986 if (zero.IsLinked()) {
3987 __ B(&done);
3988 }
3989 break;
3990 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00003991
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00003992 case TypeCheckKind::kArrayObjectCheck: {
Vladimir Marko87584542017-12-12 17:47:52 +00003993 ReadBarrierOption read_barrier_option =
3994 CodeGenerator::ReadBarrierOptionForInstanceOf(instruction);
Mathieu Chartier9fd8c602016-11-14 14:38:53 -08003995 // /* HeapReference<Class> */ out = obj->klass_
3996 GenerateReferenceLoadTwoRegisters(instruction,
3997 out_loc,
3998 obj_loc,
3999 class_offset,
4000 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00004001 read_barrier_option);
Nicolas Geoffrayabfcf182015-09-21 18:41:21 +01004002 // Do an exact check.
Scott Wakeling97c72b72016-06-24 16:19:36 +01004003 vixl::aarch64::Label exact_check;
Nicolas Geoffrayabfcf182015-09-21 18:41:21 +01004004 __ Cmp(out, cls);
4005 __ B(eq, &exact_check);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004006 // Otherwise, we need to check that the object's class is a non-primitive array.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004007 // /* HeapReference<Class> */ out = out->component_type_
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08004008 GenerateReferenceLoadOneRegister(instruction,
4009 out_loc,
4010 component_offset,
4011 maybe_temp_loc,
Vladimir Marko87584542017-12-12 17:47:52 +00004012 read_barrier_option);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004013 // If `out` is null, we use it for the result, and jump to `done`.
4014 __ Cbz(out, &done);
4015 __ Ldrh(out, HeapOperand(out, primitive_offset));
4016 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
4017 __ Cbnz(out, &zero);
Nicolas Geoffrayabfcf182015-09-21 18:41:21 +01004018 __ Bind(&exact_check);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004019 __ Mov(out, 1);
4020 __ B(&done);
4021 break;
4022 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004023
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004024 case TypeCheckKind::kArrayCheck: {
Mathieu Chartier9fd8c602016-11-14 14:38:53 -08004025 // No read barrier since the slow path will retry upon failure.
4026 // /* HeapReference<Class> */ out = obj->klass_
4027 GenerateReferenceLoadTwoRegisters(instruction,
4028 out_loc,
4029 obj_loc,
4030 class_offset,
4031 maybe_temp_loc,
4032 kWithoutReadBarrier);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004033 __ Cmp(out, cls);
4034 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01004035 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARM64(
4036 instruction, /* is_fatal */ false);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004037 codegen_->AddSlowPath(slow_path);
4038 __ B(ne, slow_path->GetEntryLabel());
4039 __ Mov(out, 1);
4040 if (zero.IsLinked()) {
4041 __ B(&done);
4042 }
4043 break;
4044 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004045
Calin Juravle98893e12015-10-02 21:05:03 +01004046 case TypeCheckKind::kUnresolvedCheck:
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004047 case TypeCheckKind::kInterfaceCheck: {
4048 // Note that we indeed only call on slow path, but we always go
4049 // into the slow path for the unresolved and interface check
4050 // cases.
4051 //
4052 // We cannot directly call the InstanceofNonTrivial runtime
4053 // entry point without resorting to a type checking slow path
4054 // here (i.e. by calling InvokeRuntime directly), as it would
4055 // require to assign fixed registers for the inputs of this
4056 // HInstanceOf instruction (following the runtime calling
4057 // convention), which might be cluttered by the potential first
4058 // read barrier emission at the beginning of this method.
Roland Levillain44015862016-01-22 11:47:17 +00004059 //
4060 // TODO: Introduce a new runtime entry point taking the object
4061 // to test (instead of its class) as argument, and let it deal
4062 // with the read barrier issues. This will let us refactor this
4063 // case of the `switch` code as it was previously (with a direct
4064 // call to the runtime not using a type checking slow path).
4065 // This should also be beneficial for the other cases above.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004066 DCHECK(locations->OnlyCallsOnSlowPath());
Vladimir Marko174b2e22017-10-12 13:34:49 +01004067 slow_path = new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARM64(
4068 instruction, /* is_fatal */ false);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004069 codegen_->AddSlowPath(slow_path);
4070 __ B(slow_path->GetEntryLabel());
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004071 if (zero.IsLinked()) {
4072 __ B(&done);
4073 }
4074 break;
4075 }
4076 }
4077
4078 if (zero.IsLinked()) {
4079 __ Bind(&zero);
Guillaume "Vermeille" Sanchezaf888352015-04-20 14:41:30 +01004080 __ Mov(out, 0);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004081 }
4082
4083 if (done.IsLinked()) {
4084 __ Bind(&done);
4085 }
4086
4087 if (slow_path != nullptr) {
4088 __ Bind(slow_path->GetExitLabel());
4089 }
4090}
4091
4092void LocationsBuilderARM64::VisitCheckCast(HCheckCast* instruction) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004093 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Vladimir Marko87584542017-12-12 17:47:52 +00004094 LocationSummary::CallKind call_kind = CodeGenerator::GetCheckCastCallKind(instruction);
Vladimir Markoca6fff82017-10-03 14:49:14 +01004095 LocationSummary* locations =
4096 new (GetGraph()->GetAllocator()) LocationSummary(instruction, call_kind);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004097 locations->SetInAt(0, Location::RequiresRegister());
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00004098 locations->SetInAt(1, Location::RequiresRegister());
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004099 // Add temps for read barriers and other uses. One is used by TypeCheckSlowPathARM64.
4100 locations->AddRegisterTemps(NumberOfCheckCastTemps(type_check_kind));
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004101}
4102
4103void InstructionCodeGeneratorARM64::VisitCheckCast(HCheckCast* instruction) {
Roland Levillain44015862016-01-22 11:47:17 +00004104 TypeCheckKind type_check_kind = instruction->GetTypeCheckKind();
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004105 LocationSummary* locations = instruction->GetLocations();
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004106 Location obj_loc = locations->InAt(0);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004107 Register obj = InputRegisterAt(instruction, 0);
Nicolas Geoffraybff7a522018-01-25 13:33:07 +00004108 Register cls = InputRegisterAt(instruction, 1);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004109 const size_t num_temps = NumberOfCheckCastTemps(type_check_kind);
4110 DCHECK_GE(num_temps, 1u);
4111 DCHECK_LE(num_temps, 3u);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004112 Location temp_loc = locations->GetTemp(0);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004113 Location maybe_temp2_loc = (num_temps >= 2) ? locations->GetTemp(1) : Location::NoLocation();
4114 Location maybe_temp3_loc = (num_temps >= 3) ? locations->GetTemp(2) : Location::NoLocation();
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004115 Register temp = WRegisterFrom(temp_loc);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004116 const uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
4117 const uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
4118 const uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
4119 const uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value();
4120 const uint32_t iftable_offset = mirror::Class::IfTableOffset().Uint32Value();
4121 const uint32_t array_length_offset = mirror::Array::LengthOffset().Uint32Value();
4122 const uint32_t object_array_data_offset =
4123 mirror::Array::DataOffset(kHeapReferenceSize).Uint32Value();
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004124
Vladimir Marko87584542017-12-12 17:47:52 +00004125 bool is_type_check_slow_path_fatal = CodeGenerator::IsTypeCheckSlowPathFatal(instruction);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004126 SlowPathCodeARM64* type_check_slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01004127 new (codegen_->GetScopedAllocator()) TypeCheckSlowPathARM64(
4128 instruction, is_type_check_slow_path_fatal);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004129 codegen_->AddSlowPath(type_check_slow_path);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004130
Scott Wakeling97c72b72016-06-24 16:19:36 +01004131 vixl::aarch64::Label done;
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004132 // Avoid null check if we know obj is not null.
4133 if (instruction->MustDoNullCheck()) {
Guillaume "Vermeille" Sanchezaf888352015-04-20 14:41:30 +01004134 __ Cbz(obj, &done);
4135 }
Alexandre Rames67555f72014-11-18 10:55:16 +00004136
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004137 switch (type_check_kind) {
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004138 case TypeCheckKind::kExactCheck:
4139 case TypeCheckKind::kArrayCheck: {
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004140 // /* HeapReference<Class> */ temp = obj->klass_
4141 GenerateReferenceLoadTwoRegisters(instruction,
4142 temp_loc,
4143 obj_loc,
4144 class_offset,
4145 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004146 kWithoutReadBarrier);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004147
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004148 __ Cmp(temp, cls);
4149 // Jump to slow path for throwing the exception or doing a
4150 // more involved array check.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004151 __ B(ne, type_check_slow_path->GetEntryLabel());
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004152 break;
4153 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004154
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004155 case TypeCheckKind::kAbstractClassCheck: {
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004156 // /* HeapReference<Class> */ temp = obj->klass_
4157 GenerateReferenceLoadTwoRegisters(instruction,
4158 temp_loc,
4159 obj_loc,
4160 class_offset,
4161 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004162 kWithoutReadBarrier);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004163
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004164 // If the class is abstract, we eagerly fetch the super class of the
4165 // object to avoid doing a comparison we know will fail.
Mathieu Chartierb99f4d62016-11-07 16:17:26 -08004166 vixl::aarch64::Label loop;
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004167 __ Bind(&loop);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004168 // /* HeapReference<Class> */ temp = temp->super_class_
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08004169 GenerateReferenceLoadOneRegister(instruction,
4170 temp_loc,
4171 super_offset,
4172 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004173 kWithoutReadBarrier);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004174
Mathieu Chartierb99f4d62016-11-07 16:17:26 -08004175 // If the class reference currently in `temp` is null, jump to the slow path to throw the
4176 // exception.
4177 __ Cbz(temp, type_check_slow_path->GetEntryLabel());
4178 // Otherwise, compare classes.
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004179 __ Cmp(temp, cls);
4180 __ B(ne, &loop);
4181 break;
4182 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004183
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004184 case TypeCheckKind::kClassHierarchyCheck: {
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004185 // /* HeapReference<Class> */ temp = obj->klass_
4186 GenerateReferenceLoadTwoRegisters(instruction,
4187 temp_loc,
4188 obj_loc,
4189 class_offset,
4190 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004191 kWithoutReadBarrier);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004192
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004193 // Walk over the class hierarchy to find a match.
Scott Wakeling97c72b72016-06-24 16:19:36 +01004194 vixl::aarch64::Label loop;
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004195 __ Bind(&loop);
4196 __ Cmp(temp, cls);
Nicolas Geoffrayabfcf182015-09-21 18:41:21 +01004197 __ B(eq, &done);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004198
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004199 // /* HeapReference<Class> */ temp = temp->super_class_
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08004200 GenerateReferenceLoadOneRegister(instruction,
4201 temp_loc,
4202 super_offset,
4203 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004204 kWithoutReadBarrier);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004205
4206 // If the class reference currently in `temp` is not null, jump
4207 // back at the beginning of the loop.
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004208 __ Cbnz(temp, &loop);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004209 // Otherwise, jump to the slow path to throw the exception.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004210 __ B(type_check_slow_path->GetEntryLabel());
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004211 break;
4212 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004213
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004214 case TypeCheckKind::kArrayObjectCheck: {
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004215 // /* HeapReference<Class> */ temp = obj->klass_
4216 GenerateReferenceLoadTwoRegisters(instruction,
4217 temp_loc,
4218 obj_loc,
4219 class_offset,
4220 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004221 kWithoutReadBarrier);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004222
Nicolas Geoffrayabfcf182015-09-21 18:41:21 +01004223 // Do an exact check.
4224 __ Cmp(temp, cls);
4225 __ B(eq, &done);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004226
4227 // Otherwise, we need to check that the object's class is a non-primitive array.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004228 // /* HeapReference<Class> */ temp = temp->component_type_
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08004229 GenerateReferenceLoadOneRegister(instruction,
4230 temp_loc,
4231 component_offset,
4232 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004233 kWithoutReadBarrier);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004234
Mathieu Chartierb99f4d62016-11-07 16:17:26 -08004235 // If the component type is null, jump to the slow path to throw the exception.
4236 __ Cbz(temp, type_check_slow_path->GetEntryLabel());
4237 // Otherwise, the object is indeed an array. Further check that this component type is not a
4238 // primitive type.
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004239 __ Ldrh(temp, HeapOperand(temp, primitive_offset));
4240 static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot");
Mathieu Chartierb99f4d62016-11-07 16:17:26 -08004241 __ Cbnz(temp, type_check_slow_path->GetEntryLabel());
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004242 break;
4243 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004244
Calin Juravle98893e12015-10-02 21:05:03 +01004245 case TypeCheckKind::kUnresolvedCheck:
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004246 // We always go into the type check slow path for the unresolved check cases.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004247 //
4248 // We cannot directly call the CheckCast runtime entry point
4249 // without resorting to a type checking slow path here (i.e. by
4250 // calling InvokeRuntime directly), as it would require to
4251 // assign fixed registers for the inputs of this HInstanceOf
4252 // instruction (following the runtime calling convention), which
4253 // might be cluttered by the potential first read barrier
4254 // emission at the beginning of this method.
4255 __ B(type_check_slow_path->GetEntryLabel());
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004256 break;
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004257 case TypeCheckKind::kInterfaceCheck: {
4258 // /* HeapReference<Class> */ temp = obj->klass_
4259 GenerateReferenceLoadTwoRegisters(instruction,
4260 temp_loc,
4261 obj_loc,
4262 class_offset,
4263 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004264 kWithoutReadBarrier);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004265
4266 // /* HeapReference<Class> */ temp = temp->iftable_
4267 GenerateReferenceLoadTwoRegisters(instruction,
4268 temp_loc,
4269 temp_loc,
4270 iftable_offset,
4271 maybe_temp2_loc,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004272 kWithoutReadBarrier);
Mathieu Chartier6beced42016-11-15 15:51:31 -08004273 // Iftable is never null.
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004274 __ Ldr(WRegisterFrom(maybe_temp2_loc), HeapOperand(temp.W(), array_length_offset));
Mathieu Chartier6beced42016-11-15 15:51:31 -08004275 // Loop through the iftable and check if any class matches.
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004276 vixl::aarch64::Label start_loop;
4277 __ Bind(&start_loop);
Mathieu Chartierafbcdaf2016-11-14 10:50:29 -08004278 __ Cbz(WRegisterFrom(maybe_temp2_loc), type_check_slow_path->GetEntryLabel());
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004279 __ Ldr(WRegisterFrom(maybe_temp3_loc), HeapOperand(temp.W(), object_array_data_offset));
4280 GetAssembler()->MaybeUnpoisonHeapReference(WRegisterFrom(maybe_temp3_loc));
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004281 // Go to next interface.
4282 __ Add(temp, temp, 2 * kHeapReferenceSize);
4283 __ Sub(WRegisterFrom(maybe_temp2_loc), WRegisterFrom(maybe_temp2_loc), 2);
Mathieu Chartierafbcdaf2016-11-14 10:50:29 -08004284 // Compare the classes and continue the loop if they do not match.
4285 __ Cmp(cls, WRegisterFrom(maybe_temp3_loc));
4286 __ B(ne, &start_loop);
Mathieu Chartier5c44c1b2016-11-04 18:13:04 -07004287 break;
4288 }
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004289 }
Nicolas Geoffray75374372015-09-17 17:12:19 +00004290 __ Bind(&done);
Nicolas Geoffray85c7bab2015-09-18 13:40:46 +00004291
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004292 __ Bind(type_check_slow_path->GetExitLabel());
Alexandre Rames67555f72014-11-18 10:55:16 +00004293}
4294
Alexandre Rames5319def2014-10-23 10:03:10 +01004295void LocationsBuilderARM64::VisitIntConstant(HIntConstant* constant) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01004296 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
Alexandre Rames5319def2014-10-23 10:03:10 +01004297 locations->SetOut(Location::ConstantLocation(constant));
4298}
4299
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01004300void InstructionCodeGeneratorARM64::VisitIntConstant(HIntConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Rames5319def2014-10-23 10:03:10 +01004301 // Will be generated at use site.
4302}
4303
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +00004304void LocationsBuilderARM64::VisitNullConstant(HNullConstant* constant) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01004305 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +00004306 locations->SetOut(Location::ConstantLocation(constant));
4307}
4308
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01004309void InstructionCodeGeneratorARM64::VisitNullConstant(HNullConstant* constant ATTRIBUTE_UNUSED) {
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +00004310 // Will be generated at use site.
Nicolas Geoffrayd6138ef2015-02-18 14:48:53 +00004311}
4312
Calin Juravle175dc732015-08-25 15:42:32 +01004313void LocationsBuilderARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
4314 // The trampoline uses the same calling convention as dex calling conventions,
4315 // except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain
4316 // the method_idx.
4317 HandleInvoke(invoke);
4318}
4319
4320void InstructionCodeGeneratorARM64::VisitInvokeUnresolved(HInvokeUnresolved* invoke) {
4321 codegen_->GenerateInvokeUnresolvedRuntimeCall(invoke);
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004322 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Calin Juravle175dc732015-08-25 15:42:32 +01004323}
4324
Alexandre Rames5319def2014-10-23 10:03:10 +01004325void LocationsBuilderARM64::HandleInvoke(HInvoke* invoke) {
Roland Levillain2d27c8e2015-04-28 15:48:45 +01004326 InvokeDexCallingConventionVisitorARM64 calling_convention_visitor;
Nicolas Geoffrayfd88f162015-06-03 11:23:52 +01004327 CodeGenerator::CreateCommonInvokeLocationSummary(invoke, &calling_convention_visitor);
Alexandre Rames5319def2014-10-23 10:03:10 +01004328}
4329
Alexandre Rames67555f72014-11-18 10:55:16 +00004330void LocationsBuilderARM64::VisitInvokeInterface(HInvokeInterface* invoke) {
4331 HandleInvoke(invoke);
4332}
4333
4334void InstructionCodeGeneratorARM64::VisitInvokeInterface(HInvokeInterface* invoke) {
4335 // TODO: b/18116999, our IMTs can miss an IncompatibleClassChangeError.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004336 LocationSummary* locations = invoke->GetLocations();
4337 Register temp = XRegisterFrom(locations->GetTemp(0));
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004338 Location receiver = locations->InAt(0);
Alexandre Rames67555f72014-11-18 10:55:16 +00004339 Offset class_offset = mirror::Object::ClassOffset();
Andreas Gampe542451c2016-07-26 09:02:02 -07004340 Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize);
Alexandre Rames67555f72014-11-18 10:55:16 +00004341
4342 // The register ip1 is required to be used for the hidden argument in
4343 // art_quick_imt_conflict_trampoline, so prevent VIXL from using it.
Alexandre Ramesd921d642015-04-16 15:07:16 +01004344 MacroAssembler* masm = GetVIXLAssembler();
4345 UseScratchRegisterScope scratch_scope(masm);
Alexandre Rames67555f72014-11-18 10:55:16 +00004346 scratch_scope.Exclude(ip1);
4347 __ Mov(ip1, invoke->GetDexMethodIndex());
4348
Artem Serov914d7a82017-02-07 14:33:49 +00004349 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
Alexandre Rames67555f72014-11-18 10:55:16 +00004350 if (receiver.IsStackSlot()) {
Mathieu Chartiere401d142015-04-22 13:56:20 -07004351 __ Ldr(temp.W(), StackOperandFrom(receiver));
Artem Serov914d7a82017-02-07 14:33:49 +00004352 {
4353 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
4354 // /* HeapReference<Class> */ temp = temp->klass_
4355 __ Ldr(temp.W(), HeapOperand(temp.W(), class_offset));
4356 codegen_->MaybeRecordImplicitNullCheck(invoke);
4357 }
Alexandre Rames67555f72014-11-18 10:55:16 +00004358 } else {
Artem Serov914d7a82017-02-07 14:33:49 +00004359 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004360 // /* HeapReference<Class> */ temp = receiver->klass_
Mathieu Chartiere401d142015-04-22 13:56:20 -07004361 __ Ldr(temp.W(), HeapOperandFrom(receiver, class_offset));
Artem Serov914d7a82017-02-07 14:33:49 +00004362 codegen_->MaybeRecordImplicitNullCheck(invoke);
Alexandre Rames67555f72014-11-18 10:55:16 +00004363 }
Artem Serov914d7a82017-02-07 14:33:49 +00004364
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004365 // Instead of simply (possibly) unpoisoning `temp` here, we should
4366 // emit a read barrier for the previous class reference load.
4367 // However this is not required in practice, as this is an
4368 // intermediate/temporary reference and because the current
4369 // concurrent copying collector keeps the from-space memory
4370 // intact/accessible until the end of the marking phase (the
4371 // concurrent copying collector may not in the future).
Roland Levillain4d027112015-07-01 15:41:14 +01004372 GetAssembler()->MaybeUnpoisonHeapReference(temp.W());
Artem Udovichenkoa62cb9b2016-06-30 09:18:25 +00004373 __ Ldr(temp,
4374 MemOperand(temp, mirror::Class::ImtPtrOffset(kArm64PointerSize).Uint32Value()));
4375 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
Matthew Gharrity465ecc82016-07-19 21:32:52 +00004376 invoke->GetImtIndex(), kArm64PointerSize));
Alexandre Rames67555f72014-11-18 10:55:16 +00004377 // temp = temp->GetImtEntryAt(method_offset);
Mathieu Chartiere401d142015-04-22 13:56:20 -07004378 __ Ldr(temp, MemOperand(temp, method_offset));
Alexandre Rames67555f72014-11-18 10:55:16 +00004379 // lr = temp->GetEntryPoint();
Mathieu Chartiere401d142015-04-22 13:56:20 -07004380 __ Ldr(lr, MemOperand(temp, entry_point.Int32Value()));
Artem Serov914d7a82017-02-07 14:33:49 +00004381
4382 {
4383 // Ensure the pc position is recorded immediately after the `blr` instruction.
4384 ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
4385
4386 // lr();
4387 __ blr(lr);
4388 DCHECK(!codegen_->IsLeafMethod());
4389 codegen_->RecordPcInfo(invoke, invoke->GetDexPc());
4390 }
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004391
4392 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames67555f72014-11-18 10:55:16 +00004393}
4394
4395void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01004396 IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetAllocator(), codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08004397 if (intrinsic.TryDispatch(invoke)) {
4398 return;
4399 }
4400
Alexandre Rames67555f72014-11-18 10:55:16 +00004401 HandleInvoke(invoke);
4402}
4403
Nicolas Geoffraye53798a2014-12-01 10:31:54 +00004404void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
David Brazdil58282f42016-01-14 12:45:10 +00004405 // Explicit clinit checks triggered by static invokes must have been pruned by
4406 // art::PrepareForRegisterAllocation.
4407 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
Roland Levillain4c0eb422015-04-24 16:43:49 +01004408
Vladimir Markoca6fff82017-10-03 14:49:14 +01004409 IntrinsicLocationsBuilderARM64 intrinsic(GetGraph()->GetAllocator(), codegen_);
Andreas Gampe878d58c2015-01-15 23:24:00 -08004410 if (intrinsic.TryDispatch(invoke)) {
4411 return;
4412 }
4413
Alexandre Rames67555f72014-11-18 10:55:16 +00004414 HandleInvoke(invoke);
4415}
4416
Andreas Gampe878d58c2015-01-15 23:24:00 -08004417static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorARM64* codegen) {
4418 if (invoke->GetLocations()->Intrinsified()) {
4419 IntrinsicCodeGeneratorARM64 intrinsic(codegen);
4420 intrinsic.Dispatch(invoke);
4421 return true;
4422 }
4423 return false;
4424}
4425
Vladimir Markodc151b22015-10-15 18:02:30 +01004426HInvokeStaticOrDirect::DispatchInfo CodeGeneratorARM64::GetSupportedInvokeStaticOrDirectDispatch(
4427 const HInvokeStaticOrDirect::DispatchInfo& desired_dispatch_info,
Nicolas Geoffray5e4e11e2016-09-22 13:17:41 +01004428 HInvokeStaticOrDirect* invoke ATTRIBUTE_UNUSED) {
Roland Levillain44015862016-01-22 11:47:17 +00004429 // On ARM64 we support all dispatch types.
Vladimir Markodc151b22015-10-15 18:02:30 +01004430 return desired_dispatch_info;
4431}
4432
Vladimir Markoe7197bf2017-06-02 17:00:23 +01004433void CodeGeneratorARM64::GenerateStaticOrDirectCall(
4434 HInvokeStaticOrDirect* invoke, Location temp, SlowPathCode* slow_path) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08004435 // Make sure that ArtMethod* is passed in kArtMethodRegister as per the calling convention.
Vladimir Marko58155012015-08-19 12:49:41 +00004436 Location callee_method = temp; // For all kinds except kRecursive, callee will be in temp.
4437 switch (invoke->GetMethodLoadKind()) {
Nicolas Geoffrayda079bb2016-09-26 17:56:07 +01004438 case HInvokeStaticOrDirect::MethodLoadKind::kStringInit: {
4439 uint32_t offset =
4440 GetThreadOffset<kArm64PointerSize>(invoke->GetStringInitEntryPoint()).Int32Value();
Vladimir Marko58155012015-08-19 12:49:41 +00004441 // temp = thread->string_init_entrypoint
Nicolas Geoffrayda079bb2016-09-26 17:56:07 +01004442 __ Ldr(XRegisterFrom(temp), MemOperand(tr, offset));
Vladimir Marko58155012015-08-19 12:49:41 +00004443 break;
Nicolas Geoffrayda079bb2016-09-26 17:56:07 +01004444 }
Vladimir Marko58155012015-08-19 12:49:41 +00004445 case HInvokeStaticOrDirect::MethodLoadKind::kRecursive:
Vladimir Markoc53c0792015-11-19 15:48:33 +00004446 callee_method = invoke->GetLocations()->InAt(invoke->GetSpecialInputIndex());
Vladimir Marko58155012015-08-19 12:49:41 +00004447 break;
Vladimir Marko65979462017-05-19 17:25:12 +01004448 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative: {
4449 DCHECK(GetCompilerOptions().IsBootImage());
4450 // Add ADRP with its PC-relative method patch.
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004451 vixl::aarch64::Label* adrp_label = NewBootImageMethodPatch(invoke->GetTargetMethod());
Vladimir Marko65979462017-05-19 17:25:12 +01004452 EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp));
4453 // Add ADD with its PC-relative method patch.
4454 vixl::aarch64::Label* add_label =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004455 NewBootImageMethodPatch(invoke->GetTargetMethod(), adrp_label);
Vladimir Marko65979462017-05-19 17:25:12 +01004456 EmitAddPlaceholder(add_label, XRegisterFrom(temp), XRegisterFrom(temp));
4457 break;
4458 }
Vladimir Marko58155012015-08-19 12:49:41 +00004459 case HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress:
4460 // Load method address from literal pool.
Alexandre Rames6dc01742015-11-12 14:44:19 +00004461 __ Ldr(XRegisterFrom(temp), DeduplicateUint64Literal(invoke->GetMethodAddress()));
Vladimir Marko58155012015-08-19 12:49:41 +00004462 break;
Vladimir Markob066d432018-01-03 13:14:37 +00004463 case HInvokeStaticOrDirect::MethodLoadKind::kBootImageRelRo: {
4464 // Add ADRP with its PC-relative .data.bimg.rel.ro patch.
Vladimir Markoe47f60c2018-02-21 13:43:28 +00004465 uint32_t boot_image_offset = GetBootImageOffset(invoke);
Vladimir Markob066d432018-01-03 13:14:37 +00004466 vixl::aarch64::Label* adrp_label = NewBootImageRelRoPatch(boot_image_offset);
4467 EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp));
4468 // Add LDR with its PC-relative .data.bimg.rel.ro patch.
4469 vixl::aarch64::Label* ldr_label = NewBootImageRelRoPatch(boot_image_offset, adrp_label);
4470 // Note: Boot image is in the low 4GiB and the entry is 32-bit, so emit a 32-bit load.
4471 EmitLdrOffsetPlaceholder(ldr_label, WRegisterFrom(temp), XRegisterFrom(temp));
4472 break;
4473 }
Vladimir Marko0eb882b2017-05-15 13:39:18 +01004474 case HInvokeStaticOrDirect::MethodLoadKind::kBssEntry: {
Vladimir Markob066d432018-01-03 13:14:37 +00004475 // Add ADRP with its PC-relative .bss entry patch.
Vladimir Marko0eb882b2017-05-15 13:39:18 +01004476 MethodReference target_method(&GetGraph()->GetDexFile(), invoke->GetDexMethodIndex());
4477 vixl::aarch64::Label* adrp_label = NewMethodBssEntryPatch(target_method);
Vladimir Markoaad75c62016-10-03 08:46:48 +00004478 EmitAdrpPlaceholder(adrp_label, XRegisterFrom(temp));
Vladimir Markob066d432018-01-03 13:14:37 +00004479 // Add LDR with its PC-relative .bss entry patch.
Scott Wakeling97c72b72016-06-24 16:19:36 +01004480 vixl::aarch64::Label* ldr_label =
Vladimir Marko0eb882b2017-05-15 13:39:18 +01004481 NewMethodBssEntryPatch(target_method, adrp_label);
Vladimir Markoaad75c62016-10-03 08:46:48 +00004482 EmitLdrOffsetPlaceholder(ldr_label, XRegisterFrom(temp), XRegisterFrom(temp));
Vladimir Marko58155012015-08-19 12:49:41 +00004483 break;
Vladimir Marko9b688a02015-05-06 14:12:42 +01004484 }
Vladimir Markoe7197bf2017-06-02 17:00:23 +01004485 case HInvokeStaticOrDirect::MethodLoadKind::kRuntimeCall: {
4486 GenerateInvokeStaticOrDirectRuntimeCall(invoke, temp, slow_path);
4487 return; // No code pointer retrieval; the runtime performs the call directly.
Vladimir Marko58155012015-08-19 12:49:41 +00004488 }
4489 }
4490
4491 switch (invoke->GetCodePtrLocation()) {
4492 case HInvokeStaticOrDirect::CodePtrLocation::kCallSelf:
Vladimir Markoe7197bf2017-06-02 17:00:23 +01004493 {
4494 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
4495 ExactAssemblyScope eas(GetVIXLAssembler(),
4496 kInstructionSize,
4497 CodeBufferCheckScope::kExactSize);
4498 __ bl(&frame_entry_label_);
4499 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
4500 }
Vladimir Marko58155012015-08-19 12:49:41 +00004501 break;
Vladimir Marko58155012015-08-19 12:49:41 +00004502 case HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod:
4503 // LR = callee_method->entry_point_from_quick_compiled_code_;
4504 __ Ldr(lr, MemOperand(
Alexandre Rames6dc01742015-11-12 14:44:19 +00004505 XRegisterFrom(callee_method),
Andreas Gampe542451c2016-07-26 09:02:02 -07004506 ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize).Int32Value()));
Artem Serov914d7a82017-02-07 14:33:49 +00004507 {
Vladimir Markoe7197bf2017-06-02 17:00:23 +01004508 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
Artem Serov914d7a82017-02-07 14:33:49 +00004509 ExactAssemblyScope eas(GetVIXLAssembler(),
4510 kInstructionSize,
4511 CodeBufferCheckScope::kExactSize);
4512 // lr()
4513 __ blr(lr);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01004514 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
Artem Serov914d7a82017-02-07 14:33:49 +00004515 }
Vladimir Marko58155012015-08-19 12:49:41 +00004516 break;
Nicolas Geoffray1cf95282014-12-12 19:22:03 +00004517 }
Alexandre Rames5319def2014-10-23 10:03:10 +01004518
Andreas Gampe878d58c2015-01-15 23:24:00 -08004519 DCHECK(!IsLeafMethod());
4520}
4521
Vladimir Markoe7197bf2017-06-02 17:00:23 +01004522void CodeGeneratorARM64::GenerateVirtualCall(
4523 HInvokeVirtual* invoke, Location temp_in, SlowPathCode* slow_path) {
Nicolas Geoffraye5234232015-12-02 09:06:11 +00004524 // Use the calling convention instead of the location of the receiver, as
4525 // intrinsics may have put the receiver in a different register. In the intrinsics
4526 // slow path, the arguments have been moved to the right place, so here we are
4527 // guaranteed that the receiver is the first register of the calling convention.
4528 InvokeDexCallingConvention calling_convention;
4529 Register receiver = calling_convention.GetRegisterAt(0);
Andreas Gampebfb5ba92015-09-01 15:45:02 +00004530 Register temp = XRegisterFrom(temp_in);
4531 size_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
4532 invoke->GetVTableIndex(), kArm64PointerSize).SizeValue();
4533 Offset class_offset = mirror::Object::ClassOffset();
Andreas Gampe542451c2016-07-26 09:02:02 -07004534 Offset entry_point = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize);
Andreas Gampebfb5ba92015-09-01 15:45:02 +00004535
Andreas Gampebfb5ba92015-09-01 15:45:02 +00004536 DCHECK(receiver.IsRegister());
Artem Serov914d7a82017-02-07 14:33:49 +00004537
4538 {
4539 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
4540 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
4541 // /* HeapReference<Class> */ temp = receiver->klass_
4542 __ Ldr(temp.W(), HeapOperandFrom(LocationFrom(receiver), class_offset));
4543 MaybeRecordImplicitNullCheck(invoke);
4544 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004545 // Instead of simply (possibly) unpoisoning `temp` here, we should
4546 // emit a read barrier for the previous class reference load.
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004547 // intermediate/temporary reference and because the current
4548 // concurrent copying collector keeps the from-space memory
4549 // intact/accessible until the end of the marking phase (the
4550 // concurrent copying collector may not in the future).
Andreas Gampebfb5ba92015-09-01 15:45:02 +00004551 GetAssembler()->MaybeUnpoisonHeapReference(temp.W());
4552 // temp = temp->GetMethodAt(method_offset);
4553 __ Ldr(temp, MemOperand(temp, method_offset));
4554 // lr = temp->GetEntryPoint();
4555 __ Ldr(lr, MemOperand(temp, entry_point.SizeValue()));
Artem Serov914d7a82017-02-07 14:33:49 +00004556 {
Vladimir Markoe7197bf2017-06-02 17:00:23 +01004557 // Use a scope to help guarantee that `RecordPcInfo()` records the correct pc.
Artem Serov914d7a82017-02-07 14:33:49 +00004558 ExactAssemblyScope eas(GetVIXLAssembler(), kInstructionSize, CodeBufferCheckScope::kExactSize);
4559 // lr();
4560 __ blr(lr);
Vladimir Markoe7197bf2017-06-02 17:00:23 +01004561 RecordPcInfo(invoke, invoke->GetDexPc(), slow_path);
Artem Serov914d7a82017-02-07 14:33:49 +00004562 }
Andreas Gampebfb5ba92015-09-01 15:45:02 +00004563}
4564
Orion Hodsonac141392017-01-13 11:53:47 +00004565void LocationsBuilderARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
4566 HandleInvoke(invoke);
4567}
4568
4569void InstructionCodeGeneratorARM64::VisitInvokePolymorphic(HInvokePolymorphic* invoke) {
4570 codegen_->GenerateInvokePolymorphicCall(invoke);
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004571 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Orion Hodsonac141392017-01-13 11:53:47 +00004572}
4573
Vladimir Markob066d432018-01-03 13:14:37 +00004574vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageRelRoPatch(
4575 uint32_t boot_image_offset,
4576 vixl::aarch64::Label* adrp_label) {
4577 return NewPcRelativePatch(
4578 /* dex_file */ nullptr, boot_image_offset, adrp_label, &boot_image_method_patches_);
4579}
4580
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004581vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageMethodPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01004582 MethodReference target_method,
Scott Wakeling97c72b72016-06-24 16:19:36 +01004583 vixl::aarch64::Label* adrp_label) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004584 return NewPcRelativePatch(
4585 target_method.dex_file, target_method.index, adrp_label, &boot_image_method_patches_);
Vladimir Markocac5a7e2016-02-22 10:39:50 +00004586}
4587
Vladimir Marko0eb882b2017-05-15 13:39:18 +01004588vixl::aarch64::Label* CodeGeneratorARM64::NewMethodBssEntryPatch(
4589 MethodReference target_method,
4590 vixl::aarch64::Label* adrp_label) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004591 return NewPcRelativePatch(
4592 target_method.dex_file, target_method.index, adrp_label, &method_bss_entry_patches_);
Vladimir Marko0eb882b2017-05-15 13:39:18 +01004593}
4594
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004595vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageTypePatch(
Scott Wakeling97c72b72016-06-24 16:19:36 +01004596 const DexFile& dex_file,
Andreas Gampea5b09a62016-11-17 15:21:22 -08004597 dex::TypeIndex type_index,
Scott Wakeling97c72b72016-06-24 16:19:36 +01004598 vixl::aarch64::Label* adrp_label) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004599 return NewPcRelativePatch(&dex_file, type_index.index_, adrp_label, &boot_image_type_patches_);
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004600}
4601
Vladimir Marko1998cd02017-01-13 13:02:58 +00004602vixl::aarch64::Label* CodeGeneratorARM64::NewBssEntryTypePatch(
4603 const DexFile& dex_file,
4604 dex::TypeIndex type_index,
4605 vixl::aarch64::Label* adrp_label) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004606 return NewPcRelativePatch(&dex_file, type_index.index_, adrp_label, &type_bss_entry_patches_);
Vladimir Marko1998cd02017-01-13 13:02:58 +00004607}
4608
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004609vixl::aarch64::Label* CodeGeneratorARM64::NewBootImageStringPatch(
Vladimir Marko65979462017-05-19 17:25:12 +01004610 const DexFile& dex_file,
4611 dex::StringIndex string_index,
4612 vixl::aarch64::Label* adrp_label) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004613 return NewPcRelativePatch(
4614 &dex_file, string_index.index_, adrp_label, &boot_image_string_patches_);
Vladimir Marko65979462017-05-19 17:25:12 +01004615}
4616
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01004617vixl::aarch64::Label* CodeGeneratorARM64::NewStringBssEntryPatch(
4618 const DexFile& dex_file,
4619 dex::StringIndex string_index,
4620 vixl::aarch64::Label* adrp_label) {
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004621 return NewPcRelativePatch(&dex_file, string_index.index_, adrp_label, &string_bss_entry_patches_);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01004622}
4623
Vladimir Markof4f2daa2017-03-20 18:26:59 +00004624vixl::aarch64::Label* CodeGeneratorARM64::NewBakerReadBarrierPatch(uint32_t custom_data) {
4625 baker_read_barrier_patches_.emplace_back(custom_data);
4626 return &baker_read_barrier_patches_.back().label;
4627}
4628
Scott Wakeling97c72b72016-06-24 16:19:36 +01004629vixl::aarch64::Label* CodeGeneratorARM64::NewPcRelativePatch(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004630 const DexFile* dex_file,
Scott Wakeling97c72b72016-06-24 16:19:36 +01004631 uint32_t offset_or_index,
4632 vixl::aarch64::Label* adrp_label,
4633 ArenaDeque<PcRelativePatchInfo>* patches) {
Vladimir Markocac5a7e2016-02-22 10:39:50 +00004634 // Add a patch entry and return the label.
4635 patches->emplace_back(dex_file, offset_or_index);
4636 PcRelativePatchInfo* info = &patches->back();
Scott Wakeling97c72b72016-06-24 16:19:36 +01004637 vixl::aarch64::Label* label = &info->label;
Vladimir Markocac5a7e2016-02-22 10:39:50 +00004638 // If adrp_label is null, this is the ADRP patch and needs to point to its own label.
4639 info->pc_insn_label = (adrp_label != nullptr) ? adrp_label : label;
4640 return label;
4641}
4642
Scott Wakeling97c72b72016-06-24 16:19:36 +01004643vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateBootImageAddressLiteral(
4644 uint64_t address) {
Vladimir Marko0eb882b2017-05-15 13:39:18 +01004645 return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address));
Vladimir Markocac5a7e2016-02-22 10:39:50 +00004646}
4647
Nicolas Geoffray132d8362016-11-16 09:19:42 +00004648vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitStringLiteral(
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00004649 const DexFile& dex_file, dex::StringIndex string_index, Handle<mirror::String> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01004650 ReserveJitStringRoot(StringReference(&dex_file, string_index), handle);
Nicolas Geoffray132d8362016-11-16 09:19:42 +00004651 return jit_string_patches_.GetOrCreate(
4652 StringReference(&dex_file, string_index),
4653 [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
4654}
4655
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00004656vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateJitClassLiteral(
Nicolas Geoffray5247c082017-01-13 14:17:29 +00004657 const DexFile& dex_file, dex::TypeIndex type_index, Handle<mirror::Class> handle) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01004658 ReserveJitClassRoot(TypeReference(&dex_file, type_index), handle);
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00004659 return jit_class_patches_.GetOrCreate(
4660 TypeReference(&dex_file, type_index),
4661 [this]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(/* placeholder */ 0u); });
4662}
4663
Vladimir Markoaad75c62016-10-03 08:46:48 +00004664void CodeGeneratorARM64::EmitAdrpPlaceholder(vixl::aarch64::Label* fixup_label,
4665 vixl::aarch64::Register reg) {
4666 DCHECK(reg.IsX());
4667 SingleEmissionCheckScope guard(GetVIXLAssembler());
4668 __ Bind(fixup_label);
Scott Wakelingb77051e2016-11-21 19:46:00 +00004669 __ adrp(reg, /* offset placeholder */ static_cast<int64_t>(0));
Vladimir Markoaad75c62016-10-03 08:46:48 +00004670}
4671
4672void CodeGeneratorARM64::EmitAddPlaceholder(vixl::aarch64::Label* fixup_label,
4673 vixl::aarch64::Register out,
4674 vixl::aarch64::Register base) {
4675 DCHECK(out.IsX());
4676 DCHECK(base.IsX());
4677 SingleEmissionCheckScope guard(GetVIXLAssembler());
4678 __ Bind(fixup_label);
4679 __ add(out, base, Operand(/* offset placeholder */ 0));
4680}
4681
4682void CodeGeneratorARM64::EmitLdrOffsetPlaceholder(vixl::aarch64::Label* fixup_label,
4683 vixl::aarch64::Register out,
4684 vixl::aarch64::Register base) {
4685 DCHECK(base.IsX());
4686 SingleEmissionCheckScope guard(GetVIXLAssembler());
4687 __ Bind(fixup_label);
4688 __ ldr(out, MemOperand(base, /* offset placeholder */ 0));
4689}
4690
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01004691template <linker::LinkerPatch (*Factory)(size_t, const DexFile*, uint32_t, uint32_t)>
Vladimir Markoaad75c62016-10-03 08:46:48 +00004692inline void CodeGeneratorARM64::EmitPcRelativeLinkerPatches(
4693 const ArenaDeque<PcRelativePatchInfo>& infos,
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01004694 ArenaVector<linker::LinkerPatch>* linker_patches) {
Vladimir Markoaad75c62016-10-03 08:46:48 +00004695 for (const PcRelativePatchInfo& info : infos) {
4696 linker_patches->push_back(Factory(info.label.GetLocation(),
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004697 info.target_dex_file,
Vladimir Markoaad75c62016-10-03 08:46:48 +00004698 info.pc_insn_label->GetLocation(),
4699 info.offset_or_index));
4700 }
4701}
4702
Vladimir Markob066d432018-01-03 13:14:37 +00004703linker::LinkerPatch DataBimgRelRoPatchAdapter(size_t literal_offset,
4704 const DexFile* target_dex_file,
4705 uint32_t pc_insn_offset,
4706 uint32_t boot_image_offset) {
4707 DCHECK(target_dex_file == nullptr); // Unused for DataBimgRelRoPatch(), should be null.
4708 return linker::LinkerPatch::DataBimgRelRoPatch(literal_offset, pc_insn_offset, boot_image_offset);
4709}
4710
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01004711void CodeGeneratorARM64::EmitLinkerPatches(ArenaVector<linker::LinkerPatch>* linker_patches) {
Vladimir Marko58155012015-08-19 12:49:41 +00004712 DCHECK(linker_patches->empty());
4713 size_t size =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004714 boot_image_method_patches_.size() +
Vladimir Marko0eb882b2017-05-15 13:39:18 +01004715 method_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004716 boot_image_type_patches_.size() +
Vladimir Markof4f2daa2017-03-20 18:26:59 +00004717 type_bss_entry_patches_.size() +
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004718 boot_image_string_patches_.size() +
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01004719 string_bss_entry_patches_.size() +
Vladimir Markof4f2daa2017-03-20 18:26:59 +00004720 baker_read_barrier_patches_.size();
Vladimir Marko58155012015-08-19 12:49:41 +00004721 linker_patches->reserve(size);
Vladimir Marko65979462017-05-19 17:25:12 +01004722 if (GetCompilerOptions().IsBootImage()) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01004723 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeMethodPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004724 boot_image_method_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01004725 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeTypePatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004726 boot_image_type_patches_, linker_patches);
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01004727 EmitPcRelativeLinkerPatches<linker::LinkerPatch::RelativeStringPatch>(
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004728 boot_image_string_patches_, linker_patches);
Vladimir Marko65979462017-05-19 17:25:12 +01004729 } else {
Vladimir Markob066d432018-01-03 13:14:37 +00004730 EmitPcRelativeLinkerPatches<DataBimgRelRoPatchAdapter>(
4731 boot_image_method_patches_, linker_patches);
Vladimir Markoe47f60c2018-02-21 13:43:28 +00004732 DCHECK(boot_image_type_patches_.empty());
4733 DCHECK(boot_image_string_patches_.empty());
Vladimir Markocac5a7e2016-02-22 10:39:50 +00004734 }
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01004735 EmitPcRelativeLinkerPatches<linker::LinkerPatch::MethodBssEntryPatch>(
4736 method_bss_entry_patches_, linker_patches);
4737 EmitPcRelativeLinkerPatches<linker::LinkerPatch::TypeBssEntryPatch>(
4738 type_bss_entry_patches_, linker_patches);
4739 EmitPcRelativeLinkerPatches<linker::LinkerPatch::StringBssEntryPatch>(
4740 string_bss_entry_patches_, linker_patches);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00004741 for (const BakerReadBarrierPatchInfo& info : baker_read_barrier_patches_) {
Vladimir Markod8dbc8d2017-09-20 13:37:47 +01004742 linker_patches->push_back(linker::LinkerPatch::BakerReadBarrierBranchPatch(
4743 info.label.GetLocation(), info.custom_data));
Vladimir Markof4f2daa2017-03-20 18:26:59 +00004744 }
Vladimir Marko1998cd02017-01-13 13:02:58 +00004745 DCHECK_EQ(size, linker_patches->size());
Vladimir Marko58155012015-08-19 12:49:41 +00004746}
4747
Vladimir Marko0eb882b2017-05-15 13:39:18 +01004748vixl::aarch64::Literal<uint32_t>* CodeGeneratorARM64::DeduplicateUint32Literal(uint32_t value) {
4749 return uint32_literals_.GetOrCreate(
Vladimir Markocac5a7e2016-02-22 10:39:50 +00004750 value,
4751 [this, value]() { return __ CreateLiteralDestroyedWithPool<uint32_t>(value); });
4752}
4753
Scott Wakeling97c72b72016-06-24 16:19:36 +01004754vixl::aarch64::Literal<uint64_t>* CodeGeneratorARM64::DeduplicateUint64Literal(uint64_t value) {
Vladimir Markocac5a7e2016-02-22 10:39:50 +00004755 return uint64_literals_.GetOrCreate(
4756 value,
4757 [this, value]() { return __ CreateLiteralDestroyedWithPool<uint64_t>(value); });
Vladimir Marko58155012015-08-19 12:49:41 +00004758}
4759
Andreas Gampe878d58c2015-01-15 23:24:00 -08004760void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) {
David Brazdil58282f42016-01-14 12:45:10 +00004761 // Explicit clinit checks triggered by static invokes must have been pruned by
4762 // art::PrepareForRegisterAllocation.
4763 DCHECK(!invoke->IsStaticWithExplicitClinitCheck());
Roland Levillain4c0eb422015-04-24 16:43:49 +01004764
Andreas Gampe878d58c2015-01-15 23:24:00 -08004765 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004766 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Andreas Gampe878d58c2015-01-15 23:24:00 -08004767 return;
4768 }
4769
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004770 {
4771 // Ensure that between the BLR (emitted by GenerateStaticOrDirectCall) and RecordPcInfo there
4772 // are no pools emitted.
4773 EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
4774 LocationSummary* locations = invoke->GetLocations();
4775 codegen_->GenerateStaticOrDirectCall(
4776 invoke, locations->HasTemps() ? locations->GetTemp(0) : Location::NoLocation());
4777 }
4778
4779 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames5319def2014-10-23 10:03:10 +01004780}
4781
4782void InstructionCodeGeneratorARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) {
Andreas Gampe878d58c2015-01-15 23:24:00 -08004783 if (TryGenerateIntrinsicCode(invoke, codegen_)) {
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004784 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Andreas Gampe878d58c2015-01-15 23:24:00 -08004785 return;
4786 }
4787
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004788 {
4789 // Ensure that between the BLR (emitted by GenerateVirtualCall) and RecordPcInfo there
4790 // are no pools emitted.
4791 EmissionCheckScope guard(GetVIXLAssembler(), kInvokeCodeMarginSizeInBytes);
4792 codegen_->GenerateVirtualCall(invoke, invoke->GetLocations()->GetTemp(0));
4793 DCHECK(!codegen_->IsLeafMethod());
4794 }
4795
4796 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames5319def2014-10-23 10:03:10 +01004797}
4798
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004799HLoadClass::LoadKind CodeGeneratorARM64::GetSupportedLoadClassKind(
4800 HLoadClass::LoadKind desired_class_load_kind) {
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004801 switch (desired_class_load_kind) {
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00004802 case HLoadClass::LoadKind::kInvalid:
4803 LOG(FATAL) << "UNREACHABLE";
4804 UNREACHABLE();
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004805 case HLoadClass::LoadKind::kReferrersClass:
4806 break;
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004807 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00004808 case HLoadClass::LoadKind::kBootImageRelRo:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00004809 case HLoadClass::LoadKind::kBssEntry:
4810 DCHECK(!Runtime::Current()->UseJitCompilation());
4811 break;
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00004812 case HLoadClass::LoadKind::kJitTableAddress:
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004813 DCHECK(Runtime::Current()->UseJitCompilation());
4814 break;
Vladimir Marko764d4542017-05-16 10:31:41 +01004815 case HLoadClass::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01004816 case HLoadClass::LoadKind::kRuntimeCall:
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004817 break;
4818 }
4819 return desired_class_load_kind;
4820}
4821
Alexandre Rames67555f72014-11-18 10:55:16 +00004822void LocationsBuilderARM64::VisitLoadClass(HLoadClass* cls) {
Vladimir Marko41559982017-01-06 14:04:23 +00004823 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01004824 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004825 InvokeRuntimeCallingConvention calling_convention;
Vladimir Marko41559982017-01-06 14:04:23 +00004826 CodeGenerator::CreateLoadClassRuntimeCallLocationSummary(
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004827 cls,
4828 LocationFrom(calling_convention.GetRegisterAt(0)),
Vladimir Marko41559982017-01-06 14:04:23 +00004829 LocationFrom(vixl::aarch64::x0));
Vladimir Markoea4c1262017-02-06 19:59:33 +00004830 DCHECK(calling_convention.GetRegisterAt(0).Is(vixl::aarch64::x0));
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004831 return;
4832 }
Vladimir Marko41559982017-01-06 14:04:23 +00004833 DCHECK(!cls->NeedsAccessCheck());
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004834
Mathieu Chartier31b12e32016-09-02 17:11:57 -07004835 const bool requires_read_barrier = kEmitCompilerReadBarrier && !cls->IsInBootImage();
4836 LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || requires_read_barrier)
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004837 ? LocationSummary::kCallOnSlowPath
4838 : LocationSummary::kNoCall;
Vladimir Markoca6fff82017-10-03 14:49:14 +01004839 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(cls, call_kind);
Mathieu Chartier31b12e32016-09-02 17:11:57 -07004840 if (kUseBakerReadBarrier && requires_read_barrier && !cls->NeedsEnvironment()) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01004841 locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers.
Vladimir Marko70e97462016-08-09 11:04:26 +01004842 }
4843
Vladimir Marko41559982017-01-06 14:04:23 +00004844 if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004845 locations->SetInAt(0, Location::RequiresRegister());
4846 }
4847 locations->SetOut(Location::RequiresRegister());
Vladimir Markoea4c1262017-02-06 19:59:33 +00004848 if (cls->GetLoadKind() == HLoadClass::LoadKind::kBssEntry) {
4849 if (!kUseReadBarrier || kUseBakerReadBarrier) {
4850 // Rely on the type resolution or initialization and marking to save everything we need.
Vladimir Markoea4c1262017-02-06 19:59:33 +00004851 RegisterSet caller_saves = RegisterSet::Empty();
4852 InvokeRuntimeCallingConvention calling_convention;
4853 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
4854 DCHECK_EQ(calling_convention.GetRegisterAt(0).GetCode(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01004855 RegisterFrom(calling_convention.GetReturnLocation(DataType::Type::kReference),
4856 DataType::Type::kReference).GetCode());
Vladimir Markoea4c1262017-02-06 19:59:33 +00004857 locations->SetCustomSlowPathCallerSaves(caller_saves);
4858 } else {
4859 // For non-Baker read barrier we have a temp-clobbering call.
4860 }
4861 }
Alexandre Rames67555f72014-11-18 10:55:16 +00004862}
4863
Nicolas Geoffray5247c082017-01-13 14:17:29 +00004864// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
4865// move.
4866void InstructionCodeGeneratorARM64::VisitLoadClass(HLoadClass* cls) NO_THREAD_SAFETY_ANALYSIS {
Vladimir Marko41559982017-01-06 14:04:23 +00004867 HLoadClass::LoadKind load_kind = cls->GetLoadKind();
Vladimir Marko847e6ce2017-06-02 13:55:07 +01004868 if (load_kind == HLoadClass::LoadKind::kRuntimeCall) {
Vladimir Marko41559982017-01-06 14:04:23 +00004869 codegen_->GenerateLoadClassRuntimeCall(cls);
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004870 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Calin Juravle580b6092015-10-06 17:35:58 +01004871 return;
4872 }
Vladimir Marko41559982017-01-06 14:04:23 +00004873 DCHECK(!cls->NeedsAccessCheck());
Calin Juravle580b6092015-10-06 17:35:58 +01004874
Roland Levillain22ccc3a2015-11-24 13:10:05 +00004875 Location out_loc = cls->GetLocations()->Out();
Calin Juravle580b6092015-10-06 17:35:58 +01004876 Register out = OutputRegister(cls);
Alexandre Rames67555f72014-11-18 10:55:16 +00004877
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004878 const ReadBarrierOption read_barrier_option = cls->IsInBootImage()
4879 ? kWithoutReadBarrier
4880 : kCompilerReadBarrierOption;
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004881 bool generate_null_check = false;
Vladimir Marko41559982017-01-06 14:04:23 +00004882 switch (load_kind) {
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004883 case HLoadClass::LoadKind::kReferrersClass: {
4884 DCHECK(!cls->CanCallRuntime());
4885 DCHECK(!cls->MustGenerateClinitCheck());
4886 // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
4887 Register current_method = InputRegisterAt(cls, 0);
Mathieu Chartier31b12e32016-09-02 17:11:57 -07004888 GenerateGcRootFieldLoad(cls,
4889 out_loc,
4890 current_method,
4891 ArtMethod::DeclaringClassOffset().Int32Value(),
Roland Levillain00468f32016-10-27 18:02:48 +01004892 /* fixup_label */ nullptr,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004893 read_barrier_option);
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004894 break;
4895 }
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004896 case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004897 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004898 // Add ADRP with its PC-relative type patch.
4899 const DexFile& dex_file = cls->GetDexFile();
Andreas Gampea5b09a62016-11-17 15:21:22 -08004900 dex::TypeIndex type_index = cls->GetTypeIndex();
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004901 vixl::aarch64::Label* adrp_label = codegen_->NewBootImageTypePatch(dex_file, type_index);
Vladimir Markoaad75c62016-10-03 08:46:48 +00004902 codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004903 // Add ADD with its PC-relative type patch.
Scott Wakeling97c72b72016-06-24 16:19:36 +01004904 vixl::aarch64::Label* add_label =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00004905 codegen_->NewBootImageTypePatch(dex_file, type_index, adrp_label);
Vladimir Markoaad75c62016-10-03 08:46:48 +00004906 codegen_->EmitAddPlaceholder(add_label, out.X(), out.X());
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004907 break;
4908 }
4909 case HLoadClass::LoadKind::kBootImageAddress: {
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08004910 DCHECK_EQ(read_barrier_option, kWithoutReadBarrier);
Nicolas Geoffray5247c082017-01-13 14:17:29 +00004911 uint32_t address = dchecked_integral_cast<uint32_t>(
4912 reinterpret_cast<uintptr_t>(cls->GetClass().Get()));
4913 DCHECK_NE(address, 0u);
4914 __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004915 break;
4916 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00004917 case HLoadClass::LoadKind::kBootImageRelRo: {
Vladimir Marko94ec2db2017-09-06 17:21:03 +01004918 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
Vladimir Markoe47f60c2018-02-21 13:43:28 +00004919 uint32_t boot_image_offset = codegen_->GetBootImageOffset(cls);
4920 // Add ADRP with its PC-relative .data.bimg.rel.ro patch.
4921 vixl::aarch64::Label* adrp_label = codegen_->NewBootImageRelRoPatch(boot_image_offset);
Vladimir Marko94ec2db2017-09-06 17:21:03 +01004922 codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
Vladimir Markoe47f60c2018-02-21 13:43:28 +00004923 // Add LDR with its PC-relative .data.bimg.rel.ro patch.
Vladimir Marko94ec2db2017-09-06 17:21:03 +01004924 vixl::aarch64::Label* ldr_label =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00004925 codegen_->NewBootImageRelRoPatch(boot_image_offset, adrp_label);
Vladimir Marko94ec2db2017-09-06 17:21:03 +01004926 codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X());
Vladimir Marko94ec2db2017-09-06 17:21:03 +01004927 break;
4928 }
Vladimir Marko6bec91c2017-01-09 15:03:12 +00004929 case HLoadClass::LoadKind::kBssEntry: {
4930 // Add ADRP with its PC-relative Class .bss entry patch.
4931 const DexFile& dex_file = cls->GetDexFile();
4932 dex::TypeIndex type_index = cls->GetTypeIndex();
Vladimir Markof3c52b42017-11-17 17:32:12 +00004933 vixl::aarch64::Register temp = XRegisterFrom(out_loc);
4934 vixl::aarch64::Label* adrp_label = codegen_->NewBssEntryTypePatch(dex_file, type_index);
4935 codegen_->EmitAdrpPlaceholder(adrp_label, temp);
Vladimir Markoe47f60c2018-02-21 13:43:28 +00004936 // Add LDR with its PC-relative Class .bss entry patch.
Vladimir Marko6bec91c2017-01-09 15:03:12 +00004937 vixl::aarch64::Label* ldr_label =
Vladimir Markof3c52b42017-11-17 17:32:12 +00004938 codegen_->NewBssEntryTypePatch(dex_file, type_index, adrp_label);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00004939 // /* GcRoot<mirror::Class> */ out = *(base_address + offset) /* PC-relative */
4940 GenerateGcRootFieldLoad(cls,
Vladimir Markoea4c1262017-02-06 19:59:33 +00004941 out_loc,
Vladimir Markof3c52b42017-11-17 17:32:12 +00004942 temp,
Vladimir Markoea4c1262017-02-06 19:59:33 +00004943 /* offset placeholder */ 0u,
Vladimir Marko6bec91c2017-01-09 15:03:12 +00004944 ldr_label,
Vladimir Markoea4c1262017-02-06 19:59:33 +00004945 read_barrier_option);
Vladimir Marko6bec91c2017-01-09 15:03:12 +00004946 generate_null_check = true;
4947 break;
4948 }
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00004949 case HLoadClass::LoadKind::kJitTableAddress: {
4950 __ Ldr(out, codegen_->DeduplicateJitClassLiteral(cls->GetDexFile(),
4951 cls->GetTypeIndex(),
Nicolas Geoffray5247c082017-01-13 14:17:29 +00004952 cls->GetClass()));
Mathieu Chartier31b12e32016-09-02 17:11:57 -07004953 GenerateGcRootFieldLoad(cls,
4954 out_loc,
4955 out.X(),
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00004956 /* offset */ 0,
Roland Levillain00468f32016-10-27 18:02:48 +01004957 /* fixup_label */ nullptr,
Vladimir Markoea4c1262017-02-06 19:59:33 +00004958 read_barrier_option);
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004959 break;
4960 }
Vladimir Marko847e6ce2017-06-02 13:55:07 +01004961 case HLoadClass::LoadKind::kRuntimeCall:
Nicolas Geoffray83c8e272017-01-31 14:36:37 +00004962 case HLoadClass::LoadKind::kInvalid:
Vladimir Marko41559982017-01-06 14:04:23 +00004963 LOG(FATAL) << "UNREACHABLE";
4964 UNREACHABLE();
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004965 }
4966
Vladimir Markoea4c1262017-02-06 19:59:33 +00004967 bool do_clinit = cls->MustGenerateClinitCheck();
4968 if (generate_null_check || do_clinit) {
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004969 DCHECK(cls->CanCallRuntime());
Vladimir Marko174b2e22017-10-12 13:34:49 +01004970 SlowPathCodeARM64* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathARM64(
Vladimir Markof3c52b42017-11-17 17:32:12 +00004971 cls, cls, cls->GetDexPc(), do_clinit);
Vladimir Markodbb7f5b2016-03-30 13:23:58 +01004972 codegen_->AddSlowPath(slow_path);
4973 if (generate_null_check) {
4974 __ Cbz(out, slow_path->GetEntryLabel());
4975 }
4976 if (cls->MustGenerateClinitCheck()) {
4977 GenerateClassInitializationCheck(slow_path, out);
4978 } else {
4979 __ Bind(slow_path->GetExitLabel());
Alexandre Rames67555f72014-11-18 10:55:16 +00004980 }
Roland Levillain2b03a1f2017-06-06 16:09:59 +01004981 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames67555f72014-11-18 10:55:16 +00004982 }
4983}
4984
David Brazdilcb1c0552015-08-04 16:22:25 +01004985static MemOperand GetExceptionTlsAddress() {
Andreas Gampe542451c2016-07-26 09:02:02 -07004986 return MemOperand(tr, Thread::ExceptionOffset<kArm64PointerSize>().Int32Value());
David Brazdilcb1c0552015-08-04 16:22:25 +01004987}
4988
Alexandre Rames67555f72014-11-18 10:55:16 +00004989void LocationsBuilderARM64::VisitLoadException(HLoadException* load) {
4990 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01004991 new (GetGraph()->GetAllocator()) LocationSummary(load, LocationSummary::kNoCall);
Alexandre Rames67555f72014-11-18 10:55:16 +00004992 locations->SetOut(Location::RequiresRegister());
4993}
4994
4995void InstructionCodeGeneratorARM64::VisitLoadException(HLoadException* instruction) {
David Brazdilcb1c0552015-08-04 16:22:25 +01004996 __ Ldr(OutputRegister(instruction), GetExceptionTlsAddress());
4997}
4998
4999void LocationsBuilderARM64::VisitClearException(HClearException* clear) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005000 new (GetGraph()->GetAllocator()) LocationSummary(clear, LocationSummary::kNoCall);
David Brazdilcb1c0552015-08-04 16:22:25 +01005001}
5002
5003void InstructionCodeGeneratorARM64::VisitClearException(HClearException* clear ATTRIBUTE_UNUSED) {
5004 __ Str(wzr, GetExceptionTlsAddress());
Alexandre Rames67555f72014-11-18 10:55:16 +00005005}
5006
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005007HLoadString::LoadKind CodeGeneratorARM64::GetSupportedLoadStringKind(
5008 HLoadString::LoadKind desired_string_load_kind) {
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005009 switch (desired_string_load_kind) {
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005010 case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
Vladimir Markoe47f60c2018-02-21 13:43:28 +00005011 case HLoadString::LoadKind::kBootImageRelRo:
Vladimir Markoaad75c62016-10-03 08:46:48 +00005012 case HLoadString::LoadKind::kBssEntry:
Calin Juravleffc87072016-04-20 14:22:09 +01005013 DCHECK(!Runtime::Current()->UseJitCompilation());
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005014 break;
Nicolas Geoffray132d8362016-11-16 09:19:42 +00005015 case HLoadString::LoadKind::kJitTableAddress:
5016 DCHECK(Runtime::Current()->UseJitCompilation());
5017 break;
Vladimir Marko764d4542017-05-16 10:31:41 +01005018 case HLoadString::LoadKind::kBootImageAddress:
Vladimir Marko847e6ce2017-06-02 13:55:07 +01005019 case HLoadString::LoadKind::kRuntimeCall:
Vladimir Marko6bec91c2017-01-09 15:03:12 +00005020 break;
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005021 }
5022 return desired_string_load_kind;
5023}
5024
Alexandre Rames67555f72014-11-18 10:55:16 +00005025void LocationsBuilderARM64::VisitLoadString(HLoadString* load) {
Nicolas Geoffray132d8362016-11-16 09:19:42 +00005026 LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
Vladimir Markoca6fff82017-10-03 14:49:14 +01005027 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(load, call_kind);
Vladimir Marko847e6ce2017-06-02 13:55:07 +01005028 if (load->GetLoadKind() == HLoadString::LoadKind::kRuntimeCall) {
Christina Wadsworth1fe89ea2016-08-31 16:14:38 -07005029 InvokeRuntimeCallingConvention calling_convention;
5030 locations->SetOut(calling_convention.GetReturnLocation(load->GetType()));
5031 } else {
5032 locations->SetOut(Location::RequiresRegister());
Vladimir Marko94ce9c22016-09-30 14:50:51 +01005033 if (load->GetLoadKind() == HLoadString::LoadKind::kBssEntry) {
5034 if (!kUseReadBarrier || kUseBakerReadBarrier) {
Vladimir Markoea4c1262017-02-06 19:59:33 +00005035 // Rely on the pResolveString and marking to save everything we need.
Vladimir Marko94ce9c22016-09-30 14:50:51 +01005036 RegisterSet caller_saves = RegisterSet::Empty();
5037 InvokeRuntimeCallingConvention calling_convention;
5038 caller_saves.Add(Location::RegisterLocation(calling_convention.GetRegisterAt(0).GetCode()));
5039 DCHECK_EQ(calling_convention.GetRegisterAt(0).GetCode(),
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005040 RegisterFrom(calling_convention.GetReturnLocation(DataType::Type::kReference),
5041 DataType::Type::kReference).GetCode());
Vladimir Marko94ce9c22016-09-30 14:50:51 +01005042 locations->SetCustomSlowPathCallerSaves(caller_saves);
5043 } else {
5044 // For non-Baker read barrier we have a temp-clobbering call.
5045 }
5046 }
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005047 }
Alexandre Rames67555f72014-11-18 10:55:16 +00005048}
5049
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00005050// NO_THREAD_SAFETY_ANALYSIS as we manipulate handles whose internal object we know does not
5051// move.
5052void InstructionCodeGeneratorARM64::VisitLoadString(HLoadString* load) NO_THREAD_SAFETY_ANALYSIS {
Alexandre Rames67555f72014-11-18 10:55:16 +00005053 Register out = OutputRegister(load);
Nicolas Geoffray132d8362016-11-16 09:19:42 +00005054 Location out_loc = load->GetLocations()->Out();
Roland Levillain22ccc3a2015-11-24 13:10:05 +00005055
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005056 switch (load->GetLoadKind()) {
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005057 case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005058 DCHECK(codegen_->GetCompilerOptions().IsBootImage());
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005059 // Add ADRP with its PC-relative String patch.
5060 const DexFile& dex_file = load->GetDexFile();
Vladimir Marko6bec91c2017-01-09 15:03:12 +00005061 const dex::StringIndex string_index = load->GetStringIndex();
Vladimir Marko59eb30f2018-02-20 11:52:34 +00005062 vixl::aarch64::Label* adrp_label = codegen_->NewBootImageStringPatch(dex_file, string_index);
Vladimir Markoaad75c62016-10-03 08:46:48 +00005063 codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005064 // Add ADD with its PC-relative String patch.
Scott Wakeling97c72b72016-06-24 16:19:36 +01005065 vixl::aarch64::Label* add_label =
Vladimir Marko59eb30f2018-02-20 11:52:34 +00005066 codegen_->NewBootImageStringPatch(dex_file, string_index, adrp_label);
Vladimir Markoaad75c62016-10-03 08:46:48 +00005067 codegen_->EmitAddPlaceholder(add_label, out.X(), out.X());
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005068 return;
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005069 }
5070 case HLoadString::LoadKind::kBootImageAddress: {
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00005071 uint32_t address = dchecked_integral_cast<uint32_t>(
5072 reinterpret_cast<uintptr_t>(load->GetString().Get()));
5073 DCHECK_NE(address, 0u);
5074 __ Ldr(out.W(), codegen_->DeduplicateBootImageAddressLiteral(address));
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005075 return;
5076 }
Vladimir Markoe47f60c2018-02-21 13:43:28 +00005077 case HLoadString::LoadKind::kBootImageRelRo: {
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005078 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
Vladimir Markoe47f60c2018-02-21 13:43:28 +00005079 // Add ADRP with its PC-relative .data.bimg.rel.ro patch.
5080 uint32_t boot_image_offset = codegen_->GetBootImageOffset(load);
5081 vixl::aarch64::Label* adrp_label = codegen_->NewBootImageRelRoPatch(boot_image_offset);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005082 codegen_->EmitAdrpPlaceholder(adrp_label, out.X());
Vladimir Markoe47f60c2018-02-21 13:43:28 +00005083 // Add LDR with its PC-relative .data.bimg.rel.ro patch.
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005084 vixl::aarch64::Label* ldr_label =
Vladimir Markoe47f60c2018-02-21 13:43:28 +00005085 codegen_->NewBootImageRelRoPatch(boot_image_offset, adrp_label);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005086 codegen_->EmitLdrOffsetPlaceholder(ldr_label, out.W(), out.X());
5087 return;
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005088 }
Vladimir Markoaad75c62016-10-03 08:46:48 +00005089 case HLoadString::LoadKind::kBssEntry: {
5090 // Add ADRP with its PC-relative String .bss entry patch.
5091 const DexFile& dex_file = load->GetDexFile();
Vladimir Marko6bec91c2017-01-09 15:03:12 +00005092 const dex::StringIndex string_index = load->GetStringIndex();
Vladimir Markoaad75c62016-10-03 08:46:48 +00005093 DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
Vladimir Markof3c52b42017-11-17 17:32:12 +00005094 Register temp = XRegisterFrom(out_loc);
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005095 vixl::aarch64::Label* adrp_label = codegen_->NewStringBssEntryPatch(dex_file, string_index);
Vladimir Marko94ce9c22016-09-30 14:50:51 +01005096 codegen_->EmitAdrpPlaceholder(adrp_label, temp);
Vladimir Markoe47f60c2018-02-21 13:43:28 +00005097 // Add LDR with its PC-relative String .bss entry patch.
Vladimir Markoaad75c62016-10-03 08:46:48 +00005098 vixl::aarch64::Label* ldr_label =
Vladimir Marko6cfbdbc2017-07-25 13:26:39 +01005099 codegen_->NewStringBssEntryPatch(dex_file, string_index, adrp_label);
Nicolas Geoffray132d8362016-11-16 09:19:42 +00005100 // /* GcRoot<mirror::String> */ out = *(base_address + offset) /* PC-relative */
Vladimir Markoaad75c62016-10-03 08:46:48 +00005101 GenerateGcRootFieldLoad(load,
Nicolas Geoffray132d8362016-11-16 09:19:42 +00005102 out_loc,
Vladimir Marko94ce9c22016-09-30 14:50:51 +01005103 temp,
Roland Levillain00468f32016-10-27 18:02:48 +01005104 /* offset placeholder */ 0u,
5105 ldr_label,
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08005106 kCompilerReadBarrierOption);
Vladimir Marko94ce9c22016-09-30 14:50:51 +01005107 SlowPathCodeARM64* slow_path =
Vladimir Markof3c52b42017-11-17 17:32:12 +00005108 new (codegen_->GetScopedAllocator()) LoadStringSlowPathARM64(load);
Vladimir Markoaad75c62016-10-03 08:46:48 +00005109 codegen_->AddSlowPath(slow_path);
5110 __ Cbz(out.X(), slow_path->GetEntryLabel());
5111 __ Bind(slow_path->GetExitLabel());
Roland Levillain2b03a1f2017-06-06 16:09:59 +01005112 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Vladimir Markoaad75c62016-10-03 08:46:48 +00005113 return;
5114 }
Nicolas Geoffray132d8362016-11-16 09:19:42 +00005115 case HLoadString::LoadKind::kJitTableAddress: {
5116 __ Ldr(out, codegen_->DeduplicateJitStringLiteral(load->GetDexFile(),
Nicolas Geoffrayf0acfe72017-01-09 20:54:52 +00005117 load->GetStringIndex(),
5118 load->GetString()));
Nicolas Geoffray132d8362016-11-16 09:19:42 +00005119 GenerateGcRootFieldLoad(load,
5120 out_loc,
5121 out.X(),
5122 /* offset */ 0,
5123 /* fixup_label */ nullptr,
5124 kCompilerReadBarrierOption);
5125 return;
5126 }
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005127 default:
Christina Wadsworthbf44e0e2016-08-18 10:37:42 -07005128 break;
Vladimir Markocac5a7e2016-02-22 10:39:50 +00005129 }
Roland Levillain22ccc3a2015-11-24 13:10:05 +00005130
Christina Wadsworthbf44e0e2016-08-18 10:37:42 -07005131 // TODO: Re-add the compiler code to do string dex cache lookup again.
Christina Wadsworth1fe89ea2016-08-31 16:14:38 -07005132 InvokeRuntimeCallingConvention calling_convention;
Vladimir Marko94ce9c22016-09-30 14:50:51 +01005133 DCHECK_EQ(calling_convention.GetRegisterAt(0).GetCode(), out.GetCode());
Andreas Gampe8a0128a2016-11-28 07:38:35 -08005134 __ Mov(calling_convention.GetRegisterAt(0).W(), load->GetStringIndex().index_);
Christina Wadsworth1fe89ea2016-08-31 16:14:38 -07005135 codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
5136 CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
Roland Levillain2b03a1f2017-06-06 16:09:59 +01005137 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames67555f72014-11-18 10:55:16 +00005138}
5139
Alexandre Rames5319def2014-10-23 10:03:10 +01005140void LocationsBuilderARM64::VisitLongConstant(HLongConstant* constant) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005141 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(constant);
Alexandre Rames5319def2014-10-23 10:03:10 +01005142 locations->SetOut(Location::ConstantLocation(constant));
5143}
5144
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01005145void InstructionCodeGeneratorARM64::VisitLongConstant(HLongConstant* constant ATTRIBUTE_UNUSED) {
Alexandre Rames5319def2014-10-23 10:03:10 +01005146 // Will be generated at use site.
5147}
5148
Alexandre Rames67555f72014-11-18 10:55:16 +00005149void LocationsBuilderARM64::VisitMonitorOperation(HMonitorOperation* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005150 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5151 instruction, LocationSummary::kCallOnMainOnly);
Alexandre Rames67555f72014-11-18 10:55:16 +00005152 InvokeRuntimeCallingConvention calling_convention;
5153 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5154}
5155
5156void InstructionCodeGeneratorARM64::VisitMonitorOperation(HMonitorOperation* instruction) {
Roland Levillain5e8d5f02016-10-18 18:03:43 +01005157 codegen_->InvokeRuntime(instruction->IsEnter() ? kQuickLockObject : kQuickUnlockObject,
Serban Constantinescu22f81d32016-02-18 16:06:31 +00005158 instruction,
5159 instruction->GetDexPc());
Roland Levillain888d0672015-11-23 18:53:50 +00005160 if (instruction->IsEnter()) {
5161 CheckEntrypointTypes<kQuickLockObject, void, mirror::Object*>();
5162 } else {
5163 CheckEntrypointTypes<kQuickUnlockObject, void, mirror::Object*>();
5164 }
Roland Levillain2b03a1f2017-06-06 16:09:59 +01005165 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames67555f72014-11-18 10:55:16 +00005166}
5167
Alexandre Rames42d641b2014-10-27 14:00:51 +00005168void LocationsBuilderARM64::VisitMul(HMul* mul) {
5169 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005170 new (GetGraph()->GetAllocator()) LocationSummary(mul, LocationSummary::kNoCall);
Alexandre Rames42d641b2014-10-27 14:00:51 +00005171 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005172 case DataType::Type::kInt32:
5173 case DataType::Type::kInt64:
Alexandre Rames42d641b2014-10-27 14:00:51 +00005174 locations->SetInAt(0, Location::RequiresRegister());
5175 locations->SetInAt(1, Location::RequiresRegister());
Alexandre Ramesfb4e5fa2014-11-06 12:41:16 +00005176 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Alexandre Rames42d641b2014-10-27 14:00:51 +00005177 break;
5178
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005179 case DataType::Type::kFloat32:
5180 case DataType::Type::kFloat64:
Alexandre Ramesa89086e2014-11-07 17:13:25 +00005181 locations->SetInAt(0, Location::RequiresFpuRegister());
5182 locations->SetInAt(1, Location::RequiresFpuRegister());
Alexandre Rames67555f72014-11-18 10:55:16 +00005183 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
Alexandre Rames42d641b2014-10-27 14:00:51 +00005184 break;
5185
5186 default:
5187 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
5188 }
5189}
5190
5191void InstructionCodeGeneratorARM64::VisitMul(HMul* mul) {
5192 switch (mul->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005193 case DataType::Type::kInt32:
5194 case DataType::Type::kInt64:
Alexandre Rames42d641b2014-10-27 14:00:51 +00005195 __ Mul(OutputRegister(mul), InputRegisterAt(mul, 0), InputRegisterAt(mul, 1));
5196 break;
5197
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005198 case DataType::Type::kFloat32:
5199 case DataType::Type::kFloat64:
Alexandre Ramesa89086e2014-11-07 17:13:25 +00005200 __ Fmul(OutputFPRegister(mul), InputFPRegisterAt(mul, 0), InputFPRegisterAt(mul, 1));
Alexandre Rames42d641b2014-10-27 14:00:51 +00005201 break;
5202
5203 default:
5204 LOG(FATAL) << "Unexpected mul type " << mul->GetResultType();
5205 }
5206}
5207
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005208void LocationsBuilderARM64::VisitNeg(HNeg* neg) {
5209 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005210 new (GetGraph()->GetAllocator()) LocationSummary(neg, LocationSummary::kNoCall);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005211 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005212 case DataType::Type::kInt32:
5213 case DataType::Type::kInt64:
Serban Constantinescu2d35d9d2015-02-22 22:08:01 +00005214 locations->SetInAt(0, ARM64EncodableConstantOrRegister(neg->InputAt(0), neg));
Alexandre Rames67555f72014-11-18 10:55:16 +00005215 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005216 break;
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005217
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005218 case DataType::Type::kFloat32:
5219 case DataType::Type::kFloat64:
Alexandre Rames67555f72014-11-18 10:55:16 +00005220 locations->SetInAt(0, Location::RequiresFpuRegister());
5221 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005222 break;
5223
5224 default:
5225 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
5226 }
5227}
5228
5229void InstructionCodeGeneratorARM64::VisitNeg(HNeg* neg) {
5230 switch (neg->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005231 case DataType::Type::kInt32:
5232 case DataType::Type::kInt64:
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005233 __ Neg(OutputRegister(neg), InputOperandAt(neg, 0));
5234 break;
5235
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005236 case DataType::Type::kFloat32:
5237 case DataType::Type::kFloat64:
Alexandre Rames67555f72014-11-18 10:55:16 +00005238 __ Fneg(OutputFPRegister(neg), InputFPRegisterAt(neg, 0));
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005239 break;
5240
5241 default:
5242 LOG(FATAL) << "Unexpected neg type " << neg->GetResultType();
5243 }
5244}
5245
5246void LocationsBuilderARM64::VisitNewArray(HNewArray* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005247 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5248 instruction, LocationSummary::kCallOnMainOnly);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005249 InvokeRuntimeCallingConvention calling_convention;
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005250 locations->SetOut(LocationFrom(x0));
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00005251 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5252 locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1)));
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005253}
5254
5255void InstructionCodeGeneratorARM64::VisitNewArray(HNewArray* instruction) {
Roland Levillain4d027112015-07-01 15:41:14 +01005256 // Note: if heap poisoning is enabled, the entry point takes cares
5257 // of poisoning the reference.
Nicolas Geoffrayb048cb72017-01-23 22:50:24 +00005258 QuickEntrypointEnum entrypoint =
5259 CodeGenerator::GetArrayAllocationEntrypoint(instruction->GetLoadClass()->GetClass());
5260 codegen_->InvokeRuntime(entrypoint, instruction, instruction->GetDexPc());
Nicolas Geoffraye761bcc2017-01-19 08:59:37 +00005261 CheckEntrypointTypes<kQuickAllocArrayResolved, void*, mirror::Class*, int32_t>();
Roland Levillain2b03a1f2017-06-06 16:09:59 +01005262 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Ramesfc19de82014-11-07 17:13:31 +00005263}
5264
Alexandre Rames5319def2014-10-23 10:03:10 +01005265void LocationsBuilderARM64::VisitNewInstance(HNewInstance* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005266 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5267 instruction, LocationSummary::kCallOnMainOnly);
Alexandre Rames5319def2014-10-23 10:03:10 +01005268 InvokeRuntimeCallingConvention calling_convention;
David Brazdil6de19382016-01-08 17:37:10 +00005269 if (instruction->IsStringAlloc()) {
5270 locations->AddTemp(LocationFrom(kArtMethodRegister));
5271 } else {
5272 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
David Brazdil6de19382016-01-08 17:37:10 +00005273 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005274 locations->SetOut(calling_convention.GetReturnLocation(DataType::Type::kReference));
Alexandre Rames5319def2014-10-23 10:03:10 +01005275}
5276
5277void InstructionCodeGeneratorARM64::VisitNewInstance(HNewInstance* instruction) {
Roland Levillain4d027112015-07-01 15:41:14 +01005278 // Note: if heap poisoning is enabled, the entry point takes cares
5279 // of poisoning the reference.
David Brazdil6de19382016-01-08 17:37:10 +00005280 if (instruction->IsStringAlloc()) {
5281 // String is allocated through StringFactory. Call NewEmptyString entry point.
5282 Location temp = instruction->GetLocations()->GetTemp(0);
Andreas Gampe542451c2016-07-26 09:02:02 -07005283 MemberOffset code_offset = ArtMethod::EntryPointFromQuickCompiledCodeOffset(kArm64PointerSize);
David Brazdil6de19382016-01-08 17:37:10 +00005284 __ Ldr(XRegisterFrom(temp), MemOperand(tr, QUICK_ENTRY_POINT(pNewEmptyString)));
5285 __ Ldr(lr, MemOperand(XRegisterFrom(temp), code_offset.Int32Value()));
Artem Serov914d7a82017-02-07 14:33:49 +00005286
5287 {
5288 // Ensure the pc position is recorded immediately after the `blr` instruction.
5289 ExactAssemblyScope eas(GetVIXLAssembler(),
5290 kInstructionSize,
5291 CodeBufferCheckScope::kExactSize);
5292 __ blr(lr);
5293 codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
5294 }
David Brazdil6de19382016-01-08 17:37:10 +00005295 } else {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00005296 codegen_->InvokeRuntime(instruction->GetEntrypoint(), instruction, instruction->GetDexPc());
Nicolas Geoffray0d3998b2017-01-12 15:35:12 +00005297 CheckEntrypointTypes<kQuickAllocObjectWithChecks, void*, mirror::Class*>();
David Brazdil6de19382016-01-08 17:37:10 +00005298 }
Roland Levillain2b03a1f2017-06-06 16:09:59 +01005299 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames5319def2014-10-23 10:03:10 +01005300}
5301
5302void LocationsBuilderARM64::VisitNot(HNot* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005303 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Alexandre Rames4e596512014-11-07 15:56:50 +00005304 locations->SetInAt(0, Location::RequiresRegister());
Alexandre Ramesfb4e5fa2014-11-06 12:41:16 +00005305 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
Alexandre Rames5319def2014-10-23 10:03:10 +01005306}
5307
5308void InstructionCodeGeneratorARM64::VisitNot(HNot* instruction) {
Nicolas Geoffrayd8ef2e92015-02-24 16:02:06 +00005309 switch (instruction->GetResultType()) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005310 case DataType::Type::kInt32:
5311 case DataType::Type::kInt64:
Roland Levillain55dcfb52014-10-24 18:09:09 +01005312 __ Mvn(OutputRegister(instruction), InputOperandAt(instruction, 0));
Alexandre Rames5319def2014-10-23 10:03:10 +01005313 break;
5314
5315 default:
5316 LOG(FATAL) << "Unexpected type for not operation " << instruction->GetResultType();
5317 }
5318}
5319
David Brazdil66d126e2015-04-03 16:02:44 +01005320void LocationsBuilderARM64::VisitBooleanNot(HBooleanNot* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005321 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
David Brazdil66d126e2015-04-03 16:02:44 +01005322 locations->SetInAt(0, Location::RequiresRegister());
5323 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5324}
5325
5326void InstructionCodeGeneratorARM64::VisitBooleanNot(HBooleanNot* instruction) {
Scott Wakeling97c72b72016-06-24 16:19:36 +01005327 __ Eor(OutputRegister(instruction), InputRegisterAt(instruction, 0), vixl::aarch64::Operand(1));
David Brazdil66d126e2015-04-03 16:02:44 +01005328}
5329
Alexandre Rames5319def2014-10-23 10:03:10 +01005330void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) {
Vladimir Marko804b03f2016-09-14 16:26:36 +01005331 LocationSummary* locations = codegen_->CreateThrowingSlowPathLocations(instruction);
5332 locations->SetInAt(0, Location::RequiresRegister());
Alexandre Rames5319def2014-10-23 10:03:10 +01005333}
5334
Calin Juravle2ae48182016-03-16 14:05:09 +00005335void CodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
5336 if (CanMoveNullCheckToUser(instruction)) {
Calin Juravle77520bc2015-01-12 18:45:46 +00005337 return;
5338 }
Artem Serov914d7a82017-02-07 14:33:49 +00005339 {
5340 // Ensure that between load and MaybeRecordImplicitNullCheck there are no pools emitted.
5341 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
5342 Location obj = instruction->GetLocations()->InAt(0);
5343 __ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
5344 RecordPcInfo(instruction, instruction->GetDexPc());
5345 }
Calin Juravlecd6dffe2015-01-08 17:35:35 +00005346}
5347
Calin Juravle2ae48182016-03-16 14:05:09 +00005348void CodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) {
Vladimir Marko174b2e22017-10-12 13:34:49 +01005349 SlowPathCodeARM64* slow_path = new (GetScopedAllocator()) NullCheckSlowPathARM64(instruction);
Calin Juravle2ae48182016-03-16 14:05:09 +00005350 AddSlowPath(slow_path);
Alexandre Rames5319def2014-10-23 10:03:10 +01005351
5352 LocationSummary* locations = instruction->GetLocations();
5353 Location obj = locations->InAt(0);
Calin Juravle77520bc2015-01-12 18:45:46 +00005354
5355 __ Cbz(RegisterFrom(obj, instruction->InputAt(0)->GetType()), slow_path->GetEntryLabel());
Alexandre Rames5319def2014-10-23 10:03:10 +01005356}
5357
Calin Juravlecd6dffe2015-01-08 17:35:35 +00005358void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
Calin Juravle2ae48182016-03-16 14:05:09 +00005359 codegen_->GenerateNullCheck(instruction);
Calin Juravlecd6dffe2015-01-08 17:35:35 +00005360}
5361
Alexandre Rames67555f72014-11-18 10:55:16 +00005362void LocationsBuilderARM64::VisitOr(HOr* instruction) {
5363 HandleBinaryOp(instruction);
5364}
5365
5366void InstructionCodeGeneratorARM64::VisitOr(HOr* instruction) {
5367 HandleBinaryOp(instruction);
5368}
5369
Alexandre Rames3e69f162014-12-10 10:36:50 +00005370void LocationsBuilderARM64::VisitParallelMove(HParallelMove* instruction ATTRIBUTE_UNUSED) {
5371 LOG(FATAL) << "Unreachable";
5372}
5373
5374void InstructionCodeGeneratorARM64::VisitParallelMove(HParallelMove* instruction) {
Vladimir Markobea75ff2017-10-11 20:39:54 +01005375 if (instruction->GetNext()->IsSuspendCheck() &&
5376 instruction->GetBlock()->GetLoopInformation() != nullptr) {
5377 HSuspendCheck* suspend_check = instruction->GetNext()->AsSuspendCheck();
5378 // The back edge will generate the suspend check.
5379 codegen_->ClearSpillSlotsFromLoopPhisInStackMap(suspend_check, instruction);
5380 }
5381
Alexandre Rames3e69f162014-12-10 10:36:50 +00005382 codegen_->GetMoveResolver()->EmitNativeCode(instruction);
5383}
5384
Alexandre Rames5319def2014-10-23 10:03:10 +01005385void LocationsBuilderARM64::VisitParameterValue(HParameterValue* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005386 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Alexandre Rames5319def2014-10-23 10:03:10 +01005387 Location location = parameter_visitor_.GetNextLocation(instruction->GetType());
5388 if (location.IsStackSlot()) {
5389 location = Location::StackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5390 } else if (location.IsDoubleStackSlot()) {
5391 location = Location::DoubleStackSlot(location.GetStackIndex() + codegen_->GetFrameSize());
5392 }
5393 locations->SetOut(location);
5394}
5395
Nicolas Geoffray76b1e172015-05-27 17:18:33 +01005396void InstructionCodeGeneratorARM64::VisitParameterValue(
5397 HParameterValue* instruction ATTRIBUTE_UNUSED) {
Alexandre Rames5319def2014-10-23 10:03:10 +01005398 // Nothing to do, the parameter is already at its location.
Nicolas Geoffray76b1e172015-05-27 17:18:33 +01005399}
5400
5401void LocationsBuilderARM64::VisitCurrentMethod(HCurrentMethod* instruction) {
5402 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005403 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Nicolas Geoffray38207af2015-06-01 15:46:22 +01005404 locations->SetOut(LocationFrom(kArtMethodRegister));
Nicolas Geoffray76b1e172015-05-27 17:18:33 +01005405}
5406
5407void InstructionCodeGeneratorARM64::VisitCurrentMethod(
5408 HCurrentMethod* instruction ATTRIBUTE_UNUSED) {
5409 // Nothing to do, the method is already at its location.
Alexandre Rames5319def2014-10-23 10:03:10 +01005410}
5411
5412void LocationsBuilderARM64::VisitPhi(HPhi* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005413 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Vladimir Marko372f10e2016-05-17 16:30:10 +01005414 for (size_t i = 0, e = locations->GetInputCount(); i < e; ++i) {
Alexandre Rames5319def2014-10-23 10:03:10 +01005415 locations->SetInAt(i, Location::Any());
5416 }
5417 locations->SetOut(Location::Any());
5418}
5419
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01005420void InstructionCodeGeneratorARM64::VisitPhi(HPhi* instruction ATTRIBUTE_UNUSED) {
Alexandre Rames5319def2014-10-23 10:03:10 +01005421 LOG(FATAL) << "Unreachable";
5422}
5423
Serban Constantinescu02164b32014-11-13 14:05:07 +00005424void LocationsBuilderARM64::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005425 DataType::Type type = rem->GetResultType();
Alexandre Rames542361f2015-01-29 16:57:31 +00005426 LocationSummary::CallKind call_kind =
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005427 DataType::IsFloatingPointType(type) ? LocationSummary::kCallOnMainOnly
Serban Constantinescu54ff4822016-07-07 18:03:19 +01005428 : LocationSummary::kNoCall;
Vladimir Markoca6fff82017-10-03 14:49:14 +01005429 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(rem, call_kind);
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00005430
5431 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005432 case DataType::Type::kInt32:
5433 case DataType::Type::kInt64:
Serban Constantinescu02164b32014-11-13 14:05:07 +00005434 locations->SetInAt(0, Location::RequiresRegister());
Zheng Xuc6667102015-05-15 16:08:45 +08005435 locations->SetInAt(1, Location::RegisterOrConstant(rem->InputAt(1)));
Serban Constantinescu02164b32014-11-13 14:05:07 +00005436 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5437 break;
5438
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005439 case DataType::Type::kFloat32:
5440 case DataType::Type::kFloat64: {
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00005441 InvokeRuntimeCallingConvention calling_convention;
5442 locations->SetInAt(0, LocationFrom(calling_convention.GetFpuRegisterAt(0)));
5443 locations->SetInAt(1, LocationFrom(calling_convention.GetFpuRegisterAt(1)));
5444 locations->SetOut(calling_convention.GetReturnLocation(type));
5445
5446 break;
5447 }
5448
Serban Constantinescu02164b32014-11-13 14:05:07 +00005449 default:
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00005450 LOG(FATAL) << "Unexpected rem type " << type;
Serban Constantinescu02164b32014-11-13 14:05:07 +00005451 }
5452}
5453
5454void InstructionCodeGeneratorARM64::VisitRem(HRem* rem) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005455 DataType::Type type = rem->GetResultType();
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00005456
Serban Constantinescu02164b32014-11-13 14:05:07 +00005457 switch (type) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005458 case DataType::Type::kInt32:
5459 case DataType::Type::kInt64: {
Zheng Xuc6667102015-05-15 16:08:45 +08005460 GenerateDivRemIntegral(rem);
Serban Constantinescu02164b32014-11-13 14:05:07 +00005461 break;
5462 }
5463
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005464 case DataType::Type::kFloat32:
5465 case DataType::Type::kFloat64: {
5466 QuickEntrypointEnum entrypoint =
5467 (type == DataType::Type::kFloat32) ? kQuickFmodf : kQuickFmod;
Serban Constantinescu22f81d32016-02-18 16:06:31 +00005468 codegen_->InvokeRuntime(entrypoint, rem, rem->GetDexPc());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005469 if (type == DataType::Type::kFloat32) {
Roland Levillain888d0672015-11-23 18:53:50 +00005470 CheckEntrypointTypes<kQuickFmodf, float, float, float>();
5471 } else {
5472 CheckEntrypointTypes<kQuickFmod, double, double, double>();
5473 }
Serban Constantinescu02d81cc2015-01-05 16:08:49 +00005474 break;
5475 }
5476
Serban Constantinescu02164b32014-11-13 14:05:07 +00005477 default:
5478 LOG(FATAL) << "Unexpected rem type " << type;
Vladimir Marko351dddf2015-12-11 16:34:46 +00005479 UNREACHABLE();
Serban Constantinescu02164b32014-11-13 14:05:07 +00005480 }
5481}
5482
Aart Bik1f8d51b2018-02-15 10:42:37 -08005483// TODO: integrate with HandleBinaryOp?
5484static void CreateMinMaxLocations(ArenaAllocator* allocator, HBinaryOperation* minmax) {
5485 LocationSummary* locations = new (allocator) LocationSummary(minmax);
5486 switch (minmax->GetResultType()) {
5487 case DataType::Type::kInt32:
5488 case DataType::Type::kInt64:
5489 locations->SetInAt(0, Location::RequiresRegister());
5490 locations->SetInAt(1, Location::RequiresRegister());
5491 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5492 break;
5493 case DataType::Type::kFloat32:
5494 case DataType::Type::kFloat64:
5495 locations->SetInAt(0, Location::RequiresFpuRegister());
5496 locations->SetInAt(1, Location::RequiresFpuRegister());
5497 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5498 break;
5499 default:
5500 LOG(FATAL) << "Unexpected type for HMinMax " << minmax->GetResultType();
5501 }
5502}
5503
Aart Bik351df3e2018-03-07 11:54:57 -08005504void InstructionCodeGeneratorARM64::GenerateMinMaxInt(LocationSummary* locations,
5505 bool is_min,
5506 DataType::Type type) {
Aart Bik1f8d51b2018-02-15 10:42:37 -08005507 Location op1 = locations->InAt(0);
5508 Location op2 = locations->InAt(1);
5509 Location out = locations->Out();
5510
5511 Register op1_reg;
5512 Register op2_reg;
5513 Register out_reg;
5514 if (type == DataType::Type::kInt64) {
5515 op1_reg = XRegisterFrom(op1);
5516 op2_reg = XRegisterFrom(op2);
5517 out_reg = XRegisterFrom(out);
5518 } else {
5519 DCHECK_EQ(type, DataType::Type::kInt32);
5520 op1_reg = WRegisterFrom(op1);
5521 op2_reg = WRegisterFrom(op2);
5522 out_reg = WRegisterFrom(out);
5523 }
5524
5525 __ Cmp(op1_reg, op2_reg);
5526 __ Csel(out_reg, op1_reg, op2_reg, is_min ? lt : gt);
5527}
5528
5529void InstructionCodeGeneratorARM64::GenerateMinMaxFP(LocationSummary* locations,
5530 bool is_min,
5531 DataType::Type type) {
5532 Location op1 = locations->InAt(0);
5533 Location op2 = locations->InAt(1);
5534 Location out = locations->Out();
5535
5536 FPRegister op1_reg;
5537 FPRegister op2_reg;
5538 FPRegister out_reg;
5539 if (type == DataType::Type::kFloat64) {
5540 op1_reg = DRegisterFrom(op1);
5541 op2_reg = DRegisterFrom(op2);
5542 out_reg = DRegisterFrom(out);
5543 } else {
5544 DCHECK_EQ(type, DataType::Type::kFloat32);
5545 op1_reg = SRegisterFrom(op1);
5546 op2_reg = SRegisterFrom(op2);
5547 out_reg = SRegisterFrom(out);
5548 }
5549
5550 if (is_min) {
5551 __ Fmin(out_reg, op1_reg, op2_reg);
5552 } else {
5553 __ Fmax(out_reg, op1_reg, op2_reg);
5554 }
5555}
5556
Aart Bik351df3e2018-03-07 11:54:57 -08005557// TODO: integrate with HandleBinaryOp?
5558void InstructionCodeGeneratorARM64::GenerateMinMax(HBinaryOperation* minmax, bool is_min) {
5559 DataType::Type type = minmax->GetResultType();
5560 switch (type) {
5561 case DataType::Type::kInt32:
5562 case DataType::Type::kInt64:
5563 GenerateMinMaxInt(minmax->GetLocations(), is_min, type);
5564 break;
5565 case DataType::Type::kFloat32:
5566 case DataType::Type::kFloat64:
5567 GenerateMinMaxFP(minmax->GetLocations(), is_min, type);
5568 break;
5569 default:
5570 LOG(FATAL) << "Unexpected type for HMinMax " << type;
5571 }
5572}
5573
Aart Bik1f8d51b2018-02-15 10:42:37 -08005574void LocationsBuilderARM64::VisitMin(HMin* min) {
5575 CreateMinMaxLocations(GetGraph()->GetAllocator(), min);
5576}
5577
Aart Bik1f8d51b2018-02-15 10:42:37 -08005578void InstructionCodeGeneratorARM64::VisitMin(HMin* min) {
Aart Bik351df3e2018-03-07 11:54:57 -08005579 GenerateMinMax(min, /*is_min*/ true);
Aart Bik1f8d51b2018-02-15 10:42:37 -08005580}
5581
5582void LocationsBuilderARM64::VisitMax(HMax* max) {
5583 CreateMinMaxLocations(GetGraph()->GetAllocator(), max);
5584}
5585
5586void InstructionCodeGeneratorARM64::VisitMax(HMax* max) {
Aart Bik351df3e2018-03-07 11:54:57 -08005587 GenerateMinMax(max, /*is_min*/ false);
Aart Bik1f8d51b2018-02-15 10:42:37 -08005588}
5589
Aart Bik3dad3412018-02-28 12:01:46 -08005590void LocationsBuilderARM64::VisitAbs(HAbs* abs) {
5591 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(abs);
5592 switch (abs->GetResultType()) {
5593 case DataType::Type::kInt32:
5594 case DataType::Type::kInt64:
5595 locations->SetInAt(0, Location::RequiresRegister());
5596 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5597 break;
5598 case DataType::Type::kFloat32:
5599 case DataType::Type::kFloat64:
5600 locations->SetInAt(0, Location::RequiresFpuRegister());
5601 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5602 break;
5603 default:
5604 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
5605 }
5606}
5607
5608void InstructionCodeGeneratorARM64::VisitAbs(HAbs* abs) {
5609 switch (abs->GetResultType()) {
5610 case DataType::Type::kInt32:
5611 case DataType::Type::kInt64: {
5612 Register in_reg = InputRegisterAt(abs, 0);
5613 Register out_reg = OutputRegister(abs);
5614 __ Cmp(in_reg, Operand(0));
5615 __ Cneg(out_reg, in_reg, lt);
5616 break;
5617 }
5618 case DataType::Type::kFloat32:
5619 case DataType::Type::kFloat64: {
5620 FPRegister in_reg = InputFPRegisterAt(abs, 0);
5621 FPRegister out_reg = OutputFPRegister(abs);
5622 __ Fabs(out_reg, in_reg);
5623 break;
5624 }
5625 default:
5626 LOG(FATAL) << "Unexpected type for abs operation " << abs->GetResultType();
5627 }
5628}
5629
Igor Murashkind01745e2017-04-05 16:40:31 -07005630void LocationsBuilderARM64::VisitConstructorFence(HConstructorFence* constructor_fence) {
5631 constructor_fence->SetLocations(nullptr);
5632}
5633
5634void InstructionCodeGeneratorARM64::VisitConstructorFence(
5635 HConstructorFence* constructor_fence ATTRIBUTE_UNUSED) {
5636 codegen_->GenerateMemoryBarrier(MemBarrierKind::kStoreStore);
5637}
5638
Calin Juravle27df7582015-04-17 19:12:31 +01005639void LocationsBuilderARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
5640 memory_barrier->SetLocations(nullptr);
5641}
5642
5643void InstructionCodeGeneratorARM64::VisitMemoryBarrier(HMemoryBarrier* memory_barrier) {
Roland Levillain44015862016-01-22 11:47:17 +00005644 codegen_->GenerateMemoryBarrier(memory_barrier->GetBarrierKind());
Calin Juravle27df7582015-04-17 19:12:31 +01005645}
5646
Alexandre Rames5319def2014-10-23 10:03:10 +01005647void LocationsBuilderARM64::VisitReturn(HReturn* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005648 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005649 DataType::Type return_type = instruction->InputAt(0)->GetType();
Alexandre Ramesa89086e2014-11-07 17:13:25 +00005650 locations->SetInAt(0, ARM64ReturnLocation(return_type));
Alexandre Rames5319def2014-10-23 10:03:10 +01005651}
5652
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01005653void InstructionCodeGeneratorARM64::VisitReturn(HReturn* instruction ATTRIBUTE_UNUSED) {
Alexandre Rames5319def2014-10-23 10:03:10 +01005654 codegen_->GenerateFrameExit();
Alexandre Rames5319def2014-10-23 10:03:10 +01005655}
5656
5657void LocationsBuilderARM64::VisitReturnVoid(HReturnVoid* instruction) {
5658 instruction->SetLocations(nullptr);
5659}
5660
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01005661void InstructionCodeGeneratorARM64::VisitReturnVoid(HReturnVoid* instruction ATTRIBUTE_UNUSED) {
Alexandre Rames5319def2014-10-23 10:03:10 +01005662 codegen_->GenerateFrameExit();
Alexandre Rames5319def2014-10-23 10:03:10 +01005663}
5664
Scott Wakeling40a04bf2015-12-11 09:50:36 +00005665void LocationsBuilderARM64::VisitRor(HRor* ror) {
5666 HandleBinaryOp(ror);
5667}
5668
5669void InstructionCodeGeneratorARM64::VisitRor(HRor* ror) {
5670 HandleBinaryOp(ror);
5671}
5672
Serban Constantinescu02164b32014-11-13 14:05:07 +00005673void LocationsBuilderARM64::VisitShl(HShl* shl) {
5674 HandleShift(shl);
5675}
5676
5677void InstructionCodeGeneratorARM64::VisitShl(HShl* shl) {
5678 HandleShift(shl);
5679}
5680
5681void LocationsBuilderARM64::VisitShr(HShr* shr) {
5682 HandleShift(shr);
5683}
5684
5685void InstructionCodeGeneratorARM64::VisitShr(HShr* shr) {
5686 HandleShift(shr);
5687}
5688
Alexandre Rames5319def2014-10-23 10:03:10 +01005689void LocationsBuilderARM64::VisitSub(HSub* instruction) {
Alexandre Rames67555f72014-11-18 10:55:16 +00005690 HandleBinaryOp(instruction);
Alexandre Rames5319def2014-10-23 10:03:10 +01005691}
5692
5693void InstructionCodeGeneratorARM64::VisitSub(HSub* instruction) {
Alexandre Rames67555f72014-11-18 10:55:16 +00005694 HandleBinaryOp(instruction);
Alexandre Rames5319def2014-10-23 10:03:10 +01005695}
5696
Alexandre Rames67555f72014-11-18 10:55:16 +00005697void LocationsBuilderARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
Vladimir Markof4f2daa2017-03-20 18:26:59 +00005698 HandleFieldGet(instruction, instruction->GetFieldInfo());
Alexandre Rames67555f72014-11-18 10:55:16 +00005699}
5700
5701void InstructionCodeGeneratorARM64::VisitStaticFieldGet(HStaticFieldGet* instruction) {
Alexandre Rames09a99962015-04-15 11:47:56 +01005702 HandleFieldGet(instruction, instruction->GetFieldInfo());
Alexandre Rames67555f72014-11-18 10:55:16 +00005703}
5704
5705void LocationsBuilderARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
Alexandre Rames09a99962015-04-15 11:47:56 +01005706 HandleFieldSet(instruction);
Alexandre Rames5319def2014-10-23 10:03:10 +01005707}
5708
Alexandre Rames67555f72014-11-18 10:55:16 +00005709void InstructionCodeGeneratorARM64::VisitStaticFieldSet(HStaticFieldSet* instruction) {
Nicolas Geoffray07276db2015-05-18 14:22:09 +01005710 HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
Alexandre Rames5319def2014-10-23 10:03:10 +01005711}
5712
Calin Juravlee460d1d2015-09-29 04:52:17 +01005713void LocationsBuilderARM64::VisitUnresolvedInstanceFieldGet(
5714 HUnresolvedInstanceFieldGet* instruction) {
5715 FieldAccessCallingConventionARM64 calling_convention;
5716 codegen_->CreateUnresolvedFieldLocationSummary(
5717 instruction, instruction->GetFieldType(), calling_convention);
5718}
5719
5720void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldGet(
5721 HUnresolvedInstanceFieldGet* instruction) {
5722 FieldAccessCallingConventionARM64 calling_convention;
5723 codegen_->GenerateUnresolvedFieldAccess(instruction,
5724 instruction->GetFieldType(),
5725 instruction->GetFieldIndex(),
5726 instruction->GetDexPc(),
5727 calling_convention);
5728}
5729
5730void LocationsBuilderARM64::VisitUnresolvedInstanceFieldSet(
5731 HUnresolvedInstanceFieldSet* instruction) {
5732 FieldAccessCallingConventionARM64 calling_convention;
5733 codegen_->CreateUnresolvedFieldLocationSummary(
5734 instruction, instruction->GetFieldType(), calling_convention);
5735}
5736
5737void InstructionCodeGeneratorARM64::VisitUnresolvedInstanceFieldSet(
5738 HUnresolvedInstanceFieldSet* instruction) {
5739 FieldAccessCallingConventionARM64 calling_convention;
5740 codegen_->GenerateUnresolvedFieldAccess(instruction,
5741 instruction->GetFieldType(),
5742 instruction->GetFieldIndex(),
5743 instruction->GetDexPc(),
5744 calling_convention);
5745}
5746
5747void LocationsBuilderARM64::VisitUnresolvedStaticFieldGet(
5748 HUnresolvedStaticFieldGet* instruction) {
5749 FieldAccessCallingConventionARM64 calling_convention;
5750 codegen_->CreateUnresolvedFieldLocationSummary(
5751 instruction, instruction->GetFieldType(), calling_convention);
5752}
5753
5754void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldGet(
5755 HUnresolvedStaticFieldGet* instruction) {
5756 FieldAccessCallingConventionARM64 calling_convention;
5757 codegen_->GenerateUnresolvedFieldAccess(instruction,
5758 instruction->GetFieldType(),
5759 instruction->GetFieldIndex(),
5760 instruction->GetDexPc(),
5761 calling_convention);
5762}
5763
5764void LocationsBuilderARM64::VisitUnresolvedStaticFieldSet(
5765 HUnresolvedStaticFieldSet* instruction) {
5766 FieldAccessCallingConventionARM64 calling_convention;
5767 codegen_->CreateUnresolvedFieldLocationSummary(
5768 instruction, instruction->GetFieldType(), calling_convention);
5769}
5770
5771void InstructionCodeGeneratorARM64::VisitUnresolvedStaticFieldSet(
5772 HUnresolvedStaticFieldSet* instruction) {
5773 FieldAccessCallingConventionARM64 calling_convention;
5774 codegen_->GenerateUnresolvedFieldAccess(instruction,
5775 instruction->GetFieldType(),
5776 instruction->GetFieldIndex(),
5777 instruction->GetDexPc(),
5778 calling_convention);
5779}
5780
Alexandre Rames5319def2014-10-23 10:03:10 +01005781void LocationsBuilderARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005782 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5783 instruction, LocationSummary::kCallOnSlowPath);
Artem Serov7957d952017-04-04 15:44:09 +01005784 // In suspend check slow path, usually there are no caller-save registers at all.
5785 // If SIMD instructions are present, however, we force spilling all live SIMD
5786 // registers in full width (since the runtime only saves/restores lower part).
5787 locations->SetCustomSlowPathCallerSaves(
5788 GetGraph()->HasSIMD() ? RegisterSet::AllFpu() : RegisterSet::Empty());
Alexandre Rames5319def2014-10-23 10:03:10 +01005789}
5790
5791void InstructionCodeGeneratorARM64::VisitSuspendCheck(HSuspendCheck* instruction) {
Serban Constantinescu02164b32014-11-13 14:05:07 +00005792 HBasicBlock* block = instruction->GetBlock();
5793 if (block->GetLoopInformation() != nullptr) {
5794 DCHECK(block->GetLoopInformation()->GetSuspendCheck() == instruction);
5795 // The back edge will generate the suspend check.
5796 return;
5797 }
5798 if (block->IsEntryBlock() && instruction->GetNext()->IsGoto()) {
5799 // The goto will generate the suspend check.
5800 return;
5801 }
5802 GenerateSuspendCheck(instruction, nullptr);
Roland Levillain2b03a1f2017-06-06 16:09:59 +01005803 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Alexandre Rames5319def2014-10-23 10:03:10 +01005804}
5805
Alexandre Rames67555f72014-11-18 10:55:16 +00005806void LocationsBuilderARM64::VisitThrow(HThrow* instruction) {
Vladimir Markoca6fff82017-10-03 14:49:14 +01005807 LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(
5808 instruction, LocationSummary::kCallOnMainOnly);
Alexandre Rames67555f72014-11-18 10:55:16 +00005809 InvokeRuntimeCallingConvention calling_convention;
5810 locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0)));
5811}
5812
5813void InstructionCodeGeneratorARM64::VisitThrow(HThrow* instruction) {
Serban Constantinescu22f81d32016-02-18 16:06:31 +00005814 codegen_->InvokeRuntime(kQuickDeliverException, instruction, instruction->GetDexPc());
Andreas Gampe1cc7dba2014-12-17 18:43:01 -08005815 CheckEntrypointTypes<kQuickDeliverException, void, mirror::Object*>();
Alexandre Rames67555f72014-11-18 10:55:16 +00005816}
5817
5818void LocationsBuilderARM64::VisitTypeConversion(HTypeConversion* conversion) {
5819 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005820 new (GetGraph()->GetAllocator()) LocationSummary(conversion, LocationSummary::kNoCall);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005821 DataType::Type input_type = conversion->GetInputType();
5822 DataType::Type result_type = conversion->GetResultType();
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005823 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
5824 << input_type << " -> " << result_type;
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005825 if ((input_type == DataType::Type::kReference) || (input_type == DataType::Type::kVoid) ||
5826 (result_type == DataType::Type::kReference) || (result_type == DataType::Type::kVoid)) {
Alexandre Rames67555f72014-11-18 10:55:16 +00005827 LOG(FATAL) << "Unexpected type conversion from " << input_type << " to " << result_type;
5828 }
5829
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005830 if (DataType::IsFloatingPointType(input_type)) {
Alexandre Rames67555f72014-11-18 10:55:16 +00005831 locations->SetInAt(0, Location::RequiresFpuRegister());
5832 } else {
5833 locations->SetInAt(0, Location::RequiresRegister());
5834 }
5835
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005836 if (DataType::IsFloatingPointType(result_type)) {
Alexandre Rames67555f72014-11-18 10:55:16 +00005837 locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap);
5838 } else {
5839 locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
5840 }
5841}
5842
5843void InstructionCodeGeneratorARM64::VisitTypeConversion(HTypeConversion* conversion) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005844 DataType::Type result_type = conversion->GetResultType();
5845 DataType::Type input_type = conversion->GetInputType();
Alexandre Rames67555f72014-11-18 10:55:16 +00005846
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005847 DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type))
5848 << input_type << " -> " << result_type;
Alexandre Rames67555f72014-11-18 10:55:16 +00005849
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005850 if (DataType::IsIntegralType(result_type) && DataType::IsIntegralType(input_type)) {
5851 int result_size = DataType::Size(result_type);
5852 int input_size = DataType::Size(input_type);
Alexandre Rames3e69f162014-12-10 10:36:50 +00005853 int min_size = std::min(result_size, input_size);
Serban Constantinescu02164b32014-11-13 14:05:07 +00005854 Register output = OutputRegister(conversion);
5855 Register source = InputRegisterAt(conversion, 0);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005856 if (result_type == DataType::Type::kInt32 && input_type == DataType::Type::kInt64) {
Alexandre Rames4dff2fd2015-08-20 13:36:35 +01005857 // 'int' values are used directly as W registers, discarding the top
5858 // bits, so we don't need to sign-extend and can just perform a move.
5859 // We do not pass the `kDiscardForSameWReg` argument to force clearing the
5860 // top 32 bits of the target register. We theoretically could leave those
5861 // bits unchanged, but we would have to make sure that no code uses a
5862 // 32bit input value as a 64bit value assuming that the top 32 bits are
5863 // zero.
5864 __ Mov(output.W(), source.W());
Vladimir Markod5d2f2c2017-09-26 12:37:26 +01005865 } else if (DataType::IsUnsignedType(result_type) ||
5866 (DataType::IsUnsignedType(input_type) && input_size < result_size)) {
5867 __ Ubfx(output, output.IsX() ? source.X() : source.W(), 0, result_size * kBitsPerByte);
Alexandre Rames67555f72014-11-18 10:55:16 +00005868 } else {
Alexandre Rames3e69f162014-12-10 10:36:50 +00005869 __ Sbfx(output, output.IsX() ? source.X() : source.W(), 0, min_size * kBitsPerByte);
Alexandre Rames67555f72014-11-18 10:55:16 +00005870 }
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005871 } else if (DataType::IsFloatingPointType(result_type) && DataType::IsIntegralType(input_type)) {
Serban Constantinescu02164b32014-11-13 14:05:07 +00005872 __ Scvtf(OutputFPRegister(conversion), InputRegisterAt(conversion, 0));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005873 } else if (DataType::IsIntegralType(result_type) && DataType::IsFloatingPointType(input_type)) {
5874 CHECK(result_type == DataType::Type::kInt32 || result_type == DataType::Type::kInt64);
Serban Constantinescu02164b32014-11-13 14:05:07 +00005875 __ Fcvtzs(OutputRegister(conversion), InputFPRegisterAt(conversion, 0));
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01005876 } else if (DataType::IsFloatingPointType(result_type) &&
5877 DataType::IsFloatingPointType(input_type)) {
Serban Constantinescu02164b32014-11-13 14:05:07 +00005878 __ Fcvt(OutputFPRegister(conversion), InputFPRegisterAt(conversion, 0));
5879 } else {
5880 LOG(FATAL) << "Unexpected or unimplemented type conversion from " << input_type
5881 << " to " << result_type;
Alexandre Rames67555f72014-11-18 10:55:16 +00005882 }
Serban Constantinescu02164b32014-11-13 14:05:07 +00005883}
Alexandre Rames67555f72014-11-18 10:55:16 +00005884
Serban Constantinescu02164b32014-11-13 14:05:07 +00005885void LocationsBuilderARM64::VisitUShr(HUShr* ushr) {
5886 HandleShift(ushr);
5887}
5888
5889void InstructionCodeGeneratorARM64::VisitUShr(HUShr* ushr) {
5890 HandleShift(ushr);
Alexandre Rames67555f72014-11-18 10:55:16 +00005891}
5892
5893void LocationsBuilderARM64::VisitXor(HXor* instruction) {
5894 HandleBinaryOp(instruction);
5895}
5896
5897void InstructionCodeGeneratorARM64::VisitXor(HXor* instruction) {
5898 HandleBinaryOp(instruction);
5899}
5900
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01005901void LocationsBuilderARM64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
Calin Juravleb1498f62015-02-16 13:13:29 +00005902 // Nothing to do, this should be removed during prepare for register allocator.
Calin Juravleb1498f62015-02-16 13:13:29 +00005903 LOG(FATAL) << "Unreachable";
5904}
5905
Roland Levillain4b8f1ec2015-08-26 18:34:03 +01005906void InstructionCodeGeneratorARM64::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
Calin Juravleb1498f62015-02-16 13:13:29 +00005907 // Nothing to do, this should be removed during prepare for register allocator.
Calin Juravleb1498f62015-02-16 13:13:29 +00005908 LOG(FATAL) << "Unreachable";
5909}
5910
Mark Mendellfe57faa2015-09-18 09:26:15 -04005911// Simple implementation of packed switch - generate cascaded compare/jumps.
5912void LocationsBuilderARM64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
5913 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01005914 new (GetGraph()->GetAllocator()) LocationSummary(switch_instr, LocationSummary::kNoCall);
Mark Mendellfe57faa2015-09-18 09:26:15 -04005915 locations->SetInAt(0, Location::RequiresRegister());
5916}
5917
5918void InstructionCodeGeneratorARM64::VisitPackedSwitch(HPackedSwitch* switch_instr) {
5919 int32_t lower_bound = switch_instr->GetStartValue();
Zheng Xu3927c8b2015-11-18 17:46:25 +08005920 uint32_t num_entries = switch_instr->GetNumEntries();
Mark Mendellfe57faa2015-09-18 09:26:15 -04005921 Register value_reg = InputRegisterAt(switch_instr, 0);
5922 HBasicBlock* default_block = switch_instr->GetDefaultBlock();
5923
Zheng Xu3927c8b2015-11-18 17:46:25 +08005924 // Roughly set 16 as max average assemblies generated per HIR in a graph.
Scott Wakeling97c72b72016-06-24 16:19:36 +01005925 static constexpr int32_t kMaxExpectedSizePerHInstruction = 16 * kInstructionSize;
Zheng Xu3927c8b2015-11-18 17:46:25 +08005926 // ADR has a limited range(+/-1MB), so we set a threshold for the number of HIRs in the graph to
5927 // make sure we don't emit it if the target may run out of range.
5928 // TODO: Instead of emitting all jump tables at the end of the code, we could keep track of ADR
5929 // ranges and emit the tables only as required.
5930 static constexpr int32_t kJumpTableInstructionThreshold = 1* MB / kMaxExpectedSizePerHInstruction;
Mark Mendellfe57faa2015-09-18 09:26:15 -04005931
Vladimir Markof3e0ee22015-12-17 15:23:13 +00005932 if (num_entries <= kPackedSwitchCompareJumpThreshold ||
Zheng Xu3927c8b2015-11-18 17:46:25 +08005933 // Current instruction id is an upper bound of the number of HIRs in the graph.
5934 GetGraph()->GetCurrentInstructionId() > kJumpTableInstructionThreshold) {
5935 // Create a series of compare/jumps.
Vladimir Markof3e0ee22015-12-17 15:23:13 +00005936 UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
5937 Register temp = temps.AcquireW();
5938 __ Subs(temp, value_reg, Operand(lower_bound));
5939
Zheng Xu3927c8b2015-11-18 17:46:25 +08005940 const ArenaVector<HBasicBlock*>& successors = switch_instr->GetBlock()->GetSuccessors();
Vladimir Markof3e0ee22015-12-17 15:23:13 +00005941 // Jump to successors[0] if value == lower_bound.
5942 __ B(eq, codegen_->GetLabelOf(successors[0]));
5943 int32_t last_index = 0;
5944 for (; num_entries - last_index > 2; last_index += 2) {
5945 __ Subs(temp, temp, Operand(2));
5946 // Jump to successors[last_index + 1] if value < case_value[last_index + 2].
5947 __ B(lo, codegen_->GetLabelOf(successors[last_index + 1]));
5948 // Jump to successors[last_index + 2] if value == case_value[last_index + 2].
5949 __ B(eq, codegen_->GetLabelOf(successors[last_index + 2]));
5950 }
5951 if (num_entries - last_index == 2) {
5952 // The last missing case_value.
5953 __ Cmp(temp, Operand(1));
5954 __ B(eq, codegen_->GetLabelOf(successors[last_index + 1]));
Zheng Xu3927c8b2015-11-18 17:46:25 +08005955 }
5956
5957 // And the default for any other value.
5958 if (!codegen_->GoesToNextBlock(switch_instr->GetBlock(), default_block)) {
5959 __ B(codegen_->GetLabelOf(default_block));
5960 }
5961 } else {
Alexandre Ramesc01a6642016-04-15 11:54:06 +01005962 JumpTableARM64* jump_table = codegen_->CreateJumpTable(switch_instr);
Zheng Xu3927c8b2015-11-18 17:46:25 +08005963
5964 UseScratchRegisterScope temps(codegen_->GetVIXLAssembler());
5965
5966 // Below instructions should use at most one blocked register. Since there are two blocked
5967 // registers, we are free to block one.
5968 Register temp_w = temps.AcquireW();
5969 Register index;
5970 // Remove the bias.
5971 if (lower_bound != 0) {
5972 index = temp_w;
5973 __ Sub(index, value_reg, Operand(lower_bound));
5974 } else {
5975 index = value_reg;
5976 }
5977
5978 // Jump to default block if index is out of the range.
5979 __ Cmp(index, Operand(num_entries));
5980 __ B(hs, codegen_->GetLabelOf(default_block));
5981
5982 // In current VIXL implementation, it won't require any blocked registers to encode the
5983 // immediate value for Adr. So we are free to use both VIXL blocked registers to reduce the
5984 // register pressure.
5985 Register table_base = temps.AcquireX();
5986 // Load jump offset from the table.
5987 __ Adr(table_base, jump_table->GetTableStartLabel());
5988 Register jump_offset = temp_w;
5989 __ Ldr(jump_offset, MemOperand(table_base, index, UXTW, 2));
5990
5991 // Jump to target block by branching to table_base(pc related) + offset.
5992 Register target_address = table_base;
5993 __ Add(target_address, table_base, Operand(jump_offset, SXTW));
5994 __ Br(target_address);
Mark Mendellfe57faa2015-09-18 09:26:15 -04005995 }
5996}
5997
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08005998void InstructionCodeGeneratorARM64::GenerateReferenceLoadOneRegister(
5999 HInstruction* instruction,
6000 Location out,
6001 uint32_t offset,
6002 Location maybe_temp,
6003 ReadBarrierOption read_barrier_option) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006004 DataType::Type type = DataType::Type::kReference;
Roland Levillain44015862016-01-22 11:47:17 +00006005 Register out_reg = RegisterFrom(out, type);
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08006006 if (read_barrier_option == kWithReadBarrier) {
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08006007 CHECK(kEmitCompilerReadBarrier);
Roland Levillain44015862016-01-22 11:47:17 +00006008 if (kUseBakerReadBarrier) {
6009 // Load with fast path based Baker's read barrier.
6010 // /* HeapReference<Object> */ out = *(out + offset)
6011 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6012 out,
6013 out_reg,
6014 offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006015 maybe_temp,
Roland Levillain44015862016-01-22 11:47:17 +00006016 /* needs_null_check */ false,
6017 /* use_load_acquire */ false);
6018 } else {
6019 // Load with slow path based read barrier.
6020 // Save the value of `out` into `maybe_temp` before overwriting it
6021 // in the following move operation, as we will need it for the
6022 // read barrier below.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006023 Register temp_reg = RegisterFrom(maybe_temp, type);
Roland Levillain44015862016-01-22 11:47:17 +00006024 __ Mov(temp_reg, out_reg);
6025 // /* HeapReference<Object> */ out = *(out + offset)
6026 __ Ldr(out_reg, HeapOperand(out_reg, offset));
6027 codegen_->GenerateReadBarrierSlow(instruction, out, out, maybe_temp, offset);
6028 }
6029 } else {
6030 // Plain load with no read barrier.
6031 // /* HeapReference<Object> */ out = *(out + offset)
6032 __ Ldr(out_reg, HeapOperand(out_reg, offset));
6033 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
6034 }
6035}
6036
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08006037void InstructionCodeGeneratorARM64::GenerateReferenceLoadTwoRegisters(
6038 HInstruction* instruction,
6039 Location out,
6040 Location obj,
6041 uint32_t offset,
6042 Location maybe_temp,
6043 ReadBarrierOption read_barrier_option) {
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006044 DataType::Type type = DataType::Type::kReference;
Roland Levillain44015862016-01-22 11:47:17 +00006045 Register out_reg = RegisterFrom(out, type);
6046 Register obj_reg = RegisterFrom(obj, type);
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08006047 if (read_barrier_option == kWithReadBarrier) {
Mathieu Chartieraa474eb2016-11-09 15:18:27 -08006048 CHECK(kEmitCompilerReadBarrier);
Roland Levillain44015862016-01-22 11:47:17 +00006049 if (kUseBakerReadBarrier) {
6050 // Load with fast path based Baker's read barrier.
Roland Levillain44015862016-01-22 11:47:17 +00006051 // /* HeapReference<Object> */ out = *(obj + offset)
6052 codegen_->GenerateFieldLoadWithBakerReadBarrier(instruction,
6053 out,
6054 obj_reg,
6055 offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006056 maybe_temp,
Roland Levillain44015862016-01-22 11:47:17 +00006057 /* needs_null_check */ false,
6058 /* use_load_acquire */ false);
6059 } else {
6060 // Load with slow path based read barrier.
6061 // /* HeapReference<Object> */ out = *(obj + offset)
6062 __ Ldr(out_reg, HeapOperand(obj_reg, offset));
6063 codegen_->GenerateReadBarrierSlow(instruction, out, out, obj, offset);
6064 }
6065 } else {
6066 // Plain load with no read barrier.
6067 // /* HeapReference<Object> */ out = *(obj + offset)
6068 __ Ldr(out_reg, HeapOperand(obj_reg, offset));
6069 GetAssembler()->MaybeUnpoisonHeapReference(out_reg);
6070 }
6071}
6072
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08006073void InstructionCodeGeneratorARM64::GenerateGcRootFieldLoad(
6074 HInstruction* instruction,
6075 Location root,
6076 Register obj,
6077 uint32_t offset,
6078 vixl::aarch64::Label* fixup_label,
6079 ReadBarrierOption read_barrier_option) {
Vladimir Markoaad75c62016-10-03 08:46:48 +00006080 DCHECK(fixup_label == nullptr || offset == 0u);
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006081 Register root_reg = RegisterFrom(root, DataType::Type::kReference);
Mathieu Chartier3af00dc2016-11-10 11:25:57 -08006082 if (read_barrier_option == kWithReadBarrier) {
Mathieu Chartier31b12e32016-09-02 17:11:57 -07006083 DCHECK(kEmitCompilerReadBarrier);
Roland Levillain44015862016-01-22 11:47:17 +00006084 if (kUseBakerReadBarrier) {
6085 // Fast path implementation of art::ReadBarrier::BarrierForRoot when
Roland Levillainba650a42017-03-06 13:52:32 +00006086 // Baker's read barrier are used.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006087 if (kBakerReadBarrierLinkTimeThunksEnableForGcRoots &&
6088 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain97c46462017-05-11 14:04:03 +01006089 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
6090 // the Marking Register) to decide whether we need to enter
6091 // the slow path to mark the GC root.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006092 //
6093 // We use link-time generated thunks for the slow path. That thunk
6094 // checks the reference and jumps to the entrypoint if needed.
6095 //
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006096 // lr = &return_address;
6097 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
Roland Levillain97c46462017-05-11 14:04:03 +01006098 // if (mr) { // Thread::Current()->GetIsGcMarking()
6099 // goto gc_root_thunk<root_reg>(lr)
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006100 // }
6101 // return_address:
Roland Levillain44015862016-01-22 11:47:17 +00006102
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006103 UseScratchRegisterScope temps(GetVIXLAssembler());
6104 DCHECK(temps.IsAvailable(ip0));
6105 DCHECK(temps.IsAvailable(ip1));
6106 temps.Exclude(ip0, ip1);
6107 uint32_t custom_data =
6108 linker::Arm64RelativePatcher::EncodeBakerReadBarrierGcRootData(root_reg.GetCode());
6109 vixl::aarch64::Label* cbnz_label = codegen_->NewBakerReadBarrierPatch(custom_data);
Roland Levillainba650a42017-03-06 13:52:32 +00006110
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006111 EmissionCheckScope guard(GetVIXLAssembler(), 3 * vixl::aarch64::kInstructionSize);
6112 vixl::aarch64::Label return_address;
6113 __ adr(lr, &return_address);
6114 if (fixup_label != nullptr) {
6115 __ Bind(fixup_label);
6116 }
6117 static_assert(BAKER_MARK_INTROSPECTION_GC_ROOT_LDR_OFFSET == -8,
6118 "GC root LDR must be 2 instruction (8B) before the return address label.");
6119 __ ldr(root_reg, MemOperand(obj.X(), offset));
6120 __ Bind(cbnz_label);
Roland Levillain97c46462017-05-11 14:04:03 +01006121 __ cbnz(mr, static_cast<int64_t>(0)); // Placeholder, patched at link-time.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006122 __ Bind(&return_address);
Vladimir Markocac5a7e2016-02-22 10:39:50 +00006123 } else {
Roland Levillain97c46462017-05-11 14:04:03 +01006124 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in
6125 // the Marking Register) to decide whether we need to enter
6126 // the slow path to mark the GC root.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006127 //
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006128 // GcRoot<mirror::Object> root = *(obj+offset); // Original reference load.
Roland Levillain97c46462017-05-11 14:04:03 +01006129 // if (mr) { // Thread::Current()->GetIsGcMarking()
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006130 // // Slow path.
Roland Levillain97c46462017-05-11 14:04:03 +01006131 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
6132 // root = entrypoint(root); // root = ReadBarrier::Mark(root); // Entry point call.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006133 // }
Roland Levillain44015862016-01-22 11:47:17 +00006134
Roland Levillain97c46462017-05-11 14:04:03 +01006135 // Slow path marking the GC root `root`. The entrypoint will
6136 // be loaded by the slow path code.
6137 SlowPathCodeARM64* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006138 new (codegen_->GetScopedAllocator()) ReadBarrierMarkSlowPathARM64(instruction, root);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006139 codegen_->AddSlowPath(slow_path);
6140
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006141 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
6142 if (fixup_label == nullptr) {
6143 __ Ldr(root_reg, MemOperand(obj, offset));
6144 } else {
6145 codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj);
6146 }
6147 static_assert(
6148 sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(GcRoot<mirror::Object>),
6149 "art::mirror::CompressedReference<mirror::Object> and art::GcRoot<mirror::Object> "
6150 "have different sizes.");
6151 static_assert(sizeof(mirror::CompressedReference<mirror::Object>) == sizeof(int32_t),
6152 "art::mirror::CompressedReference<mirror::Object> and int32_t "
6153 "have different sizes.");
6154
Roland Levillain97c46462017-05-11 14:04:03 +01006155 __ Cbnz(mr, slow_path->GetEntryLabel());
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006156 __ Bind(slow_path->GetExitLabel());
6157 }
Roland Levillain44015862016-01-22 11:47:17 +00006158 } else {
6159 // GC root loaded through a slow path for read barriers other
6160 // than Baker's.
6161 // /* GcRoot<mirror::Object>* */ root = obj + offset
Vladimir Markocac5a7e2016-02-22 10:39:50 +00006162 if (fixup_label == nullptr) {
6163 __ Add(root_reg.X(), obj.X(), offset);
6164 } else {
Vladimir Markoaad75c62016-10-03 08:46:48 +00006165 codegen_->EmitAddPlaceholder(fixup_label, root_reg.X(), obj.X());
Vladimir Markocac5a7e2016-02-22 10:39:50 +00006166 }
Roland Levillain44015862016-01-22 11:47:17 +00006167 // /* mirror::Object* */ root = root->Read()
6168 codegen_->GenerateReadBarrierForRootSlow(instruction, root, root);
6169 }
6170 } else {
6171 // Plain GC root load with no read barrier.
6172 // /* GcRoot<mirror::Object> */ root = *(obj + offset)
Vladimir Markocac5a7e2016-02-22 10:39:50 +00006173 if (fixup_label == nullptr) {
6174 __ Ldr(root_reg, MemOperand(obj, offset));
6175 } else {
Vladimir Markoaad75c62016-10-03 08:46:48 +00006176 codegen_->EmitLdrOffsetPlaceholder(fixup_label, root_reg, obj.X());
Vladimir Markocac5a7e2016-02-22 10:39:50 +00006177 }
Roland Levillain44015862016-01-22 11:47:17 +00006178 // Note that GC roots are not affected by heap poisoning, thus we
6179 // do not have to unpoison `root_reg` here.
6180 }
Roland Levillain2b03a1f2017-06-06 16:09:59 +01006181 codegen_->MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Roland Levillain44015862016-01-22 11:47:17 +00006182}
6183
6184void CodeGeneratorARM64::GenerateFieldLoadWithBakerReadBarrier(HInstruction* instruction,
6185 Location ref,
Scott Wakeling97c72b72016-06-24 16:19:36 +01006186 Register obj,
Roland Levillain44015862016-01-22 11:47:17 +00006187 uint32_t offset,
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006188 Location maybe_temp,
Roland Levillain44015862016-01-22 11:47:17 +00006189 bool needs_null_check,
6190 bool use_load_acquire) {
6191 DCHECK(kEmitCompilerReadBarrier);
6192 DCHECK(kUseBakerReadBarrier);
6193
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006194 if (kBakerReadBarrierLinkTimeThunksEnableForFields &&
6195 !use_load_acquire &&
6196 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain97c46462017-05-11 14:04:03 +01006197 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
6198 // Marking Register) to decide whether we need to enter the slow
6199 // path to mark the reference. Then, in the slow path, check the
6200 // gray bit in the lock word of the reference's holder (`obj`) to
6201 // decide whether to mark `ref` or not.
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006202 //
6203 // We use link-time generated thunks for the slow path. That thunk checks
6204 // the holder and jumps to the entrypoint if needed. If the holder is not
6205 // gray, it creates a fake dependency and returns to the LDR instruction.
6206 //
Vladimir Marko66d691d2017-04-07 17:53:39 +01006207 // lr = &gray_return_address;
Roland Levillain97c46462017-05-11 14:04:03 +01006208 // if (mr) { // Thread::Current()->GetIsGcMarking()
6209 // goto field_thunk<holder_reg, base_reg>(lr)
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006210 // }
6211 // not_gray_return_address:
6212 // // Original reference load. If the offset is too large to fit
6213 // // into LDR, we use an adjusted base register here.
Vladimir Marko88abba22017-05-03 17:09:25 +01006214 // HeapReference<mirror::Object> reference = *(obj+offset);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006215 // gray_return_address:
6216
6217 DCHECK_ALIGNED(offset, sizeof(mirror::HeapReference<mirror::Object>));
6218 Register base = obj;
6219 if (offset >= kReferenceLoadMinFarOffset) {
6220 DCHECK(maybe_temp.IsRegister());
6221 base = WRegisterFrom(maybe_temp);
6222 static_assert(IsPowerOfTwo(kReferenceLoadMinFarOffset), "Expecting a power of 2.");
6223 __ Add(base, obj, Operand(offset & ~(kReferenceLoadMinFarOffset - 1u)));
6224 offset &= (kReferenceLoadMinFarOffset - 1u);
6225 }
6226 UseScratchRegisterScope temps(GetVIXLAssembler());
6227 DCHECK(temps.IsAvailable(ip0));
6228 DCHECK(temps.IsAvailable(ip1));
6229 temps.Exclude(ip0, ip1);
6230 uint32_t custom_data = linker::Arm64RelativePatcher::EncodeBakerReadBarrierFieldData(
6231 base.GetCode(),
6232 obj.GetCode());
6233 vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data);
6234
Roland Levillain2b03a1f2017-06-06 16:09:59 +01006235 {
6236 EmissionCheckScope guard(GetVIXLAssembler(),
6237 (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
6238 vixl::aarch64::Label return_address;
6239 __ adr(lr, &return_address);
6240 __ Bind(cbnz_label);
6241 __ cbnz(mr, static_cast<int64_t>(0)); // Placeholder, patched at link-time.
6242 static_assert(BAKER_MARK_INTROSPECTION_FIELD_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
6243 "Field LDR must be 1 instruction (4B) before the return address label; "
6244 " 2 instructions (8B) for heap poisoning.");
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006245 Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
Roland Levillain2b03a1f2017-06-06 16:09:59 +01006246 __ ldr(ref_reg, MemOperand(base.X(), offset));
6247 if (needs_null_check) {
6248 MaybeRecordImplicitNullCheck(instruction);
6249 }
6250 GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
6251 __ Bind(&return_address);
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006252 }
Roland Levillain2b03a1f2017-06-06 16:09:59 +01006253 MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__, /* temp_loc */ LocationFrom(ip1));
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006254 return;
6255 }
6256
Roland Levillain44015862016-01-22 11:47:17 +00006257 // /* HeapReference<Object> */ ref = *(obj + offset)
Vladimir Markof4f2daa2017-03-20 18:26:59 +00006258 Register temp = WRegisterFrom(maybe_temp);
Roland Levillain44015862016-01-22 11:47:17 +00006259 Location no_index = Location::NoLocation();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01006260 size_t no_scale_factor = 0u;
Roland Levillainbfea3352016-06-23 13:48:47 +01006261 GenerateReferenceLoadWithBakerReadBarrier(instruction,
6262 ref,
6263 obj,
6264 offset,
6265 no_index,
6266 no_scale_factor,
6267 temp,
6268 needs_null_check,
6269 use_load_acquire);
Roland Levillain44015862016-01-22 11:47:17 +00006270}
6271
6272void CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier(HInstruction* instruction,
6273 Location ref,
Scott Wakeling97c72b72016-06-24 16:19:36 +01006274 Register obj,
Roland Levillain44015862016-01-22 11:47:17 +00006275 uint32_t data_offset,
6276 Location index,
6277 Register temp,
6278 bool needs_null_check) {
6279 DCHECK(kEmitCompilerReadBarrier);
6280 DCHECK(kUseBakerReadBarrier);
6281
Vladimir Marko66d691d2017-04-07 17:53:39 +01006282 static_assert(
6283 sizeof(mirror::HeapReference<mirror::Object>) == sizeof(int32_t),
6284 "art::mirror::HeapReference<art::mirror::Object> and int32_t have different sizes.");
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006285 size_t scale_factor = DataType::SizeShift(DataType::Type::kReference);
Vladimir Marko66d691d2017-04-07 17:53:39 +01006286
6287 if (kBakerReadBarrierLinkTimeThunksEnableForArrays &&
6288 !Runtime::Current()->UseJitCompilation()) {
Roland Levillain97c46462017-05-11 14:04:03 +01006289 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
6290 // Marking Register) to decide whether we need to enter the slow
6291 // path to mark the reference. Then, in the slow path, check the
6292 // gray bit in the lock word of the reference's holder (`obj`) to
6293 // decide whether to mark `ref` or not.
Vladimir Marko66d691d2017-04-07 17:53:39 +01006294 //
6295 // We use link-time generated thunks for the slow path. That thunk checks
6296 // the holder and jumps to the entrypoint if needed. If the holder is not
6297 // gray, it creates a fake dependency and returns to the LDR instruction.
6298 //
Vladimir Marko66d691d2017-04-07 17:53:39 +01006299 // lr = &gray_return_address;
Roland Levillain97c46462017-05-11 14:04:03 +01006300 // if (mr) { // Thread::Current()->GetIsGcMarking()
6301 // goto array_thunk<base_reg>(lr)
Vladimir Marko66d691d2017-04-07 17:53:39 +01006302 // }
6303 // not_gray_return_address:
6304 // // Original reference load. If the offset is too large to fit
6305 // // into LDR, we use an adjusted base register here.
Vladimir Marko88abba22017-05-03 17:09:25 +01006306 // HeapReference<mirror::Object> reference = data[index];
Vladimir Marko66d691d2017-04-07 17:53:39 +01006307 // gray_return_address:
6308
6309 DCHECK(index.IsValid());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006310 Register index_reg = RegisterFrom(index, DataType::Type::kInt32);
6311 Register ref_reg = RegisterFrom(ref, DataType::Type::kReference);
Vladimir Marko66d691d2017-04-07 17:53:39 +01006312
6313 UseScratchRegisterScope temps(GetVIXLAssembler());
6314 DCHECK(temps.IsAvailable(ip0));
6315 DCHECK(temps.IsAvailable(ip1));
6316 temps.Exclude(ip0, ip1);
6317 uint32_t custom_data =
6318 linker::Arm64RelativePatcher::EncodeBakerReadBarrierArrayData(temp.GetCode());
6319 vixl::aarch64::Label* cbnz_label = NewBakerReadBarrierPatch(custom_data);
6320
Vladimir Marko66d691d2017-04-07 17:53:39 +01006321 __ Add(temp.X(), obj.X(), Operand(data_offset));
Roland Levillain2b03a1f2017-06-06 16:09:59 +01006322 {
6323 EmissionCheckScope guard(GetVIXLAssembler(),
6324 (kPoisonHeapReferences ? 4u : 3u) * vixl::aarch64::kInstructionSize);
6325 vixl::aarch64::Label return_address;
6326 __ adr(lr, &return_address);
6327 __ Bind(cbnz_label);
6328 __ cbnz(mr, static_cast<int64_t>(0)); // Placeholder, patched at link-time.
6329 static_assert(BAKER_MARK_INTROSPECTION_ARRAY_LDR_OFFSET == (kPoisonHeapReferences ? -8 : -4),
6330 "Array LDR must be 1 instruction (4B) before the return address label; "
6331 " 2 instructions (8B) for heap poisoning.");
6332 __ ldr(ref_reg, MemOperand(temp.X(), index_reg.X(), LSL, scale_factor));
6333 DCHECK(!needs_null_check); // The thunk cannot handle the null check.
6334 GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
6335 __ Bind(&return_address);
6336 }
6337 MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__, /* temp_loc */ LocationFrom(ip1));
Vladimir Marko66d691d2017-04-07 17:53:39 +01006338 return;
6339 }
6340
Roland Levillain44015862016-01-22 11:47:17 +00006341 // Array cells are never volatile variables, therefore array loads
6342 // never use Load-Acquire instructions on ARM64.
6343 const bool use_load_acquire = false;
6344
6345 // /* HeapReference<Object> */ ref =
6346 // *(obj + data_offset + index * sizeof(HeapReference<Object>))
Roland Levillainbfea3352016-06-23 13:48:47 +01006347 GenerateReferenceLoadWithBakerReadBarrier(instruction,
6348 ref,
6349 obj,
6350 data_offset,
6351 index,
6352 scale_factor,
6353 temp,
6354 needs_null_check,
6355 use_load_acquire);
Roland Levillain44015862016-01-22 11:47:17 +00006356}
6357
6358void CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier(HInstruction* instruction,
6359 Location ref,
Scott Wakeling97c72b72016-06-24 16:19:36 +01006360 Register obj,
Roland Levillain44015862016-01-22 11:47:17 +00006361 uint32_t offset,
6362 Location index,
Roland Levillainbfea3352016-06-23 13:48:47 +01006363 size_t scale_factor,
Roland Levillain44015862016-01-22 11:47:17 +00006364 Register temp,
6365 bool needs_null_check,
Roland Levillainff487002017-03-07 16:50:01 +00006366 bool use_load_acquire) {
Roland Levillain44015862016-01-22 11:47:17 +00006367 DCHECK(kEmitCompilerReadBarrier);
6368 DCHECK(kUseBakerReadBarrier);
Roland Levillainbfea3352016-06-23 13:48:47 +01006369 // If we are emitting an array load, we should not be using a
6370 // Load Acquire instruction. In other words:
6371 // `instruction->IsArrayGet()` => `!use_load_acquire`.
6372 DCHECK(!instruction->IsArrayGet() || !use_load_acquire);
Roland Levillain44015862016-01-22 11:47:17 +00006373
Roland Levillain97c46462017-05-11 14:04:03 +01006374 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
6375 // Marking Register) to decide whether we need to enter the slow
6376 // path to mark the reference. Then, in the slow path, check the
6377 // gray bit in the lock word of the reference's holder (`obj`) to
6378 // decide whether to mark `ref` or not.
Roland Levillain44015862016-01-22 11:47:17 +00006379 //
Roland Levillain97c46462017-05-11 14:04:03 +01006380 // if (mr) { // Thread::Current()->GetIsGcMarking()
Roland Levillainba650a42017-03-06 13:52:32 +00006381 // // Slow path.
Roland Levillain54f869e2017-03-06 13:54:11 +00006382 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
6383 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
6384 // HeapReference<mirror::Object> ref = *src; // Original reference load.
6385 // bool is_gray = (rb_state == ReadBarrier::GrayState());
6386 // if (is_gray) {
Roland Levillain97c46462017-05-11 14:04:03 +01006387 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
6388 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
Roland Levillain54f869e2017-03-06 13:54:11 +00006389 // }
6390 // } else {
6391 // HeapReference<mirror::Object> ref = *src; // Original reference load.
Roland Levillain44015862016-01-22 11:47:17 +00006392 // }
Roland Levillain44015862016-01-22 11:47:17 +00006393
Roland Levillainba650a42017-03-06 13:52:32 +00006394 // Slow path marking the object `ref` when the GC is marking. The
Roland Levillain97c46462017-05-11 14:04:03 +01006395 // entrypoint will be loaded by the slow path code.
Roland Levillainff487002017-03-07 16:50:01 +00006396 SlowPathCodeARM64* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006397 new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierSlowPathARM64(
Roland Levillainff487002017-03-07 16:50:01 +00006398 instruction,
6399 ref,
6400 obj,
6401 offset,
6402 index,
6403 scale_factor,
6404 needs_null_check,
6405 use_load_acquire,
Roland Levillain97c46462017-05-11 14:04:03 +01006406 temp);
Roland Levillainba650a42017-03-06 13:52:32 +00006407 AddSlowPath(slow_path);
6408
Roland Levillain97c46462017-05-11 14:04:03 +01006409 __ Cbnz(mr, slow_path->GetEntryLabel());
Roland Levillainff487002017-03-07 16:50:01 +00006410 // Fast path: the GC is not marking: just load the reference.
Roland Levillain54f869e2017-03-06 13:54:11 +00006411 GenerateRawReferenceLoad(
6412 instruction, ref, obj, offset, index, scale_factor, needs_null_check, use_load_acquire);
Roland Levillainba650a42017-03-06 13:52:32 +00006413 __ Bind(slow_path->GetExitLabel());
Roland Levillain2b03a1f2017-06-06 16:09:59 +01006414 MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Roland Levillainba650a42017-03-06 13:52:32 +00006415}
6416
Roland Levillainff487002017-03-07 16:50:01 +00006417void CodeGeneratorARM64::UpdateReferenceFieldWithBakerReadBarrier(HInstruction* instruction,
6418 Location ref,
6419 Register obj,
6420 Location field_offset,
6421 Register temp,
6422 bool needs_null_check,
6423 bool use_load_acquire) {
6424 DCHECK(kEmitCompilerReadBarrier);
6425 DCHECK(kUseBakerReadBarrier);
6426 // If we are emitting an array load, we should not be using a
6427 // Load Acquire instruction. In other words:
6428 // `instruction->IsArrayGet()` => `!use_load_acquire`.
6429 DCHECK(!instruction->IsArrayGet() || !use_load_acquire);
6430
Roland Levillain97c46462017-05-11 14:04:03 +01006431 // Query `art::Thread::Current()->GetIsGcMarking()` (stored in the
6432 // Marking Register) to decide whether we need to enter the slow
6433 // path to update the reference field within `obj`. Then, in the
6434 // slow path, check the gray bit in the lock word of the reference's
6435 // holder (`obj`) to decide whether to mark `ref` and update the
6436 // field or not.
Roland Levillainff487002017-03-07 16:50:01 +00006437 //
Roland Levillain97c46462017-05-11 14:04:03 +01006438 // if (mr) { // Thread::Current()->GetIsGcMarking()
Roland Levillainff487002017-03-07 16:50:01 +00006439 // // Slow path.
6440 // uint32_t rb_state = Lockword(obj->monitor_).ReadBarrierState();
6441 // lfence; // Load fence or artificial data dependency to prevent load-load reordering
6442 // HeapReference<mirror::Object> ref = *(obj + field_offset); // Reference load.
6443 // bool is_gray = (rb_state == ReadBarrier::GrayState());
6444 // if (is_gray) {
6445 // old_ref = ref;
Roland Levillain97c46462017-05-11 14:04:03 +01006446 // entrypoint = Thread::Current()->pReadBarrierMarkReg ## root.reg()
6447 // ref = entrypoint(ref); // ref = ReadBarrier::Mark(ref); // Runtime entry point call.
Roland Levillainff487002017-03-07 16:50:01 +00006448 // compareAndSwapObject(obj, field_offset, old_ref, ref);
6449 // }
6450 // }
6451
6452 // Slow path updating the object reference at address `obj + field_offset`
Roland Levillain97c46462017-05-11 14:04:03 +01006453 // when the GC is marking. The entrypoint will be loaded by the slow path code.
Roland Levillainff487002017-03-07 16:50:01 +00006454 SlowPathCodeARM64* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006455 new (GetScopedAllocator()) LoadReferenceWithBakerReadBarrierAndUpdateFieldSlowPathARM64(
Roland Levillainff487002017-03-07 16:50:01 +00006456 instruction,
6457 ref,
6458 obj,
6459 /* offset */ 0u,
6460 /* index */ field_offset,
6461 /* scale_factor */ 0u /* "times 1" */,
6462 needs_null_check,
6463 use_load_acquire,
Roland Levillain97c46462017-05-11 14:04:03 +01006464 temp);
Roland Levillainff487002017-03-07 16:50:01 +00006465 AddSlowPath(slow_path);
6466
Roland Levillain97c46462017-05-11 14:04:03 +01006467 __ Cbnz(mr, slow_path->GetEntryLabel());
Roland Levillainff487002017-03-07 16:50:01 +00006468 // Fast path: the GC is not marking: nothing to do (the field is
6469 // up-to-date, and we don't need to load the reference).
6470 __ Bind(slow_path->GetExitLabel());
Roland Levillain2b03a1f2017-06-06 16:09:59 +01006471 MaybeGenerateMarkingRegisterCheck(/* code */ __LINE__);
Roland Levillainff487002017-03-07 16:50:01 +00006472}
6473
Roland Levillainba650a42017-03-06 13:52:32 +00006474void CodeGeneratorARM64::GenerateRawReferenceLoad(HInstruction* instruction,
6475 Location ref,
6476 Register obj,
6477 uint32_t offset,
6478 Location index,
6479 size_t scale_factor,
6480 bool needs_null_check,
6481 bool use_load_acquire) {
6482 DCHECK(obj.IsW());
Vladimir Marko0ebe0d82017-09-21 22:50:39 +01006483 DataType::Type type = DataType::Type::kReference;
Roland Levillain44015862016-01-22 11:47:17 +00006484 Register ref_reg = RegisterFrom(ref, type);
Roland Levillain44015862016-01-22 11:47:17 +00006485
Roland Levillainba650a42017-03-06 13:52:32 +00006486 // If needed, vixl::EmissionCheckScope guards are used to ensure
6487 // that no pools are emitted between the load (macro) instruction
6488 // and MaybeRecordImplicitNullCheck.
Roland Levillain44015862016-01-22 11:47:17 +00006489
Roland Levillain44015862016-01-22 11:47:17 +00006490 if (index.IsValid()) {
Roland Levillaina1aa3b12016-10-26 13:03:38 +01006491 // Load types involving an "index": ArrayGet,
6492 // UnsafeGetObject/UnsafeGetObjectVolatile and UnsafeCASObject
6493 // intrinsics.
Roland Levillainbfea3352016-06-23 13:48:47 +01006494 if (use_load_acquire) {
6495 // UnsafeGetObjectVolatile intrinsic case.
6496 // Register `index` is not an index in an object array, but an
6497 // offset to an object reference field within object `obj`.
6498 DCHECK(instruction->IsInvoke()) << instruction->DebugName();
6499 DCHECK(instruction->GetLocations()->Intrinsified());
6500 DCHECK(instruction->AsInvoke()->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile)
6501 << instruction->AsInvoke()->GetIntrinsic();
Roland Levillaina1aa3b12016-10-26 13:03:38 +01006502 DCHECK_EQ(offset, 0u);
6503 DCHECK_EQ(scale_factor, 0u);
Roland Levillainba650a42017-03-06 13:52:32 +00006504 DCHECK_EQ(needs_null_check, false);
6505 // /* HeapReference<mirror::Object> */ ref = *(obj + index)
Roland Levillainbfea3352016-06-23 13:48:47 +01006506 MemOperand field = HeapOperand(obj, XRegisterFrom(index));
6507 LoadAcquire(instruction, ref_reg, field, /* needs_null_check */ false);
Roland Levillain44015862016-01-22 11:47:17 +00006508 } else {
Roland Levillainba650a42017-03-06 13:52:32 +00006509 // ArrayGet and UnsafeGetObject and UnsafeCASObject intrinsics cases.
6510 // /* HeapReference<mirror::Object> */ ref = *(obj + offset + (index << scale_factor))
Roland Levillainbfea3352016-06-23 13:48:47 +01006511 if (index.IsConstant()) {
6512 uint32_t computed_offset = offset + (Int64ConstantFrom(index) << scale_factor);
Roland Levillainba650a42017-03-06 13:52:32 +00006513 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Roland Levillainbfea3352016-06-23 13:48:47 +01006514 Load(type, ref_reg, HeapOperand(obj, computed_offset));
Roland Levillainba650a42017-03-06 13:52:32 +00006515 if (needs_null_check) {
6516 MaybeRecordImplicitNullCheck(instruction);
6517 }
Roland Levillainbfea3352016-06-23 13:48:47 +01006518 } else {
Roland Levillainba650a42017-03-06 13:52:32 +00006519 UseScratchRegisterScope temps(GetVIXLAssembler());
6520 Register temp = temps.AcquireW();
6521 __ Add(temp, obj, offset);
6522 {
6523 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
6524 Load(type, ref_reg, HeapOperand(temp, XRegisterFrom(index), LSL, scale_factor));
6525 if (needs_null_check) {
6526 MaybeRecordImplicitNullCheck(instruction);
6527 }
6528 }
Roland Levillainbfea3352016-06-23 13:48:47 +01006529 }
Roland Levillain44015862016-01-22 11:47:17 +00006530 }
Roland Levillain44015862016-01-22 11:47:17 +00006531 } else {
Roland Levillainba650a42017-03-06 13:52:32 +00006532 // /* HeapReference<mirror::Object> */ ref = *(obj + offset)
Roland Levillain44015862016-01-22 11:47:17 +00006533 MemOperand field = HeapOperand(obj, offset);
6534 if (use_load_acquire) {
Roland Levillainba650a42017-03-06 13:52:32 +00006535 // Implicit null checks are handled by CodeGeneratorARM64::LoadAcquire.
6536 LoadAcquire(instruction, ref_reg, field, needs_null_check);
Roland Levillain44015862016-01-22 11:47:17 +00006537 } else {
Roland Levillainba650a42017-03-06 13:52:32 +00006538 EmissionCheckScope guard(GetVIXLAssembler(), kMaxMacroInstructionSizeInBytes);
Roland Levillain44015862016-01-22 11:47:17 +00006539 Load(type, ref_reg, field);
Roland Levillainba650a42017-03-06 13:52:32 +00006540 if (needs_null_check) {
6541 MaybeRecordImplicitNullCheck(instruction);
6542 }
Roland Levillain44015862016-01-22 11:47:17 +00006543 }
6544 }
6545
6546 // Object* ref = ref_addr->AsMirrorPtr()
6547 GetAssembler()->MaybeUnpoisonHeapReference(ref_reg);
Roland Levillain44015862016-01-22 11:47:17 +00006548}
6549
Roland Levillain2b03a1f2017-06-06 16:09:59 +01006550void CodeGeneratorARM64::MaybeGenerateMarkingRegisterCheck(int code, Location temp_loc) {
6551 // The following condition is a compile-time one, so it does not have a run-time cost.
6552 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && kIsDebugBuild) {
6553 // The following condition is a run-time one; it is executed after the
6554 // previous compile-time test, to avoid penalizing non-debug builds.
6555 if (GetCompilerOptions().EmitRunTimeChecksInDebugMode()) {
6556 UseScratchRegisterScope temps(GetVIXLAssembler());
6557 Register temp = temp_loc.IsValid() ? WRegisterFrom(temp_loc) : temps.AcquireW();
6558 GetAssembler()->GenerateMarkingRegisterCheck(temp, code);
6559 }
6560 }
6561}
6562
Roland Levillain44015862016-01-22 11:47:17 +00006563void CodeGeneratorARM64::GenerateReadBarrierSlow(HInstruction* instruction,
6564 Location out,
6565 Location ref,
6566 Location obj,
6567 uint32_t offset,
6568 Location index) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006569 DCHECK(kEmitCompilerReadBarrier);
6570
Roland Levillain44015862016-01-22 11:47:17 +00006571 // Insert a slow path based read barrier *after* the reference load.
6572 //
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006573 // If heap poisoning is enabled, the unpoisoning of the loaded
6574 // reference will be carried out by the runtime within the slow
6575 // path.
6576 //
6577 // Note that `ref` currently does not get unpoisoned (when heap
6578 // poisoning is enabled), which is alright as the `ref` argument is
6579 // not used by the artReadBarrierSlow entry point.
6580 //
6581 // TODO: Unpoison `ref` when it is used by artReadBarrierSlow.
Vladimir Marko174b2e22017-10-12 13:34:49 +01006582 SlowPathCodeARM64* slow_path = new (GetScopedAllocator())
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006583 ReadBarrierForHeapReferenceSlowPathARM64(instruction, out, ref, obj, offset, index);
6584 AddSlowPath(slow_path);
6585
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006586 __ B(slow_path->GetEntryLabel());
6587 __ Bind(slow_path->GetExitLabel());
6588}
6589
Roland Levillain44015862016-01-22 11:47:17 +00006590void CodeGeneratorARM64::MaybeGenerateReadBarrierSlow(HInstruction* instruction,
6591 Location out,
6592 Location ref,
6593 Location obj,
6594 uint32_t offset,
6595 Location index) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006596 if (kEmitCompilerReadBarrier) {
Roland Levillain44015862016-01-22 11:47:17 +00006597 // Baker's read barriers shall be handled by the fast path
6598 // (CodeGeneratorARM64::GenerateReferenceLoadWithBakerReadBarrier).
6599 DCHECK(!kUseBakerReadBarrier);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006600 // If heap poisoning is enabled, unpoisoning will be taken care of
6601 // by the runtime within the slow path.
Roland Levillain44015862016-01-22 11:47:17 +00006602 GenerateReadBarrierSlow(instruction, out, ref, obj, offset, index);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006603 } else if (kPoisonHeapReferences) {
6604 GetAssembler()->UnpoisonHeapReference(WRegisterFrom(out));
6605 }
6606}
6607
Roland Levillain44015862016-01-22 11:47:17 +00006608void CodeGeneratorARM64::GenerateReadBarrierForRootSlow(HInstruction* instruction,
6609 Location out,
6610 Location root) {
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006611 DCHECK(kEmitCompilerReadBarrier);
6612
Roland Levillain44015862016-01-22 11:47:17 +00006613 // Insert a slow path based read barrier *after* the GC root load.
6614 //
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006615 // Note that GC roots are not affected by heap poisoning, so we do
6616 // not need to do anything special for this here.
6617 SlowPathCodeARM64* slow_path =
Vladimir Marko174b2e22017-10-12 13:34:49 +01006618 new (GetScopedAllocator()) ReadBarrierForRootSlowPathARM64(instruction, out, root);
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006619 AddSlowPath(slow_path);
6620
Roland Levillain22ccc3a2015-11-24 13:10:05 +00006621 __ B(slow_path->GetEntryLabel());
6622 __ Bind(slow_path->GetExitLabel());
6623}
6624
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00006625void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) {
6626 LocationSummary* locations =
Vladimir Markoca6fff82017-10-03 14:49:14 +01006627 new (GetGraph()->GetAllocator()) LocationSummary(instruction, LocationSummary::kNoCall);
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00006628 locations->SetInAt(0, Location::RequiresRegister());
6629 locations->SetOut(Location::RequiresRegister());
6630}
6631
6632void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) {
6633 LocationSummary* locations = instruction->GetLocations();
Vladimir Markoa1de9182016-02-25 11:37:38 +00006634 if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
Nicolas Geoffrayff484b92016-07-13 14:13:48 +01006635 uint32_t method_offset = mirror::Class::EmbeddedVTableEntryOffset(
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00006636 instruction->GetIndex(), kArm64PointerSize).SizeValue();
Nicolas Geoffrayff484b92016-07-13 14:13:48 +01006637 __ Ldr(XRegisterFrom(locations->Out()),
6638 MemOperand(XRegisterFrom(locations->InAt(0)), method_offset));
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00006639 } else {
Nicolas Geoffrayff484b92016-07-13 14:13:48 +01006640 uint32_t method_offset = static_cast<uint32_t>(ImTable::OffsetOfElement(
Matthew Gharrity465ecc82016-07-19 21:32:52 +00006641 instruction->GetIndex(), kArm64PointerSize));
Artem Udovichenkoa62cb9b2016-06-30 09:18:25 +00006642 __ Ldr(XRegisterFrom(locations->Out()), MemOperand(XRegisterFrom(locations->InAt(0)),
6643 mirror::Class::ImtPtrOffset(kArm64PointerSize).Uint32Value()));
Nicolas Geoffrayff484b92016-07-13 14:13:48 +01006644 __ Ldr(XRegisterFrom(locations->Out()),
6645 MemOperand(XRegisterFrom(locations->Out()), method_offset));
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00006646 }
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00006647}
6648
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00006649static void PatchJitRootUse(uint8_t* code,
6650 const uint8_t* roots_data,
6651 vixl::aarch64::Literal<uint32_t>* literal,
6652 uint64_t index_in_table) {
6653 uint32_t literal_offset = literal->GetOffset();
6654 uintptr_t address =
6655 reinterpret_cast<uintptr_t>(roots_data) + index_in_table * sizeof(GcRoot<mirror::Object>);
6656 uint8_t* data = code + literal_offset;
6657 reinterpret_cast<uint32_t*>(data)[0] = dchecked_integral_cast<uint32_t>(address);
6658}
6659
Nicolas Geoffray132d8362016-11-16 09:19:42 +00006660void CodeGeneratorARM64::EmitJitRootPatches(uint8_t* code, const uint8_t* roots_data) {
6661 for (const auto& entry : jit_string_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01006662 const StringReference& string_reference = entry.first;
6663 vixl::aarch64::Literal<uint32_t>* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01006664 uint64_t index_in_table = GetJitStringRootIndex(string_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01006665 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Nicolas Geoffray22384ae2016-12-12 22:33:36 +00006666 }
6667 for (const auto& entry : jit_class_patches_) {
Vladimir Marko7d157fc2017-05-10 16:29:23 +01006668 const TypeReference& type_reference = entry.first;
6669 vixl::aarch64::Literal<uint32_t>* table_entry_literal = entry.second;
Vladimir Marko174b2e22017-10-12 13:34:49 +01006670 uint64_t index_in_table = GetJitClassRootIndex(type_reference);
Vladimir Marko7d157fc2017-05-10 16:29:23 +01006671 PatchJitRootUse(code, roots_data, table_entry_literal, index_in_table);
Nicolas Geoffray132d8362016-11-16 09:19:42 +00006672 }
6673}
Nicolas Geoffraya42363f2015-12-17 14:57:09 +00006674
Alexandre Rames67555f72014-11-18 10:55:16 +00006675#undef __
6676#undef QUICK_ENTRY_POINT
6677
Alexandre Rames5319def2014-10-23 10:03:10 +01006678} // namespace arm64
6679} // namespace art