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