• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 "calling_convention_arm.h"
18 
19 #include <android-base/logging.h>
20 
21 #include "arch/instruction_set.h"
22 #include "base/macros.h"
23 #include "handle_scope-inl.h"
24 #include "utils/arm/managed_register_arm.h"
25 
26 namespace art {
27 namespace arm {
28 
29 static_assert(kArmPointerSize == PointerSize::k32, "Unexpected ARM pointer size");
30 
31 //
32 // JNI calling convention constants.
33 //
34 
35 // List of parameters passed via registers for JNI.
36 // JNI uses soft-float, so there is only a GPR list.
37 static const Register kJniArgumentRegisters[] = {
38   R0, R1, R2, R3
39 };
40 
41 static const size_t kJniArgumentRegisterCount = arraysize(kJniArgumentRegisters);
42 
43 //
44 // Managed calling convention constants.
45 //
46 
47 // Used by hard float. (General purpose registers.)
48 static const Register kHFCoreArgumentRegisters[] = {
49   R0, R1, R2, R3
50 };
51 
52 // (VFP single-precision registers.)
53 static const SRegister kHFSArgumentRegisters[] = {
54   S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15
55 };
56 
57 // (VFP double-precision registers.)
58 static const DRegister kHFDArgumentRegisters[] = {
59   D0, D1, D2, D3, D4, D5, D6, D7
60 };
61 
62 static_assert(arraysize(kHFDArgumentRegisters) * 2 == arraysize(kHFSArgumentRegisters),
63     "ks d argument registers mismatch");
64 
65 //
66 // Shared managed+JNI calling convention constants.
67 //
68 
69 static constexpr ManagedRegister kCalleeSaveRegisters[] = {
70     // Core registers.
71     ArmManagedRegister::FromCoreRegister(R5),
72     ArmManagedRegister::FromCoreRegister(R6),
73     ArmManagedRegister::FromCoreRegister(R7),
74     ArmManagedRegister::FromCoreRegister(R8),
75     ArmManagedRegister::FromCoreRegister(R10),
76     ArmManagedRegister::FromCoreRegister(R11),
77     // Hard float registers.
78     ArmManagedRegister::FromSRegister(S16),
79     ArmManagedRegister::FromSRegister(S17),
80     ArmManagedRegister::FromSRegister(S18),
81     ArmManagedRegister::FromSRegister(S19),
82     ArmManagedRegister::FromSRegister(S20),
83     ArmManagedRegister::FromSRegister(S21),
84     ArmManagedRegister::FromSRegister(S22),
85     ArmManagedRegister::FromSRegister(S23),
86     ArmManagedRegister::FromSRegister(S24),
87     ArmManagedRegister::FromSRegister(S25),
88     ArmManagedRegister::FromSRegister(S26),
89     ArmManagedRegister::FromSRegister(S27),
90     ArmManagedRegister::FromSRegister(S28),
91     ArmManagedRegister::FromSRegister(S29),
92     ArmManagedRegister::FromSRegister(S30),
93     ArmManagedRegister::FromSRegister(S31)
94 };
95 
CalculateCoreCalleeSpillMask()96 static constexpr uint32_t CalculateCoreCalleeSpillMask() {
97   // LR is a special callee save which is not reported by CalleeSaveRegisters().
98   uint32_t result = 1 << LR;
99   for (auto&& r : kCalleeSaveRegisters) {
100     if (r.AsArm().IsCoreRegister()) {
101       result |= (1 << r.AsArm().AsCoreRegister());
102     }
103   }
104   return result;
105 }
106 
CalculateFpCalleeSpillMask()107 static constexpr uint32_t CalculateFpCalleeSpillMask() {
108   uint32_t result = 0;
109   for (auto&& r : kCalleeSaveRegisters) {
110     if (r.AsArm().IsSRegister()) {
111       result |= (1 << r.AsArm().AsSRegister());
112     }
113   }
114   return result;
115 }
116 
117 static constexpr uint32_t kCoreCalleeSpillMask = CalculateCoreCalleeSpillMask();
118 static constexpr uint32_t kFpCalleeSpillMask = CalculateFpCalleeSpillMask();
119 
120 // Calling convention
121 
InterproceduralScratchRegister()122 ManagedRegister ArmManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
123   return ArmManagedRegister::FromCoreRegister(IP);  // R12
124 }
125 
InterproceduralScratchRegister()126 ManagedRegister ArmJniCallingConvention::InterproceduralScratchRegister() {
127   return ArmManagedRegister::FromCoreRegister(IP);  // R12
128 }
129 
ReturnRegister()130 ManagedRegister ArmManagedRuntimeCallingConvention::ReturnRegister() {
131   switch (GetShorty()[0]) {
132     case 'V':
133       return ArmManagedRegister::NoRegister();
134     case 'D':
135       return ArmManagedRegister::FromDRegister(D0);
136     case 'F':
137       return ArmManagedRegister::FromSRegister(S0);
138     case 'J':
139       return ArmManagedRegister::FromRegisterPair(R0_R1);
140     default:
141       return ArmManagedRegister::FromCoreRegister(R0);
142   }
143 }
144 
ReturnRegister()145 ManagedRegister ArmJniCallingConvention::ReturnRegister() {
146   switch (GetShorty()[0]) {
147   case 'V':
148     return ArmManagedRegister::NoRegister();
149   case 'D':
150   case 'J':
151     return ArmManagedRegister::FromRegisterPair(R0_R1);
152   default:
153     return ArmManagedRegister::FromCoreRegister(R0);
154   }
155 }
156 
IntReturnRegister()157 ManagedRegister ArmJniCallingConvention::IntReturnRegister() {
158   return ArmManagedRegister::FromCoreRegister(R0);
159 }
160 
161 // Managed runtime calling convention
162 
MethodRegister()163 ManagedRegister ArmManagedRuntimeCallingConvention::MethodRegister() {
164   return ArmManagedRegister::FromCoreRegister(R0);
165 }
166 
IsCurrentParamInRegister()167 bool ArmManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
168   return false;  // Everything moved to stack on entry.
169 }
170 
IsCurrentParamOnStack()171 bool ArmManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
172   return true;
173 }
174 
CurrentParamRegister()175 ManagedRegister ArmManagedRuntimeCallingConvention::CurrentParamRegister() {
176   LOG(FATAL) << "Should not reach here";
177   UNREACHABLE();
178 }
179 
CurrentParamStackOffset()180 FrameOffset ArmManagedRuntimeCallingConvention::CurrentParamStackOffset() {
181   CHECK(IsCurrentParamOnStack());
182   FrameOffset result =
183       FrameOffset(displacement_.Int32Value() +        // displacement
184                   kFramePointerSize +                 // Method*
185                   (itr_slots_ * kFramePointerSize));  // offset into in args
186   return result;
187 }
188 
EntrySpills()189 const ManagedRegisterEntrySpills& ArmManagedRuntimeCallingConvention::EntrySpills() {
190   // We spill the argument registers on ARM to free them up for scratch use, we then assume
191   // all arguments are on the stack.
192   if ((entry_spills_.size() == 0) && (NumArgs() > 0)) {
193     uint32_t gpr_index = 1;  // R0 ~ R3. Reserve r0 for ArtMethod*.
194     uint32_t fpr_index = 0;  // S0 ~ S15.
195     uint32_t fpr_double_index = 0;  // D0 ~ D7.
196 
197     ResetIterator(FrameOffset(0));
198     while (HasNext()) {
199       if (IsCurrentParamAFloatOrDouble()) {
200         if (IsCurrentParamADouble()) {  // Double.
201           // Double should not overlap with float.
202           fpr_double_index = (std::max(fpr_double_index * 2, RoundUp(fpr_index, 2))) / 2;
203           if (fpr_double_index < arraysize(kHFDArgumentRegisters)) {
204             entry_spills_.push_back(
205                 ArmManagedRegister::FromDRegister(kHFDArgumentRegisters[fpr_double_index++]));
206           } else {
207             entry_spills_.push_back(ManagedRegister::NoRegister(), 8);
208           }
209         } else {  // Float.
210           // Float should not overlap with double.
211           if (fpr_index % 2 == 0) {
212             fpr_index = std::max(fpr_double_index * 2, fpr_index);
213           }
214           if (fpr_index < arraysize(kHFSArgumentRegisters)) {
215             entry_spills_.push_back(
216                 ArmManagedRegister::FromSRegister(kHFSArgumentRegisters[fpr_index++]));
217           } else {
218             entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
219           }
220         }
221       } else {
222         // FIXME: Pointer this returns as both reference and long.
223         if (IsCurrentParamALong() && !IsCurrentParamAReference()) {  // Long.
224           if (gpr_index < arraysize(kHFCoreArgumentRegisters) - 1) {
225             // Skip R1, and use R2_R3 if the long is the first parameter.
226             if (gpr_index == 1) {
227               gpr_index++;
228             }
229           }
230 
231           // If it spans register and memory, we must use the value in memory.
232           if (gpr_index < arraysize(kHFCoreArgumentRegisters) - 1) {
233             entry_spills_.push_back(
234                 ArmManagedRegister::FromCoreRegister(kHFCoreArgumentRegisters[gpr_index++]));
235           } else if (gpr_index == arraysize(kHFCoreArgumentRegisters) - 1) {
236             gpr_index++;
237             entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
238           } else {
239             entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
240           }
241         }
242         // High part of long or 32-bit argument.
243         if (gpr_index < arraysize(kHFCoreArgumentRegisters)) {
244           entry_spills_.push_back(
245               ArmManagedRegister::FromCoreRegister(kHFCoreArgumentRegisters[gpr_index++]));
246         } else {
247           entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
248         }
249       }
250       Next();
251     }
252   }
253   return entry_spills_;
254 }
255 // JNI calling convention
256 
ArmJniCallingConvention(bool is_static,bool is_synchronized,bool is_critical_native,const char * shorty)257 ArmJniCallingConvention::ArmJniCallingConvention(bool is_static,
258                                                  bool is_synchronized,
259                                                  bool is_critical_native,
260                                                  const char* shorty)
261     : JniCallingConvention(is_static,
262                            is_synchronized,
263                            is_critical_native,
264                            shorty,
265                            kArmPointerSize) {
266   // AAPCS 4.1 specifies fundamental alignments for each type. All of our stack arguments are
267   // usually 4-byte aligned, however longs and doubles must be 8 bytes aligned. Add padding to
268   // maintain 8-byte alignment invariant.
269   //
270   // Compute padding to ensure longs and doubles are not split in AAPCS.
271   size_t shift = 0;
272 
273   size_t cur_arg, cur_reg;
274   if (LIKELY(HasExtraArgumentsForJni())) {
275     // Ignore the 'this' jobject or jclass for static methods and the JNIEnv.
276     // We start at the aligned register r2.
277     //
278     // Ignore the first 2 parameters because they are guaranteed to be aligned.
279     cur_arg = NumImplicitArgs();  // skip the "this" arg.
280     cur_reg = 2;  // skip {r0=JNIEnv, r1=jobject} / {r0=JNIEnv, r1=jclass} parameters (start at r2).
281   } else {
282     // Check every parameter.
283     cur_arg = 0;
284     cur_reg = 0;
285   }
286 
287   // TODO: Maybe should just use IsCurrentParamALongOrDouble instead to be cleaner?
288   // (this just seems like an unnecessary micro-optimization).
289 
290   // Shift across a logical register mapping that looks like:
291   //
292   //   | r0 | r1 | r2 | r3 | SP | SP+4| SP+8 | SP+12 | ... | SP+n | SP+n+4 |
293   //
294   //   (where SP is some arbitrary stack pointer that our 0th stack arg would go into).
295   //
296   // Any time there would normally be a long/double in an odd logical register,
297   // we have to push out the rest of the mappings by 4 bytes to maintain an 8-byte alignment.
298   //
299   // This works for both physical register pairs {r0, r1}, {r2, r3} and for when
300   // the value is on the stack.
301   //
302   // For example:
303   // (a) long would normally go into r1, but we shift it into r2
304   //  | INT | (PAD) | LONG      |
305   //  | r0  |  r1   |  r2  | r3 |
306   //
307   // (b) long would normally go into r3, but we shift it into SP
308   //  | INT | INT | INT | (PAD) | LONG     |
309   //  | r0  |  r1 |  r2 |  r3   | SP+4 SP+8|
310   //
311   // where INT is any <=4 byte arg, and LONG is any 8-byte arg.
312   for (; cur_arg < NumArgs(); cur_arg++) {
313     if (IsParamALongOrDouble(cur_arg)) {
314       if ((cur_reg & 1) != 0) {  // check that it's in a logical contiguous register pair
315         shift += 4;
316         cur_reg++;  // additional bump to ensure alignment
317       }
318       cur_reg += 2;  // bump the iterator twice for every long argument
319     } else {
320       cur_reg++;  // bump the iterator for every non-long argument
321     }
322   }
323 
324   if (cur_reg < kJniArgumentRegisterCount) {
325     // As a special case when, as a result of shifting (or not) there are no arguments on the stack,
326     // we actually have 0 stack padding.
327     //
328     // For example with @CriticalNative and:
329     // (int, long) -> shifts the long but doesn't need to pad the stack
330     //
331     //          shift
332     //           \/
333     //  | INT | (PAD) | LONG      | (EMPTY) ...
334     //  | r0  |  r1   |  r2  | r3 |   SP    ...
335     //                                /\
336     //                          no stack padding
337     padding_ = 0;
338   } else {
339     padding_ = shift;
340   }
341 
342   // TODO: add some new JNI tests for @CriticalNative that introduced new edge cases
343   // (a) Using r0,r1 pair = f(long,...)
344   // (b) Shifting r1 long into r2,r3 pair = f(int, long, int, ...);
345   // (c) Shifting but not introducing a stack padding = f(int, long);
346 }
347 
CoreSpillMask() const348 uint32_t ArmJniCallingConvention::CoreSpillMask() const {
349   // Compute spill mask to agree with callee saves initialized in the constructor
350   return kCoreCalleeSpillMask;
351 }
352 
FpSpillMask() const353 uint32_t ArmJniCallingConvention::FpSpillMask() const {
354   return kFpCalleeSpillMask;
355 }
356 
ReturnScratchRegister() const357 ManagedRegister ArmJniCallingConvention::ReturnScratchRegister() const {
358   return ArmManagedRegister::FromCoreRegister(R2);
359 }
360 
FrameSize()361 size_t ArmJniCallingConvention::FrameSize() {
362   // Method*, LR and callee save area size, local reference segment state
363   const size_t method_ptr_size = static_cast<size_t>(kArmPointerSize);
364   const size_t lr_return_addr_size = kFramePointerSize;
365   const size_t callee_save_area_size = CalleeSaveRegisters().size() * kFramePointerSize;
366   size_t frame_data_size = method_ptr_size + lr_return_addr_size + callee_save_area_size;
367 
368   if (LIKELY(HasLocalReferenceSegmentState())) {
369     // local reference segment state
370     frame_data_size += kFramePointerSize;
371     // TODO: Probably better to use sizeof(IRTSegmentState) here...
372   }
373 
374   // References plus link_ (pointer) and number_of_references_ (uint32_t) for HandleScope header
375   const size_t handle_scope_size = HandleScope::SizeOf(kArmPointerSize, ReferenceCount());
376 
377   size_t total_size = frame_data_size;
378   if (LIKELY(HasHandleScope())) {
379     // HandleScope is sometimes excluded.
380     total_size += handle_scope_size;                                 // handle scope size
381   }
382 
383   // Plus return value spill area size
384   total_size += SizeOfReturnValue();
385 
386   return RoundUp(total_size, kStackAlignment);
387 }
388 
OutArgSize()389 size_t ArmJniCallingConvention::OutArgSize() {
390   // TODO: Identical to x86_64 except for also adding additional padding.
391   return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize + padding_,
392                  kStackAlignment);
393 }
394 
CalleeSaveRegisters() const395 ArrayRef<const ManagedRegister> ArmJniCallingConvention::CalleeSaveRegisters() const {
396   return ArrayRef<const ManagedRegister>(kCalleeSaveRegisters);
397 }
398 
399 // JniCallingConvention ABI follows AAPCS where longs and doubles must occur
400 // in even register numbers and stack slots
Next()401 void ArmJniCallingConvention::Next() {
402   // Update the iterator by usual JNI rules.
403   JniCallingConvention::Next();
404 
405   if (LIKELY(HasNext())) {  // Avoid CHECK failure for IsCurrentParam
406     // Ensure slot is 8-byte aligned for longs/doubles (AAPCS).
407     if (IsCurrentParamALongOrDouble() && ((itr_slots_ & 0x1u) != 0)) {
408       // itr_slots_ needs to be an even number, according to AAPCS.
409       itr_slots_++;
410     }
411   }
412 }
413 
IsCurrentParamInRegister()414 bool ArmJniCallingConvention::IsCurrentParamInRegister() {
415   return itr_slots_ < kJniArgumentRegisterCount;
416 }
417 
IsCurrentParamOnStack()418 bool ArmJniCallingConvention::IsCurrentParamOnStack() {
419   return !IsCurrentParamInRegister();
420 }
421 
CurrentParamRegister()422 ManagedRegister ArmJniCallingConvention::CurrentParamRegister() {
423   CHECK_LT(itr_slots_, kJniArgumentRegisterCount);
424   if (IsCurrentParamALongOrDouble()) {
425     // AAPCS 5.1.1 requires 64-bit values to be in a consecutive register pair:
426     // "A double-word sized type is passed in two consecutive registers (e.g., r0 and r1, or r2 and
427     // r3). The content of the registers is as if the value had been loaded from memory
428     // representation with a single LDM instruction."
429     if (itr_slots_ == 0u) {
430       return ArmManagedRegister::FromRegisterPair(R0_R1);
431     } else if (itr_slots_ == 2u) {
432       return ArmManagedRegister::FromRegisterPair(R2_R3);
433     } else {
434       // The register can either be R0 (+R1) or R2 (+R3). Cannot be other values.
435       LOG(FATAL) << "Invalid iterator register position for a long/double " << itr_args_;
436       UNREACHABLE();
437     }
438   } else {
439     // All other types can fit into one register.
440     return ArmManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
441   }
442 }
443 
CurrentParamStackOffset()444 FrameOffset ArmJniCallingConvention::CurrentParamStackOffset() {
445   CHECK_GE(itr_slots_, kJniArgumentRegisterCount);
446   size_t offset =
447       displacement_.Int32Value()
448           - OutArgSize()
449           + ((itr_slots_ - kJniArgumentRegisterCount) * kFramePointerSize);
450   CHECK_LT(offset, OutArgSize());
451   return FrameOffset(offset);
452 }
453 
NumberOfOutgoingStackArgs()454 size_t ArmJniCallingConvention::NumberOfOutgoingStackArgs() {
455   size_t static_args = HasSelfClass() ? 1 : 0;  // count jclass
456   // regular argument parameters and this
457   size_t param_args = NumArgs() + NumLongOrDoubleArgs();  // twice count 8-byte args
458   // XX: Why is the long/ordouble counted twice but not JNIEnv* ???
459   // count JNIEnv* less arguments in registers
460   size_t internal_args = (HasJniEnv() ? 1 : 0 /* jni env */);
461   size_t total_args = static_args + param_args + internal_args;
462 
463   return total_args - std::min(kJniArgumentRegisterCount, static_cast<size_t>(total_args));
464 
465   // TODO: Very similar to x86_64 except for the return pc.
466 }
467 
468 }  // namespace arm
469 }  // namespace art
470