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