1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "jni_macro_assembler_arm_vixl.h"
18
19 #include <iostream>
20 #include <type_traits>
21
22 #include "entrypoints/quick/quick_entrypoints.h"
23 #include "thread.h"
24
25 using namespace vixl::aarch32; // NOLINT(build/namespaces)
26 namespace vixl32 = vixl::aarch32;
27
28 using vixl::ExactAssemblyScope;
29 using vixl::CodeBufferCheckScope;
30
31 namespace art {
32 namespace arm {
33
34 #ifdef ___
35 #error "ARM Assembler macro already defined."
36 #else
37 #define ___ asm_.GetVIXLAssembler()->
38 #endif
39
40 // The AAPCS requires 8-byte alignement. This is not as strict as the Managed ABI stack alignment.
41 static constexpr size_t kAapcsStackAlignment = 8u;
42 static_assert(kAapcsStackAlignment < kStackAlignment);
43
44 // STRD immediate can encode any 4-byte aligned offset smaller than this cutoff.
45 static constexpr size_t kStrdOffsetCutoff = 1024u;
46
AsVIXLRegister(ArmManagedRegister reg)47 vixl::aarch32::Register AsVIXLRegister(ArmManagedRegister reg) {
48 CHECK(reg.IsCoreRegister());
49 return vixl::aarch32::Register(reg.RegId());
50 }
51
AsVIXLSRegister(ArmManagedRegister reg)52 static inline vixl::aarch32::SRegister AsVIXLSRegister(ArmManagedRegister reg) {
53 CHECK(reg.IsSRegister());
54 return vixl::aarch32::SRegister(reg.RegId() - kNumberOfCoreRegIds);
55 }
56
AsVIXLDRegister(ArmManagedRegister reg)57 static inline vixl::aarch32::DRegister AsVIXLDRegister(ArmManagedRegister reg) {
58 CHECK(reg.IsDRegister());
59 return vixl::aarch32::DRegister(reg.RegId() - kNumberOfCoreRegIds - kNumberOfSRegIds);
60 }
61
AsVIXLRegisterPairLow(ArmManagedRegister reg)62 static inline vixl::aarch32::Register AsVIXLRegisterPairLow(ArmManagedRegister reg) {
63 return vixl::aarch32::Register(reg.AsRegisterPairLow());
64 }
65
AsVIXLRegisterPairHigh(ArmManagedRegister reg)66 static inline vixl::aarch32::Register AsVIXLRegisterPairHigh(ArmManagedRegister reg) {
67 return vixl::aarch32::Register(reg.AsRegisterPairHigh());
68 }
69
FinalizeCode()70 void ArmVIXLJNIMacroAssembler::FinalizeCode() {
71 for (const std::unique_ptr<
72 ArmVIXLJNIMacroAssembler::ArmException>& exception : exception_blocks_) {
73 EmitExceptionPoll(exception.get());
74 }
75 asm_.FinalizeCode();
76 }
77
78 static constexpr size_t kFramePointerSize = static_cast<size_t>(kArmPointerSize);
79
BuildFrame(size_t frame_size,ManagedRegister method_reg,ArrayRef<const ManagedRegister> callee_save_regs)80 void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size,
81 ManagedRegister method_reg,
82 ArrayRef<const ManagedRegister> callee_save_regs) {
83 // If we're creating an actual frame with the method, enforce managed stack alignment,
84 // otherwise only the native stack alignment.
85 if (method_reg.IsNoRegister()) {
86 CHECK_ALIGNED_PARAM(frame_size, kAapcsStackAlignment);
87 } else {
88 CHECK_ALIGNED_PARAM(frame_size, kStackAlignment);
89 }
90
91 // Push callee saves and link register.
92 RegList core_spill_mask = 0;
93 uint32_t fp_spill_mask = 0;
94 for (const ManagedRegister& reg : callee_save_regs) {
95 if (reg.AsArm().IsCoreRegister()) {
96 core_spill_mask |= 1 << reg.AsArm().AsCoreRegister();
97 } else {
98 fp_spill_mask |= 1 << reg.AsArm().AsSRegister();
99 }
100 }
101 if (core_spill_mask == (1u << lr.GetCode()) &&
102 fp_spill_mask == 0u &&
103 frame_size == 2 * kFramePointerSize &&
104 !method_reg.IsRegister()) {
105 // Special case: Only LR to push and one word to skip. Do this with a single
106 // 16-bit PUSH instruction by arbitrarily pushing r3 (without CFI for r3).
107 core_spill_mask |= 1u << r3.GetCode();
108 ___ Push(RegisterList(core_spill_mask));
109 cfi().AdjustCFAOffset(2 * kFramePointerSize);
110 cfi().RelOffset(DWARFReg(lr), kFramePointerSize);
111 } else if (core_spill_mask != 0u) {
112 ___ Push(RegisterList(core_spill_mask));
113 cfi().AdjustCFAOffset(POPCOUNT(core_spill_mask) * kFramePointerSize);
114 cfi().RelOffsetForMany(DWARFReg(r0), 0, core_spill_mask, kFramePointerSize);
115 }
116 if (fp_spill_mask != 0) {
117 uint32_t first = CTZ(fp_spill_mask);
118
119 // Check that list is contiguous.
120 DCHECK_EQ(fp_spill_mask >> CTZ(fp_spill_mask), ~0u >> (32 - POPCOUNT(fp_spill_mask)));
121
122 ___ Vpush(SRegisterList(vixl32::SRegister(first), POPCOUNT(fp_spill_mask)));
123 cfi().AdjustCFAOffset(POPCOUNT(fp_spill_mask) * kFramePointerSize);
124 cfi().RelOffsetForMany(DWARFReg(s0), 0, fp_spill_mask, kFramePointerSize);
125 }
126
127 // Increase frame to required size.
128 int pushed_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask);
129 // Must at least have space for Method* if we're going to spill it.
130 CHECK_GE(frame_size, (pushed_values + (method_reg.IsRegister() ? 1u : 0u)) * kFramePointerSize);
131 IncreaseFrameSize(frame_size - pushed_values * kFramePointerSize); // handles CFI as well.
132
133 if (method_reg.IsRegister()) {
134 // Write out Method*.
135 CHECK(r0.Is(AsVIXLRegister(method_reg.AsArm())));
136 asm_.StoreToOffset(kStoreWord, r0, sp, 0);
137 }
138 }
139
RemoveFrame(size_t frame_size,ArrayRef<const ManagedRegister> callee_save_regs,bool may_suspend)140 void ArmVIXLJNIMacroAssembler::RemoveFrame(size_t frame_size,
141 ArrayRef<const ManagedRegister> callee_save_regs,
142 bool may_suspend) {
143 CHECK_ALIGNED(frame_size, kAapcsStackAlignment);
144
145 // Compute callee saves to pop.
146 RegList core_spill_mask = 0u;
147 uint32_t fp_spill_mask = 0u;
148 for (const ManagedRegister& reg : callee_save_regs) {
149 if (reg.AsArm().IsCoreRegister()) {
150 core_spill_mask |= 1u << reg.AsArm().AsCoreRegister();
151 } else {
152 fp_spill_mask |= 1u << reg.AsArm().AsSRegister();
153 }
154 }
155
156 // Pop LR to PC unless we need to emit some read barrier code just before returning.
157 bool emit_code_before_return =
158 (kEmitCompilerReadBarrier && kUseBakerReadBarrier) &&
159 (may_suspend || (kIsDebugBuild && emit_run_time_checks_in_debug_mode_));
160 if ((core_spill_mask & (1u << lr.GetCode())) != 0u && !emit_code_before_return) {
161 DCHECK_EQ(core_spill_mask & (1u << pc.GetCode()), 0u);
162 core_spill_mask ^= (1u << lr.GetCode()) | (1u << pc.GetCode());
163 }
164
165 // If there are no FP registers to pop and we pop PC, we can avoid emitting any CFI.
166 if (fp_spill_mask == 0u && (core_spill_mask & (1u << pc.GetCode())) != 0u) {
167 if (frame_size == POPCOUNT(core_spill_mask) * kFramePointerSize) {
168 // Just pop all registers and avoid CFI.
169 ___ Pop(RegisterList(core_spill_mask));
170 return;
171 } else if (frame_size == 8u && core_spill_mask == (1u << pc.GetCode())) {
172 // Special case: One word to ignore and one to pop to PC. We are free to clobber the
173 // caller-save register r3 on return, so use a 16-bit POP instruction and avoid CFI.
174 ___ Pop(RegisterList((1u << r3.GetCode()) | (1u << pc.GetCode())));
175 return;
176 }
177 }
178
179 // We shall need to adjust CFI and restore it after the frame exit sequence.
180 cfi().RememberState();
181
182 // Decrease frame to start of callee saves.
183 size_t pop_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask);
184 CHECK_GE(frame_size, pop_values * kFramePointerSize);
185 DecreaseFrameSize(frame_size - (pop_values * kFramePointerSize)); // handles CFI as well.
186
187 // Pop FP callee saves.
188 if (fp_spill_mask != 0u) {
189 uint32_t first = CTZ(fp_spill_mask);
190 // Check that list is contiguous.
191 DCHECK_EQ(fp_spill_mask >> CTZ(fp_spill_mask), ~0u >> (32 - POPCOUNT(fp_spill_mask)));
192
193 ___ Vpop(SRegisterList(vixl32::SRegister(first), POPCOUNT(fp_spill_mask)));
194 cfi().AdjustCFAOffset(-kFramePointerSize * POPCOUNT(fp_spill_mask));
195 cfi().RestoreMany(DWARFReg(s0), fp_spill_mask);
196 }
197
198 // Pop core callee saves.
199 if (core_spill_mask != 0u) {
200 if (IsPowerOfTwo(core_spill_mask) &&
201 core_spill_mask != (1u << pc.GetCode()) &&
202 WhichPowerOf2(core_spill_mask) >= 8) {
203 // FIXME(vixl): vixl fails to transform a pop with single high register
204 // to a post-index STR (also known as POP encoding T3) and emits the LDMIA
205 // (also known as POP encoding T2) which is UNPREDICTABLE for 1 register.
206 // So we have to explicitly do the transformation here. Bug: 178048807
207 vixl32::Register reg(WhichPowerOf2(core_spill_mask));
208 ___ Ldr(reg, MemOperand(sp, kFramePointerSize, PostIndex));
209 } else {
210 ___ Pop(RegisterList(core_spill_mask));
211 }
212 if ((core_spill_mask & (1u << pc.GetCode())) == 0u) {
213 cfi().AdjustCFAOffset(-kFramePointerSize * POPCOUNT(core_spill_mask));
214 cfi().RestoreMany(DWARFReg(r0), core_spill_mask);
215 }
216 }
217
218 if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) {
219 if (may_suspend) {
220 // The method may be suspended; refresh the Marking Register.
221 ___ Ldr(mr, MemOperand(tr, Thread::IsGcMarkingOffset<kArmPointerSize>().Int32Value()));
222 } else {
223 // The method shall not be suspended; no need to refresh the Marking Register.
224
225 // The Marking Register is a callee-save register, and thus has been
226 // preserved by native code following the AAPCS calling convention.
227
228 // The following condition is a compile-time one, so it does not have a run-time cost.
229 if (kIsDebugBuild) {
230 // The following condition is a run-time one; it is executed after the
231 // previous compile-time test, to avoid penalizing non-debug builds.
232 if (emit_run_time_checks_in_debug_mode_) {
233 // Emit a run-time check verifying that the Marking Register is up-to-date.
234 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
235 vixl32::Register temp = temps.Acquire();
236 // Ensure we are not clobbering a callee-save register that was restored before.
237 DCHECK_EQ(core_spill_mask & (1 << temp.GetCode()), 0)
238 << "core_spill_mask hould not contain scratch register R" << temp.GetCode();
239 asm_.GenerateMarkingRegisterCheck(temp);
240 }
241 }
242 }
243 }
244
245 // Return to LR.
246 if ((core_spill_mask & (1u << pc.GetCode())) == 0u) {
247 ___ Bx(vixl32::lr);
248 }
249
250 // The CFI should be restored for any code that follows the exit block.
251 cfi().RestoreState();
252 cfi().DefCFAOffset(frame_size);
253 }
254
255
IncreaseFrameSize(size_t adjust)256 void ArmVIXLJNIMacroAssembler::IncreaseFrameSize(size_t adjust) {
257 if (adjust != 0u) {
258 asm_.AddConstant(sp, -adjust);
259 cfi().AdjustCFAOffset(adjust);
260 }
261 }
262
DecreaseFrameSize(size_t adjust)263 void ArmVIXLJNIMacroAssembler::DecreaseFrameSize(size_t adjust) {
264 if (adjust != 0u) {
265 asm_.AddConstant(sp, adjust);
266 cfi().AdjustCFAOffset(-adjust);
267 }
268 }
269
Store(FrameOffset dest,ManagedRegister m_src,size_t size)270 void ArmVIXLJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister m_src, size_t size) {
271 ArmManagedRegister src = m_src.AsArm();
272 if (src.IsNoRegister()) {
273 CHECK_EQ(0u, size);
274 } else if (src.IsCoreRegister()) {
275 CHECK_EQ(4u, size);
276 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
277 temps.Exclude(AsVIXLRegister(src));
278 asm_.StoreToOffset(kStoreWord, AsVIXLRegister(src), sp, dest.Int32Value());
279 } else if (src.IsRegisterPair()) {
280 CHECK_EQ(8u, size);
281 ___ Strd(AsVIXLRegisterPairLow(src),
282 AsVIXLRegisterPairHigh(src),
283 MemOperand(sp, dest.Int32Value()));
284 } else if (src.IsSRegister()) {
285 CHECK_EQ(4u, size);
286 asm_.StoreSToOffset(AsVIXLSRegister(src), sp, dest.Int32Value());
287 } else {
288 CHECK_EQ(8u, size);
289 CHECK(src.IsDRegister()) << src;
290 asm_.StoreDToOffset(AsVIXLDRegister(src), sp, dest.Int32Value());
291 }
292 }
293
StoreRef(FrameOffset dest,ManagedRegister msrc)294 void ArmVIXLJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
295 vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
296 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
297 temps.Exclude(src);
298 asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
299 }
300
StoreRawPtr(FrameOffset dest,ManagedRegister msrc)301 void ArmVIXLJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
302 vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
303 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
304 temps.Exclude(src);
305 asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
306 }
307
StoreSpanning(FrameOffset dest,ManagedRegister msrc,FrameOffset in_off)308 void ArmVIXLJNIMacroAssembler::StoreSpanning(FrameOffset dest,
309 ManagedRegister msrc,
310 FrameOffset in_off) {
311 vixl::aarch32::Register src = AsVIXLRegister(msrc.AsArm());
312 asm_.StoreToOffset(kStoreWord, src, sp, dest.Int32Value());
313 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
314 vixl32::Register scratch = temps.Acquire();
315 asm_.LoadFromOffset(kLoadWord, scratch, sp, in_off.Int32Value());
316 asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4);
317 }
318
CopyRef(FrameOffset dest,FrameOffset src)319 void ArmVIXLJNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src) {
320 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
321 vixl32::Register scratch = temps.Acquire();
322 asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
323 asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
324 }
325
CopyRef(FrameOffset dest,ManagedRegister base,MemberOffset offs,bool unpoison_reference)326 void ArmVIXLJNIMacroAssembler::CopyRef(FrameOffset dest,
327 ManagedRegister base,
328 MemberOffset offs,
329 bool unpoison_reference) {
330 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
331 vixl32::Register scratch = temps.Acquire();
332 asm_.LoadFromOffset(kLoadWord, scratch, AsVIXLRegister(base.AsArm()), offs.Int32Value());
333 if (unpoison_reference) {
334 asm_.MaybeUnpoisonHeapReference(scratch);
335 }
336 asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
337 }
338
LoadRef(ManagedRegister mdest,ManagedRegister mbase,MemberOffset offs,bool unpoison_reference)339 void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister mdest,
340 ManagedRegister mbase,
341 MemberOffset offs,
342 bool unpoison_reference) {
343 vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm());
344 vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm());
345 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
346 temps.Exclude(dest, base);
347 asm_.LoadFromOffset(kLoadWord, dest, base, offs.Int32Value());
348
349 if (unpoison_reference) {
350 asm_.MaybeUnpoisonHeapReference(dest);
351 }
352 }
353
LoadRef(ManagedRegister dest ATTRIBUTE_UNUSED,FrameOffset src ATTRIBUTE_UNUSED)354 void ArmVIXLJNIMacroAssembler::LoadRef(ManagedRegister dest ATTRIBUTE_UNUSED,
355 FrameOffset src ATTRIBUTE_UNUSED) {
356 UNIMPLEMENTED(FATAL);
357 }
358
LoadRawPtr(ManagedRegister dest ATTRIBUTE_UNUSED,ManagedRegister base ATTRIBUTE_UNUSED,Offset offs ATTRIBUTE_UNUSED)359 void ArmVIXLJNIMacroAssembler::LoadRawPtr(ManagedRegister dest ATTRIBUTE_UNUSED,
360 ManagedRegister base ATTRIBUTE_UNUSED,
361 Offset offs ATTRIBUTE_UNUSED) {
362 UNIMPLEMENTED(FATAL);
363 }
364
StoreImmediateToFrame(FrameOffset dest,uint32_t imm)365 void ArmVIXLJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm) {
366 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
367 vixl32::Register scratch = temps.Acquire();
368 asm_.LoadImmediate(scratch, imm);
369 asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
370 }
371
Load(ManagedRegister m_dst,FrameOffset src,size_t size)372 void ArmVIXLJNIMacroAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) {
373 return Load(m_dst.AsArm(), sp, src.Int32Value(), size);
374 }
375
LoadFromThread(ManagedRegister m_dst,ThreadOffset32 src,size_t size)376 void ArmVIXLJNIMacroAssembler::LoadFromThread(ManagedRegister m_dst,
377 ThreadOffset32 src,
378 size_t size) {
379 return Load(m_dst.AsArm(), tr, src.Int32Value(), size);
380 }
381
LoadRawPtrFromThread(ManagedRegister mdest,ThreadOffset32 offs)382 void ArmVIXLJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister mdest, ThreadOffset32 offs) {
383 vixl::aarch32::Register dest = AsVIXLRegister(mdest.AsArm());
384 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
385 temps.Exclude(dest);
386 asm_.LoadFromOffset(kLoadWord, dest, tr, offs.Int32Value());
387 }
388
CopyRawPtrFromThread(FrameOffset fr_offs,ThreadOffset32 thr_offs)389 void ArmVIXLJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs, ThreadOffset32 thr_offs) {
390 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
391 vixl32::Register scratch = temps.Acquire();
392 asm_.LoadFromOffset(kLoadWord, scratch, tr, thr_offs.Int32Value());
393 asm_.StoreToOffset(kStoreWord, scratch, sp, fr_offs.Int32Value());
394 }
395
CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIBUTE_UNUSED,FrameOffset fr_offs ATTRIBUTE_UNUSED,ManagedRegister mscratch ATTRIBUTE_UNUSED)396 void ArmVIXLJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs ATTRIBUTE_UNUSED,
397 FrameOffset fr_offs ATTRIBUTE_UNUSED,
398 ManagedRegister mscratch ATTRIBUTE_UNUSED) {
399 UNIMPLEMENTED(FATAL);
400 }
401
StoreStackOffsetToThread(ThreadOffset32 thr_offs,FrameOffset fr_offs)402 void ArmVIXLJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
403 FrameOffset fr_offs) {
404 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
405 vixl32::Register scratch = temps.Acquire();
406 asm_.AddConstant(scratch, sp, fr_offs.Int32Value());
407 asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value());
408 }
409
StoreStackPointerToThread(ThreadOffset32 thr_offs)410 void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
411 asm_.StoreToOffset(kStoreWord, sp, tr, thr_offs.Int32Value());
412 }
413
SignExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)414 void ArmVIXLJNIMacroAssembler::SignExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,
415 size_t size ATTRIBUTE_UNUSED) {
416 UNIMPLEMENTED(FATAL) << "no sign extension necessary for arm";
417 }
418
ZeroExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)419 void ArmVIXLJNIMacroAssembler::ZeroExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,
420 size_t size ATTRIBUTE_UNUSED) {
421 UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm";
422 }
423
IsCoreRegisterOrPair(ArmManagedRegister reg)424 static inline bool IsCoreRegisterOrPair(ArmManagedRegister reg) {
425 return reg.IsCoreRegister() || reg.IsRegisterPair();
426 }
427
NoSpillGap(const ArgumentLocation & loc1,const ArgumentLocation & loc2)428 static inline bool NoSpillGap(const ArgumentLocation& loc1, const ArgumentLocation& loc2) {
429 DCHECK(!loc1.IsRegister());
430 DCHECK(!loc2.IsRegister());
431 uint32_t loc1_offset = loc1.GetFrameOffset().Uint32Value();
432 uint32_t loc2_offset = loc2.GetFrameOffset().Uint32Value();
433 DCHECK_LT(loc1_offset, loc2_offset);
434 return loc1_offset + loc1.GetSize() == loc2_offset;
435 }
436
GetSRegisterNumber(ArmManagedRegister reg)437 static inline uint32_t GetSRegisterNumber(ArmManagedRegister reg) {
438 if (reg.IsSRegister()) {
439 return static_cast<uint32_t>(reg.AsSRegister());
440 } else {
441 DCHECK(reg.IsDRegister());
442 return 2u * static_cast<uint32_t>(reg.AsDRegister());
443 }
444 }
445
446 // Get the number of locations to spill together.
GetSpillChunkSize(ArrayRef<ArgumentLocation> dests,ArrayRef<ArgumentLocation> srcs,size_t start,bool have_extra_temp)447 static inline size_t GetSpillChunkSize(ArrayRef<ArgumentLocation> dests,
448 ArrayRef<ArgumentLocation> srcs,
449 size_t start,
450 bool have_extra_temp) {
451 DCHECK_LT(start, dests.size());
452 DCHECK_ALIGNED(dests[start].GetFrameOffset().Uint32Value(), 4u);
453 const ArgumentLocation& first_src = srcs[start];
454 if (!first_src.IsRegister()) {
455 DCHECK_ALIGNED(first_src.GetFrameOffset().Uint32Value(), 4u);
456 // If we have an extra temporary, look for opportunities to move 2 words
457 // at a time with LDRD/STRD when the source types are word-sized.
458 if (have_extra_temp &&
459 start + 1u != dests.size() &&
460 !srcs[start + 1u].IsRegister() &&
461 first_src.GetSize() == 4u &&
462 srcs[start + 1u].GetSize() == 4u &&
463 NoSpillGap(first_src, srcs[start + 1u]) &&
464 NoSpillGap(dests[start], dests[start + 1u]) &&
465 dests[start].GetFrameOffset().Uint32Value() < kStrdOffsetCutoff) {
466 // Note: The source and destination may not be 8B aligned (but they are 4B aligned).
467 return 2u;
468 }
469 return 1u;
470 }
471 ArmManagedRegister first_src_reg = first_src.GetRegister().AsArm();
472 size_t end = start + 1u;
473 if (IsCoreRegisterOrPair(first_src_reg)) {
474 while (end != dests.size() &&
475 NoSpillGap(dests[end - 1u], dests[end]) &&
476 srcs[end].IsRegister() &&
477 IsCoreRegisterOrPair(srcs[end].GetRegister().AsArm())) {
478 ++end;
479 }
480 } else {
481 DCHECK(first_src_reg.IsSRegister() || first_src_reg.IsDRegister());
482 uint32_t next_sreg = GetSRegisterNumber(first_src_reg) + first_src.GetSize() / kSRegSizeInBytes;
483 while (end != dests.size() &&
484 NoSpillGap(dests[end - 1u], dests[end]) &&
485 srcs[end].IsRegister() &&
486 !IsCoreRegisterOrPair(srcs[end].GetRegister().AsArm()) &&
487 GetSRegisterNumber(srcs[end].GetRegister().AsArm()) == next_sreg) {
488 next_sreg += srcs[end].GetSize() / kSRegSizeInBytes;
489 ++end;
490 }
491 }
492 return end - start;
493 }
494
GetCoreRegisterMask(ArmManagedRegister reg)495 static inline uint32_t GetCoreRegisterMask(ArmManagedRegister reg) {
496 if (reg.IsCoreRegister()) {
497 return 1u << static_cast<size_t>(reg.AsCoreRegister());
498 } else {
499 DCHECK(reg.IsRegisterPair());
500 DCHECK_LT(reg.AsRegisterPairLow(), reg.AsRegisterPairHigh());
501 return (1u << static_cast<size_t>(reg.AsRegisterPairLow())) |
502 (1u << static_cast<size_t>(reg.AsRegisterPairHigh()));
503 }
504 }
505
GetCoreRegisterMask(ArrayRef<ArgumentLocation> srcs)506 static inline uint32_t GetCoreRegisterMask(ArrayRef<ArgumentLocation> srcs) {
507 uint32_t mask = 0u;
508 for (const ArgumentLocation& loc : srcs) {
509 DCHECK(loc.IsRegister());
510 mask |= GetCoreRegisterMask(loc.GetRegister().AsArm());
511 }
512 return mask;
513 }
514
UseStrdForChunk(ArrayRef<ArgumentLocation> srcs,size_t start,size_t length)515 static inline bool UseStrdForChunk(ArrayRef<ArgumentLocation> srcs, size_t start, size_t length) {
516 DCHECK_GE(length, 2u);
517 DCHECK(srcs[start].IsRegister());
518 DCHECK(srcs[start + 1u].IsRegister());
519 // The destination may not be 8B aligned (but it is 4B aligned).
520 // Allow arbitrary destination offset, macro assembler will use a temp if needed.
521 // Note: T32 allows unrelated registers in STRD. (A32 does not.)
522 return length == 2u &&
523 srcs[start].GetRegister().AsArm().IsCoreRegister() &&
524 srcs[start + 1u].GetRegister().AsArm().IsCoreRegister();
525 }
526
UseVstrForChunk(ArrayRef<ArgumentLocation> srcs,size_t start,size_t length)527 static inline bool UseVstrForChunk(ArrayRef<ArgumentLocation> srcs, size_t start, size_t length) {
528 DCHECK_GE(length, 2u);
529 DCHECK(srcs[start].IsRegister());
530 DCHECK(srcs[start + 1u].IsRegister());
531 // The destination may not be 8B aligned (but it is 4B aligned).
532 // Allow arbitrary destination offset, macro assembler will use a temp if needed.
533 return length == 2u &&
534 srcs[start].GetRegister().AsArm().IsSRegister() &&
535 srcs[start + 1u].GetRegister().AsArm().IsSRegister() &&
536 IsAligned<2u>(static_cast<size_t>(srcs[start].GetRegister().AsArm().AsSRegister()));
537 }
538
MoveArguments(ArrayRef<ArgumentLocation> dests,ArrayRef<ArgumentLocation> srcs)539 void ArmVIXLJNIMacroAssembler::MoveArguments(ArrayRef<ArgumentLocation> dests,
540 ArrayRef<ArgumentLocation> srcs) {
541 DCHECK_EQ(dests.size(), srcs.size());
542
543 // Native ABI is soft-float, so all destinations should be core registers or stack offsets.
544 // And register locations should be first, followed by stack locations with increasing offset.
545 auto is_register = [](const ArgumentLocation& loc) { return loc.IsRegister(); };
546 DCHECK(std::is_partitioned(dests.begin(), dests.end(), is_register));
547 size_t num_reg_dests =
548 std::distance(dests.begin(), std::partition_point(dests.begin(), dests.end(), is_register));
549 DCHECK(std::is_sorted(
550 dests.begin() + num_reg_dests,
551 dests.end(),
552 [](const ArgumentLocation& lhs, const ArgumentLocation& rhs) {
553 return lhs.GetFrameOffset().Uint32Value() < rhs.GetFrameOffset().Uint32Value();
554 }));
555
556 // Collect registers to move. No need to record FP regs as destinations are only core regs.
557 uint32_t src_regs = 0u;
558 uint32_t dest_regs = 0u;
559 for (size_t i = 0; i != num_reg_dests; ++i) {
560 const ArgumentLocation& src = srcs[i];
561 const ArgumentLocation& dest = dests[i];
562 DCHECK(dest.IsRegister() && IsCoreRegisterOrPair(dest.GetRegister().AsArm()));
563 if (src.IsRegister() && IsCoreRegisterOrPair(src.GetRegister().AsArm())) {
564 if (src.GetRegister().Equals(dest.GetRegister())) {
565 continue;
566 }
567 src_regs |= GetCoreRegisterMask(src.GetRegister().AsArm());
568 }
569 dest_regs |= GetCoreRegisterMask(dest.GetRegister().AsArm());
570 }
571
572 // Spill args first. Look for opportunities to spill multiple arguments at once.
573 {
574 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
575 vixl32::Register xtemp; // Extra temp register;
576 if ((dest_regs & ~src_regs) != 0u) {
577 xtemp = vixl32::Register(CTZ(dest_regs & ~src_regs));
578 DCHECK(!temps.IsAvailable(xtemp));
579 }
580 auto move_two_words = [&](FrameOffset dest_offset, FrameOffset src_offset) {
581 DCHECK(xtemp.IsValid());
582 DCHECK_LT(dest_offset.Uint32Value(), kStrdOffsetCutoff);
583 // VIXL macro assembler can use destination registers for loads from large offsets.
584 UseScratchRegisterScope temps2(asm_.GetVIXLAssembler());
585 vixl32::Register temp2 = temps2.Acquire();
586 ___ Ldrd(xtemp, temp2, MemOperand(sp, src_offset.Uint32Value()));
587 ___ Strd(xtemp, temp2, MemOperand(sp, dest_offset.Uint32Value()));
588 };
589 for (size_t i = num_reg_dests, arg_count = dests.size(); i != arg_count; ) {
590 const ArgumentLocation& src = srcs[i];
591 const ArgumentLocation& dest = dests[i];
592 DCHECK_EQ(src.GetSize(), dest.GetSize());
593 DCHECK(!dest.IsRegister());
594 uint32_t frame_offset = dest.GetFrameOffset().Uint32Value();
595 size_t chunk_size = GetSpillChunkSize(dests, srcs, i, xtemp.IsValid());
596 DCHECK_NE(chunk_size, 0u);
597 if (chunk_size == 1u) {
598 if (src.IsRegister()) {
599 Store(dest.GetFrameOffset(), src.GetRegister(), dest.GetSize());
600 } else if (dest.GetSize() == 8u && xtemp.IsValid() && frame_offset < kStrdOffsetCutoff) {
601 move_two_words(dest.GetFrameOffset(), src.GetFrameOffset());
602 } else {
603 Copy(dest.GetFrameOffset(), src.GetFrameOffset(), dest.GetSize());
604 }
605 } else if (!src.IsRegister()) {
606 DCHECK_EQ(chunk_size, 2u);
607 DCHECK_EQ(dest.GetSize(), 4u);
608 DCHECK_EQ(dests[i + 1u].GetSize(), 4u);
609 move_two_words(dest.GetFrameOffset(), src.GetFrameOffset());
610 } else if (UseStrdForChunk(srcs, i, chunk_size)) {
611 ___ Strd(AsVIXLRegister(srcs[i].GetRegister().AsArm()),
612 AsVIXLRegister(srcs[i + 1u].GetRegister().AsArm()),
613 MemOperand(sp, frame_offset));
614 } else if (UseVstrForChunk(srcs, i, chunk_size)) {
615 size_t sreg = GetSRegisterNumber(src.GetRegister().AsArm());
616 DCHECK_ALIGNED(sreg, 2u);
617 ___ Vstr(vixl32::DRegister(sreg / 2u), MemOperand(sp, frame_offset));
618 } else {
619 UseScratchRegisterScope temps2(asm_.GetVIXLAssembler());
620 vixl32::Register base_reg;
621 if (frame_offset == 0u) {
622 base_reg = sp;
623 } else {
624 base_reg = temps2.Acquire();
625 ___ Add(base_reg, sp, frame_offset);
626 }
627
628 ArmManagedRegister src_reg = src.GetRegister().AsArm();
629 if (IsCoreRegisterOrPair(src_reg)) {
630 uint32_t core_reg_mask = GetCoreRegisterMask(srcs.SubArray(i, chunk_size));
631 ___ Stm(base_reg, NO_WRITE_BACK, RegisterList(core_reg_mask));
632 } else {
633 uint32_t start_sreg = GetSRegisterNumber(src_reg);
634 const ArgumentLocation& last_dest = dests[i + chunk_size - 1u];
635 uint32_t total_size =
636 last_dest.GetFrameOffset().Uint32Value() + last_dest.GetSize() - frame_offset;
637 if (IsAligned<2u>(start_sreg) &&
638 IsAligned<kDRegSizeInBytes>(frame_offset) &&
639 IsAligned<kDRegSizeInBytes>(total_size)) {
640 uint32_t dreg_count = total_size / kDRegSizeInBytes;
641 DRegisterList dreg_list(vixl32::DRegister(start_sreg / 2u), dreg_count);
642 ___ Vstm(F64, base_reg, NO_WRITE_BACK, dreg_list);
643 } else {
644 uint32_t sreg_count = total_size / kSRegSizeInBytes;
645 SRegisterList sreg_list(vixl32::SRegister(start_sreg), sreg_count);
646 ___ Vstm(F32, base_reg, NO_WRITE_BACK, sreg_list);
647 }
648 }
649 }
650 i += chunk_size;
651 }
652 }
653
654 // Fill destination registers from source core registers.
655 // There should be no cycles, so this algorithm should make progress.
656 while (src_regs != 0u) {
657 uint32_t old_src_regs = src_regs;
658 for (size_t i = 0; i != num_reg_dests; ++i) {
659 DCHECK(dests[i].IsRegister() && IsCoreRegisterOrPair(dests[i].GetRegister().AsArm()));
660 if (!srcs[i].IsRegister() || !IsCoreRegisterOrPair(srcs[i].GetRegister().AsArm())) {
661 continue;
662 }
663 uint32_t dest_reg_mask = GetCoreRegisterMask(dests[i].GetRegister().AsArm());
664 if ((dest_reg_mask & dest_regs) == 0u) {
665 continue; // Equals source, or already filled in one of previous iterations.
666 }
667 // There are no partial overlaps of 8-byte arguments, otherwise we would have to
668 // tweak this check; Move() can deal with partial overlap for historical reasons.
669 if ((dest_reg_mask & src_regs) != 0u) {
670 continue; // Cannot clobber this register yet.
671 }
672 Move(dests[i].GetRegister(), srcs[i].GetRegister(), dests[i].GetSize());
673 uint32_t src_reg_mask = GetCoreRegisterMask(srcs[i].GetRegister().AsArm());
674 DCHECK_EQ(src_regs & src_reg_mask, src_reg_mask);
675 src_regs &= ~src_reg_mask; // Allow clobbering the source register or pair.
676 dest_regs &= ~dest_reg_mask; // Destination register or pair was filled.
677 }
678 CHECK_NE(old_src_regs, src_regs);
679 DCHECK_EQ(0u, src_regs & ~old_src_regs);
680 }
681
682 // Now fill destination registers from FP registers or stack slots, looking for
683 // opportunities to use LDRD/VMOV to fill 2 registers with one instruction.
684 for (size_t i = 0, j; i != num_reg_dests; i = j) {
685 j = i + 1u;
686 DCHECK(dests[i].IsRegister() && IsCoreRegisterOrPair(dests[i].GetRegister().AsArm()));
687 if (srcs[i].IsRegister() && IsCoreRegisterOrPair(srcs[i].GetRegister().AsArm())) {
688 DCHECK_EQ(GetCoreRegisterMask(dests[i].GetRegister().AsArm()) & dest_regs, 0u);
689 continue; // Equals destination or moved above.
690 }
691 DCHECK_NE(GetCoreRegisterMask(dests[i].GetRegister().AsArm()) & dest_regs, 0u);
692 if (dests[i].GetSize() == 4u) {
693 // Find next register to load.
694 while (j != num_reg_dests &&
695 (srcs[j].IsRegister() && IsCoreRegisterOrPair(srcs[j].GetRegister().AsArm()))) {
696 DCHECK_EQ(GetCoreRegisterMask(dests[j].GetRegister().AsArm()) & dest_regs, 0u);
697 ++j; // Equals destination or moved above.
698 }
699 if (j != num_reg_dests && dests[j].GetSize() == 4u) {
700 if (!srcs[i].IsRegister() && !srcs[j].IsRegister() && NoSpillGap(srcs[i], srcs[j])) {
701 ___ Ldrd(AsVIXLRegister(dests[i].GetRegister().AsArm()),
702 AsVIXLRegister(dests[j].GetRegister().AsArm()),
703 MemOperand(sp, srcs[i].GetFrameOffset().Uint32Value()));
704 ++j;
705 continue;
706 }
707 if (srcs[i].IsRegister() && srcs[j].IsRegister()) {
708 uint32_t first_sreg = GetSRegisterNumber(srcs[i].GetRegister().AsArm());
709 if (IsAligned<2u>(first_sreg) &&
710 first_sreg + 1u == GetSRegisterNumber(srcs[j].GetRegister().AsArm())) {
711 ___ Vmov(AsVIXLRegister(dests[i].GetRegister().AsArm()),
712 AsVIXLRegister(dests[j].GetRegister().AsArm()),
713 vixl32::DRegister(first_sreg / 2u));
714 ++j;
715 continue;
716 }
717 }
718 }
719 }
720 if (srcs[i].IsRegister()) {
721 Move(dests[i].GetRegister(), srcs[i].GetRegister(), dests[i].GetSize());
722 } else {
723 Load(dests[i].GetRegister(), srcs[i].GetFrameOffset(), dests[i].GetSize());
724 }
725 }
726 }
727
Move(ManagedRegister mdst,ManagedRegister msrc,size_t size ATTRIBUTE_UNUSED)728 void ArmVIXLJNIMacroAssembler::Move(ManagedRegister mdst,
729 ManagedRegister msrc,
730 size_t size ATTRIBUTE_UNUSED) {
731 ArmManagedRegister dst = mdst.AsArm();
732 if (kIsDebugBuild) {
733 // Check that the destination is not a scratch register.
734 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
735 if (dst.IsCoreRegister()) {
736 CHECK(!temps.IsAvailable(AsVIXLRegister(dst)));
737 } else if (dst.IsDRegister()) {
738 CHECK(!temps.IsAvailable(AsVIXLDRegister(dst)));
739 } else if (dst.IsSRegister()) {
740 CHECK(!temps.IsAvailable(AsVIXLSRegister(dst)));
741 } else {
742 CHECK(dst.IsRegisterPair()) << dst;
743 CHECK(!temps.IsAvailable(AsVIXLRegisterPairLow(dst)));
744 CHECK(!temps.IsAvailable(AsVIXLRegisterPairHigh(dst)));
745 }
746 }
747 ArmManagedRegister src = msrc.AsArm();
748 if (!dst.Equals(src)) {
749 if (dst.IsCoreRegister()) {
750 if (src.IsCoreRegister()) {
751 ___ Mov(AsVIXLRegister(dst), AsVIXLRegister(src));
752 } else {
753 CHECK(src.IsSRegister()) << src;
754 ___ Vmov(AsVIXLRegister(dst), AsVIXLSRegister(src));
755 }
756 } else if (dst.IsDRegister()) {
757 if (src.IsDRegister()) {
758 ___ Vmov(F64, AsVIXLDRegister(dst), AsVIXLDRegister(src));
759 } else {
760 // VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi})
761 CHECK(src.IsRegisterPair()) << src;
762 ___ Vmov(AsVIXLDRegister(dst), AsVIXLRegisterPairLow(src), AsVIXLRegisterPairHigh(src));
763 }
764 } else if (dst.IsSRegister()) {
765 if (src.IsSRegister()) {
766 ___ Vmov(F32, AsVIXLSRegister(dst), AsVIXLSRegister(src));
767 } else {
768 // VMOV Sn, Rn (Sn = Rn)
769 CHECK(src.IsCoreRegister()) << src;
770 ___ Vmov(AsVIXLSRegister(dst), AsVIXLRegister(src));
771 }
772 } else {
773 CHECK(dst.IsRegisterPair()) << dst;
774 if (src.IsRegisterPair()) {
775 // Ensure that the first move doesn't clobber the input of the second.
776 if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) {
777 ___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src));
778 ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src));
779 } else {
780 ___ Mov(AsVIXLRegisterPairHigh(dst), AsVIXLRegisterPairHigh(src));
781 ___ Mov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairLow(src));
782 }
783 } else {
784 CHECK(src.IsDRegister()) << src;
785 ___ Vmov(AsVIXLRegisterPairLow(dst), AsVIXLRegisterPairHigh(dst), AsVIXLDRegister(src));
786 }
787 }
788 }
789 }
790
Copy(FrameOffset dest,FrameOffset src,size_t size)791 void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest, FrameOffset src, size_t size) {
792 DCHECK(size == 4 || size == 8) << size;
793 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
794 vixl32::Register scratch = temps.Acquire();
795 if (size == 4) {
796 asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
797 asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
798 } else if (size == 8) {
799 asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value());
800 asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value());
801 asm_.LoadFromOffset(kLoadWord, scratch, sp, src.Int32Value() + 4);
802 asm_.StoreToOffset(kStoreWord, scratch, sp, dest.Int32Value() + 4);
803 }
804 }
805
Copy(FrameOffset dest ATTRIBUTE_UNUSED,ManagedRegister src_base ATTRIBUTE_UNUSED,Offset src_offset ATTRIBUTE_UNUSED,ManagedRegister mscratch ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)806 void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dest ATTRIBUTE_UNUSED,
807 ManagedRegister src_base ATTRIBUTE_UNUSED,
808 Offset src_offset ATTRIBUTE_UNUSED,
809 ManagedRegister mscratch ATTRIBUTE_UNUSED,
810 size_t size ATTRIBUTE_UNUSED) {
811 UNIMPLEMENTED(FATAL);
812 }
813
Copy(ManagedRegister dest_base ATTRIBUTE_UNUSED,Offset dest_offset ATTRIBUTE_UNUSED,FrameOffset src ATTRIBUTE_UNUSED,ManagedRegister mscratch ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)814 void ArmVIXLJNIMacroAssembler::Copy(ManagedRegister dest_base ATTRIBUTE_UNUSED,
815 Offset dest_offset ATTRIBUTE_UNUSED,
816 FrameOffset src ATTRIBUTE_UNUSED,
817 ManagedRegister mscratch ATTRIBUTE_UNUSED,
818 size_t size ATTRIBUTE_UNUSED) {
819 UNIMPLEMENTED(FATAL);
820 }
821
Copy(FrameOffset dst ATTRIBUTE_UNUSED,FrameOffset src_base ATTRIBUTE_UNUSED,Offset src_offset ATTRIBUTE_UNUSED,ManagedRegister mscratch ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)822 void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dst ATTRIBUTE_UNUSED,
823 FrameOffset src_base ATTRIBUTE_UNUSED,
824 Offset src_offset ATTRIBUTE_UNUSED,
825 ManagedRegister mscratch ATTRIBUTE_UNUSED,
826 size_t size ATTRIBUTE_UNUSED) {
827 UNIMPLEMENTED(FATAL);
828 }
829
Copy(ManagedRegister dest ATTRIBUTE_UNUSED,Offset dest_offset ATTRIBUTE_UNUSED,ManagedRegister src ATTRIBUTE_UNUSED,Offset src_offset ATTRIBUTE_UNUSED,ManagedRegister mscratch ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)830 void ArmVIXLJNIMacroAssembler::Copy(ManagedRegister dest ATTRIBUTE_UNUSED,
831 Offset dest_offset ATTRIBUTE_UNUSED,
832 ManagedRegister src ATTRIBUTE_UNUSED,
833 Offset src_offset ATTRIBUTE_UNUSED,
834 ManagedRegister mscratch ATTRIBUTE_UNUSED,
835 size_t size ATTRIBUTE_UNUSED) {
836 UNIMPLEMENTED(FATAL);
837 }
838
Copy(FrameOffset dst ATTRIBUTE_UNUSED,Offset dest_offset ATTRIBUTE_UNUSED,FrameOffset src ATTRIBUTE_UNUSED,Offset src_offset ATTRIBUTE_UNUSED,ManagedRegister scratch ATTRIBUTE_UNUSED,size_t size ATTRIBUTE_UNUSED)839 void ArmVIXLJNIMacroAssembler::Copy(FrameOffset dst ATTRIBUTE_UNUSED,
840 Offset dest_offset ATTRIBUTE_UNUSED,
841 FrameOffset src ATTRIBUTE_UNUSED,
842 Offset src_offset ATTRIBUTE_UNUSED,
843 ManagedRegister scratch ATTRIBUTE_UNUSED,
844 size_t size ATTRIBUTE_UNUSED) {
845 UNIMPLEMENTED(FATAL);
846 }
847
CreateJObject(ManagedRegister mout_reg,FrameOffset spilled_reference_offset,ManagedRegister min_reg,bool null_allowed)848 void ArmVIXLJNIMacroAssembler::CreateJObject(ManagedRegister mout_reg,
849 FrameOffset spilled_reference_offset,
850 ManagedRegister min_reg,
851 bool null_allowed) {
852 vixl::aarch32::Register out_reg = AsVIXLRegister(mout_reg.AsArm());
853 vixl::aarch32::Register in_reg =
854 min_reg.AsArm().IsNoRegister() ? vixl::aarch32::Register() : AsVIXLRegister(min_reg.AsArm());
855 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
856 temps.Exclude(out_reg);
857 if (null_allowed) {
858 // Null values get a jobject value null. Otherwise, the jobject is
859 // the address of the spilled reference.
860 // e.g. out_reg = (handle == 0) ? 0 : (SP+spilled_reference_offset)
861 if (!in_reg.IsValid()) {
862 asm_.LoadFromOffset(kLoadWord, out_reg, sp, spilled_reference_offset.Int32Value());
863 in_reg = out_reg;
864 }
865
866 temps.Exclude(in_reg);
867 ___ Cmp(in_reg, 0);
868
869 if (asm_.ShifterOperandCanHold(ADD, spilled_reference_offset.Int32Value())) {
870 if (!out_reg.Is(in_reg)) {
871 ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
872 3 * vixl32::kMaxInstructionSizeInBytes,
873 CodeBufferCheckScope::kMaximumSize);
874 ___ it(eq, 0xc);
875 ___ mov(eq, out_reg, 0);
876 asm_.AddConstantInIt(out_reg, sp, spilled_reference_offset.Int32Value(), ne);
877 } else {
878 ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
879 2 * vixl32::kMaxInstructionSizeInBytes,
880 CodeBufferCheckScope::kMaximumSize);
881 ___ it(ne, 0x8);
882 asm_.AddConstantInIt(out_reg, sp, spilled_reference_offset.Int32Value(), ne);
883 }
884 } else {
885 // TODO: Implement this (old arm assembler would have crashed here).
886 UNIMPLEMENTED(FATAL);
887 }
888 } else {
889 asm_.AddConstant(out_reg, sp, spilled_reference_offset.Int32Value());
890 }
891 }
892
CreateJObject(FrameOffset out_off,FrameOffset spilled_reference_offset,bool null_allowed)893 void ArmVIXLJNIMacroAssembler::CreateJObject(FrameOffset out_off,
894 FrameOffset spilled_reference_offset,
895 bool null_allowed) {
896 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
897 vixl32::Register scratch = temps.Acquire();
898 if (null_allowed) {
899 asm_.LoadFromOffset(kLoadWord, scratch, sp, spilled_reference_offset.Int32Value());
900 // Null values get a jobject value null. Otherwise, the jobject is
901 // the address of the spilled reference.
902 // e.g. scratch = (scratch == 0) ? 0 : (SP+spilled_reference_offset)
903 ___ Cmp(scratch, 0);
904
905 if (asm_.ShifterOperandCanHold(ADD, spilled_reference_offset.Int32Value())) {
906 ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
907 2 * vixl32::kMaxInstructionSizeInBytes,
908 CodeBufferCheckScope::kMaximumSize);
909 ___ it(ne, 0x8);
910 asm_.AddConstantInIt(scratch, sp, spilled_reference_offset.Int32Value(), ne);
911 } else {
912 // TODO: Implement this (old arm assembler would have crashed here).
913 UNIMPLEMENTED(FATAL);
914 }
915 } else {
916 asm_.AddConstant(scratch, sp, spilled_reference_offset.Int32Value());
917 }
918 asm_.StoreToOffset(kStoreWord, scratch, sp, out_off.Int32Value());
919 }
920
VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,bool could_be_null ATTRIBUTE_UNUSED)921 void ArmVIXLJNIMacroAssembler::VerifyObject(ManagedRegister src ATTRIBUTE_UNUSED,
922 bool could_be_null ATTRIBUTE_UNUSED) {
923 // TODO: not validating references.
924 }
925
VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,bool could_be_null ATTRIBUTE_UNUSED)926 void ArmVIXLJNIMacroAssembler::VerifyObject(FrameOffset src ATTRIBUTE_UNUSED,
927 bool could_be_null ATTRIBUTE_UNUSED) {
928 // TODO: not validating references.
929 }
930
Jump(ManagedRegister mbase,Offset offset)931 void ArmVIXLJNIMacroAssembler::Jump(ManagedRegister mbase, Offset offset) {
932 vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm());
933 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
934 vixl32::Register scratch = temps.Acquire();
935 asm_.LoadFromOffset(kLoadWord, scratch, base, offset.Int32Value());
936 ___ Bx(scratch);
937 }
938
Call(ManagedRegister mbase,Offset offset)939 void ArmVIXLJNIMacroAssembler::Call(ManagedRegister mbase, Offset offset) {
940 vixl::aarch32::Register base = AsVIXLRegister(mbase.AsArm());
941 asm_.LoadFromOffset(kLoadWord, lr, base, offset.Int32Value());
942 ___ Blx(lr);
943 // TODO: place reference map on call.
944 }
945
Call(FrameOffset base,Offset offset)946 void ArmVIXLJNIMacroAssembler::Call(FrameOffset base, Offset offset) {
947 // Call *(*(SP + base) + offset)
948 asm_.LoadFromOffset(kLoadWord, lr, sp, base.Int32Value());
949 asm_.LoadFromOffset(kLoadWord, lr, lr, offset.Int32Value());
950 ___ Blx(lr);
951 // TODO: place reference map on call
952 }
953
CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED)954 void ArmVIXLJNIMacroAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED) {
955 UNIMPLEMENTED(FATAL);
956 }
957
GetCurrentThread(ManagedRegister dest)958 void ArmVIXLJNIMacroAssembler::GetCurrentThread(ManagedRegister dest) {
959 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
960 temps.Exclude(AsVIXLRegister(dest.AsArm()));
961 ___ Mov(AsVIXLRegister(dest.AsArm()), tr);
962 }
963
GetCurrentThread(FrameOffset dest_offset)964 void ArmVIXLJNIMacroAssembler::GetCurrentThread(FrameOffset dest_offset) {
965 asm_.StoreToOffset(kStoreWord, tr, sp, dest_offset.Int32Value());
966 }
967
ExceptionPoll(size_t stack_adjust)968 void ArmVIXLJNIMacroAssembler::ExceptionPoll(size_t stack_adjust) {
969 CHECK_ALIGNED(stack_adjust, kAapcsStackAlignment);
970 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
971 vixl32::Register scratch = temps.Acquire();
972 exception_blocks_.emplace_back(
973 new ArmVIXLJNIMacroAssembler::ArmException(scratch, stack_adjust));
974 asm_.LoadFromOffset(kLoadWord,
975 scratch,
976 tr,
977 Thread::ExceptionOffset<kArmPointerSize>().Int32Value());
978
979 ___ Cmp(scratch, 0);
980 vixl32::Label* label = exception_blocks_.back()->Entry();
981 ___ BPreferNear(ne, label);
982 // TODO: think about using CBNZ here.
983 }
984
CreateLabel()985 std::unique_ptr<JNIMacroLabel> ArmVIXLJNIMacroAssembler::CreateLabel() {
986 return std::unique_ptr<JNIMacroLabel>(new ArmVIXLJNIMacroLabel());
987 }
988
Jump(JNIMacroLabel * label)989 void ArmVIXLJNIMacroAssembler::Jump(JNIMacroLabel* label) {
990 CHECK(label != nullptr);
991 ___ B(ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
992 }
993
TestGcMarking(JNIMacroLabel * label,JNIMacroUnaryCondition cond)994 void ArmVIXLJNIMacroAssembler::TestGcMarking(JNIMacroLabel* label, JNIMacroUnaryCondition cond) {
995 CHECK(label != nullptr);
996
997 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
998 vixl32::Register test_reg;
999 DCHECK_EQ(Thread::IsGcMarkingSize(), 4u);
1000 DCHECK(kUseReadBarrier);
1001 if (kUseBakerReadBarrier) {
1002 // TestGcMarking() is used in the JNI stub entry when the marking register is up to date.
1003 if (kIsDebugBuild && emit_run_time_checks_in_debug_mode_) {
1004 vixl32::Register temp = temps.Acquire();
1005 asm_.GenerateMarkingRegisterCheck(temp);
1006 }
1007 test_reg = mr;
1008 } else {
1009 test_reg = temps.Acquire();
1010 ___ Ldr(test_reg, MemOperand(tr, Thread::IsGcMarkingOffset<kArmPointerSize>().Int32Value()));
1011 }
1012 switch (cond) {
1013 case JNIMacroUnaryCondition::kZero:
1014 ___ CompareAndBranchIfZero(test_reg, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
1015 break;
1016 case JNIMacroUnaryCondition::kNotZero:
1017 ___ CompareAndBranchIfNonZero(test_reg, ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
1018 break;
1019 default:
1020 LOG(FATAL) << "Not implemented unary condition: " << static_cast<int>(cond);
1021 UNREACHABLE();
1022 }
1023 }
1024
Bind(JNIMacroLabel * label)1025 void ArmVIXLJNIMacroAssembler::Bind(JNIMacroLabel* label) {
1026 CHECK(label != nullptr);
1027 ___ Bind(ArmVIXLJNIMacroLabel::Cast(label)->AsArm());
1028 }
1029
EmitExceptionPoll(ArmVIXLJNIMacroAssembler::ArmException * exception)1030 void ArmVIXLJNIMacroAssembler::EmitExceptionPoll(
1031 ArmVIXLJNIMacroAssembler::ArmException* exception) {
1032 ___ Bind(exception->Entry());
1033 if (exception->stack_adjust_ != 0) { // Fix up the frame.
1034 DecreaseFrameSize(exception->stack_adjust_);
1035 }
1036
1037 vixl32::Register scratch = exception->scratch_;
1038 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
1039 temps.Exclude(scratch);
1040 // Pass exception object as argument.
1041 // Don't care about preserving r0 as this won't return.
1042 ___ Mov(r0, scratch);
1043 ___ Ldr(lr,
1044 MemOperand(tr,
1045 QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, pDeliverException).Int32Value()));
1046 ___ Blx(lr);
1047 }
1048
MemoryBarrier(ManagedRegister scratch ATTRIBUTE_UNUSED)1049 void ArmVIXLJNIMacroAssembler::MemoryBarrier(ManagedRegister scratch ATTRIBUTE_UNUSED) {
1050 UNIMPLEMENTED(FATAL);
1051 }
1052
Load(ArmManagedRegister dest,vixl32::Register base,int32_t offset,size_t size)1053 void ArmVIXLJNIMacroAssembler::Load(ArmManagedRegister
1054 dest,
1055 vixl32::Register base,
1056 int32_t offset,
1057 size_t size) {
1058 if (dest.IsNoRegister()) {
1059 CHECK_EQ(0u, size) << dest;
1060 } else if (dest.IsCoreRegister()) {
1061 vixl::aarch32::Register dst = AsVIXLRegister(dest);
1062 CHECK(!dst.Is(sp)) << dest;
1063
1064 UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
1065 temps.Exclude(dst);
1066
1067 if (size == 1u) {
1068 ___ Ldrb(dst, MemOperand(base, offset));
1069 } else {
1070 CHECK_EQ(4u, size) << dest;
1071 ___ Ldr(dst, MemOperand(base, offset));
1072 }
1073 } else if (dest.IsRegisterPair()) {
1074 CHECK_EQ(8u, size) << dest;
1075 ___ Ldr(AsVIXLRegisterPairLow(dest), MemOperand(base, offset));
1076 ___ Ldr(AsVIXLRegisterPairHigh(dest), MemOperand(base, offset + 4));
1077 } else if (dest.IsSRegister()) {
1078 ___ Vldr(AsVIXLSRegister(dest), MemOperand(base, offset));
1079 } else {
1080 CHECK(dest.IsDRegister()) << dest;
1081 ___ Vldr(AsVIXLDRegister(dest), MemOperand(base, offset));
1082 }
1083 }
1084
1085 } // namespace arm
1086 } // namespace art
1087