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