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