blob: ed876f0dc672151ef42361906d971f207c3a6523 [file] [log] [blame]
Ian Rogersb033c752011-07-20 12:22:35 -07001// Copyright 2011 Google Inc. All Rights Reserved.
Ian Rogersb033c752011-07-20 12:22:35 -07002
Ian Rogers2c8f6532011-09-02 17:16:34 -07003#include "calling_convention_arm.h"
Brian Carlstrom578bbdc2011-07-21 14:07:47 -07004#include "logging.h"
Ian Rogers2c8f6532011-09-02 17:16:34 -07005#include "managed_register_arm.h"
Ian Rogersb033c752011-07-20 12:22:35 -07006
7namespace art {
Ian Rogers2c8f6532011-09-02 17:16:34 -07008namespace arm {
Ian Rogersb033c752011-07-20 12:22:35 -07009
Ian Rogers2c8f6532011-09-02 17:16:34 -070010// Calling convention
11
12ManagedRegister ArmManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
13 return ArmManagedRegister::FromCoreRegister(IP); // R12
Ian Rogersb033c752011-07-20 12:22:35 -070014}
15
Ian Rogers2c8f6532011-09-02 17:16:34 -070016ManagedRegister ArmJniCallingConvention::InterproceduralScratchRegister() {
17 return ArmManagedRegister::FromCoreRegister(IP); // R12
Shih-wei Liao668512a2011-09-01 14:18:34 -070018}
19
Ian Rogers2c8f6532011-09-02 17:16:34 -070020static ManagedRegister ReturnRegisterForMethod(Method* method) {
21 if (method->IsReturnAFloat()) {
22 return ArmManagedRegister::FromCoreRegister(R0);
23 } else if (method->IsReturnADouble()) {
24 return ArmManagedRegister::FromRegisterPair(R0_R1);
Ian Rogers45a76cb2011-07-21 22:00:15 -070025 } else if (method->IsReturnALong()) {
Ian Rogers2c8f6532011-09-02 17:16:34 -070026 return ArmManagedRegister::FromRegisterPair(R0_R1);
Ian Rogers45a76cb2011-07-21 22:00:15 -070027 } else if (method->IsReturnVoid()) {
Ian Rogers2c8f6532011-09-02 17:16:34 -070028 return ArmManagedRegister::NoRegister();
Ian Rogersb033c752011-07-20 12:22:35 -070029 } else {
Ian Rogers2c8f6532011-09-02 17:16:34 -070030 return ArmManagedRegister::FromCoreRegister(R0);
Ian Rogersb033c752011-07-20 12:22:35 -070031 }
32}
33
Ian Rogers2c8f6532011-09-02 17:16:34 -070034ManagedRegister ArmManagedRuntimeCallingConvention::ReturnRegister() {
35 return ReturnRegisterForMethod(GetMethod());
36}
37
38ManagedRegister ArmJniCallingConvention::ReturnRegister() {
39 return ReturnRegisterForMethod(GetMethod());
40}
41
Ian Rogersb033c752011-07-20 12:22:35 -070042// Managed runtime calling convention
43
Ian Rogers2c8f6532011-09-02 17:16:34 -070044ManagedRegister ArmManagedRuntimeCallingConvention::MethodRegister() {
45 return ArmManagedRegister::FromCoreRegister(R0);
46}
47
48bool ArmManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
Ian Rogerscdd1d2d2011-08-18 09:58:17 -070049 return itr_slots_ < 3;
Ian Rogersb033c752011-07-20 12:22:35 -070050}
51
Ian Rogers2c8f6532011-09-02 17:16:34 -070052bool ArmManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
Ian Rogers7a99c112011-09-07 12:48:27 -070053 if (itr_slots_ < 2) {
54 return false;
55 } else if (itr_slots_ > 2) {
56 return true;
57 } else {
58 // handle funny case of a long/double straddling registers and the stack
59 return GetMethod()->IsParamALongOrDouble(itr_args_);
60 }
Ian Rogersb033c752011-07-20 12:22:35 -070061}
62
63static const Register kManagedArgumentRegisters[] = {
64 R1, R2, R3
65};
Ian Rogers2c8f6532011-09-02 17:16:34 -070066ManagedRegister ArmManagedRuntimeCallingConvention::CurrentParamRegister() {
Ian Rogers7a99c112011-09-07 12:48:27 -070067 CHECK(IsCurrentParamInRegister());
Carl Shapiroe2d373e2011-07-25 15:20:06 -070068 const Method* method = GetMethod();
Shih-wei Liao5381cf92011-07-27 00:28:04 -070069 if (method->IsParamALongOrDouble(itr_args_)) {
70 if (itr_slots_ == 0) {
Ian Rogers2c8f6532011-09-02 17:16:34 -070071 return ArmManagedRegister::FromRegisterPair(R1_R2);
Shih-wei Liao5381cf92011-07-27 00:28:04 -070072 } else if (itr_slots_ == 1) {
Ian Rogers2c8f6532011-09-02 17:16:34 -070073 return ArmManagedRegister::FromRegisterPair(R2_R3);
Shih-wei Liao5381cf92011-07-27 00:28:04 -070074 } else {
75 // This is a long/double split between registers and the stack
Ian Rogers2c8f6532011-09-02 17:16:34 -070076 return ArmManagedRegister::FromCoreRegister(
Shih-wei Liao5381cf92011-07-27 00:28:04 -070077 kManagedArgumentRegisters[itr_slots_]);
Carl Shapiroe2d373e2011-07-25 15:20:06 -070078 }
Carl Shapiroe2d373e2011-07-25 15:20:06 -070079 } else {
80 return
Ian Rogers2c8f6532011-09-02 17:16:34 -070081 ArmManagedRegister::FromCoreRegister(kManagedArgumentRegisters[itr_slots_]);
Carl Shapiroe2d373e2011-07-25 15:20:06 -070082 }
Ian Rogersb033c752011-07-20 12:22:35 -070083}
84
Ian Rogers2c8f6532011-09-02 17:16:34 -070085FrameOffset ArmManagedRuntimeCallingConvention::CurrentParamStackOffset() {
Ian Rogers7a99c112011-09-07 12:48:27 -070086 CHECK(IsCurrentParamOnStack());
87 FrameOffset result =
88 FrameOffset(displacement_.Int32Value() + // displacement
89 kPointerSize + // Method*
90 (itr_slots_ * kPointerSize)); // offset into in args
91 if (itr_slots_ == 2) {
92 // the odd spanning case, bump the offset to skip the first half of the
93 // input which is in a register
94 CHECK(IsCurrentParamInRegister());
95 result = FrameOffset(result.Int32Value() + 4);
96 }
97 return result;
Ian Rogersb033c752011-07-20 12:22:35 -070098}
99
100// JNI calling convention
101
Ian Rogersbdb03912011-09-14 00:55:44 -0700102ArmJniCallingConvention::ArmJniCallingConvention(Method* method) : JniCallingConvention(method) {
Ian Rogers67375ac2011-09-14 00:55:44 -0700103 // Compute padding to ensure longs and doubles are not split in AAPCS
104 // TODO: in terms of outgoing argument size this may be overly generous
105 // due to padding appearing in the registers
106 size_t padding = 0;
107 size_t check = method->IsStatic() ? 1 : 0;
108 for(size_t i = 0; i < method->NumArgs(); i++) {
109 if(((i & 1) == check) && method->IsParamALongOrDouble(i)) {
110 padding += 4;
111 }
Ian Rogers2c8f6532011-09-02 17:16:34 -0700112 }
Ian Rogers67375ac2011-09-14 00:55:44 -0700113 padding_ = padding;
114 if (method->IsSynchronized()) {
115 // Preserve callee saves that may be clobbered during monitor enter where
116 // we copy across R0 to R3
117 if (method->NumArgs() > 0) {
118 callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R4));
119 if (method->NumArgs() > 1) {
120 callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R5));
121 if (method->NumArgs() > 2) {
122 callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R6));
123 if (method->NumArgs() > 3) {
124 callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R7));
125 }
126 }
127 }
128 }
129 }
130}
131
132uint32_t ArmJniCallingConvention::CoreSpillMask() const {
133 // Compute spill mask to agree with callee saves initialized in the constructor
134 uint32_t result = 0;
135 Method* method = GetMethod();
136 if (method->IsSynchronized()) {
137 if (method->NumArgs() > 0) {
138 result |= 1 << R4;
139 if (method->NumArgs() > 1) {
140 result |= 1 << R5;
141 if (method->NumArgs() > 2) {
142 result |= 1 << R6;
143 if (method->NumArgs() > 3) {
144 result |= 1 << R7;
145 }
146 }
147 }
148 }
149 }
150 return result;
Ian Rogers2c8f6532011-09-02 17:16:34 -0700151}
152
153size_t ArmJniCallingConvention::FrameSize() {
Ian Rogersbdb03912011-09-14 00:55:44 -0700154 // Method*, LR and callee save area size
155 size_t frame_data_size = (2 + CalleeSaveRegisters().size()) * kPointerSize;
Ian Rogers408f79a2011-08-23 18:22:33 -0700156 // References plus 2 words for SIRT header
157 size_t sirt_size = (ReferenceCount() + 2) * kPointerSize;
Ian Rogers0d666d82011-08-14 16:03:46 -0700158 // Plus return value spill area size
Ian Rogers408f79a2011-08-23 18:22:33 -0700159 return RoundUp(frame_data_size + sirt_size + SizeOfReturnValue(),
160 kStackAlignment);
Ian Rogers0d666d82011-08-14 16:03:46 -0700161}
162
Ian Rogers2c8f6532011-09-02 17:16:34 -0700163size_t ArmJniCallingConvention::OutArgSize() {
Ian Rogers67375ac2011-09-14 00:55:44 -0700164 return RoundUp(NumberOfOutgoingStackArgs() * kPointerSize + padding_,
Ian Rogers7a99c112011-09-07 12:48:27 -0700165 kStackAlignment);
166}
167
Ian Rogers2c8f6532011-09-02 17:16:34 -0700168size_t ArmJniCallingConvention::ReturnPcOffset() {
Ian Rogersbdb03912011-09-14 00:55:44 -0700169 // Link register is always the first value pushed when the frame is constructed
170 return FrameSize() - kPointerSize;
Ian Rogers0d666d82011-08-14 16:03:46 -0700171}
172
Carl Shapiroe2d373e2011-07-25 15:20:06 -0700173// Will reg be crushed by an outgoing argument?
Ian Rogersad42e132011-09-17 20:23:33 -0700174bool ArmJniCallingConvention::IsMethodRegisterClobberedPreCall() {
Ian Rogers67375ac2011-09-14 00:55:44 -0700175 return true; // The method register R0 is always clobbered by the JNIEnv
Carl Shapiroe2d373e2011-07-25 15:20:06 -0700176}
177
Ian Rogers67375ac2011-09-14 00:55:44 -0700178// JniCallingConvention ABI follows AAPCS where longs and doubles must occur
179// in even register numbers and stack slots
180void ArmJniCallingConvention::Next() {
181 JniCallingConvention::Next();
Ian Rogers2c8f6532011-09-02 17:16:34 -0700182 Method* method = GetMethod();
Ian Rogers67375ac2011-09-14 00:55:44 -0700183 size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
184 if ((itr_args_ >= 2) &&
185 (arg_pos < GetMethod()->NumArgs()) &&
186 method->IsParamALongOrDouble(arg_pos)) {
Shih-wei Liao5381cf92011-07-27 00:28:04 -0700187 // itr_slots_ needs to be an even number, according to AAPCS.
Ian Rogersbdb03912011-09-14 00:55:44 -0700188 if ((itr_slots_ & 0x1u) != 0) {
Shih-wei Liao5381cf92011-07-27 00:28:04 -0700189 itr_slots_++;
190 }
191 }
Ian Rogers67375ac2011-09-14 00:55:44 -0700192}
Shih-wei Liao5381cf92011-07-27 00:28:04 -0700193
Ian Rogers67375ac2011-09-14 00:55:44 -0700194bool ArmJniCallingConvention::IsCurrentParamInRegister() {
Shih-wei Liao5381cf92011-07-27 00:28:04 -0700195 return itr_slots_ < 4;
Ian Rogersb033c752011-07-20 12:22:35 -0700196}
197
Ian Rogers2c8f6532011-09-02 17:16:34 -0700198bool ArmJniCallingConvention::IsCurrentParamOnStack() {
Shih-wei Liao5381cf92011-07-27 00:28:04 -0700199 return !IsCurrentParamInRegister();
Ian Rogersb033c752011-07-20 12:22:35 -0700200}
201
202static const Register kJniArgumentRegisters[] = {
203 R0, R1, R2, R3
204};
Ian Rogers2c8f6532011-09-02 17:16:34 -0700205ManagedRegister ArmJniCallingConvention::CurrentParamRegister() {
Shih-wei Liao5381cf92011-07-27 00:28:04 -0700206 CHECK_LT(itr_slots_, 4u);
Ian Rogers2c8f6532011-09-02 17:16:34 -0700207 Method* method = GetMethod();
Shih-wei Liao5381cf92011-07-27 00:28:04 -0700208 int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni(method);
209 if ((itr_args_ >= 2) && method->IsParamALongOrDouble(arg_pos)) {
210 CHECK_EQ(itr_slots_, 2u);
Ian Rogers2c8f6532011-09-02 17:16:34 -0700211 return ArmManagedRegister::FromRegisterPair(R2_R3);
Carl Shapiroe2d373e2011-07-25 15:20:06 -0700212 } else {
213 return
Ian Rogers2c8f6532011-09-02 17:16:34 -0700214 ArmManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
Carl Shapiroe2d373e2011-07-25 15:20:06 -0700215 }
Ian Rogersb033c752011-07-20 12:22:35 -0700216}
217
Ian Rogers2c8f6532011-09-02 17:16:34 -0700218FrameOffset ArmJniCallingConvention::CurrentParamStackOffset() {
Shih-wei Liao5381cf92011-07-27 00:28:04 -0700219 CHECK_GE(itr_slots_, 4u);
Ian Rogersb033c752011-07-20 12:22:35 -0700220 return FrameOffset(displacement_.Int32Value() - OutArgSize()
Ian Rogers67375ac2011-09-14 00:55:44 -0700221 + ((itr_slots_ - 4) * kPointerSize));
Ian Rogersb033c752011-07-20 12:22:35 -0700222}
223
Ian Rogers2c8f6532011-09-02 17:16:34 -0700224size_t ArmJniCallingConvention::NumberOfOutgoingStackArgs() {
225 Method* method = GetMethod();
Ian Rogers7a99c112011-09-07 12:48:27 -0700226 size_t static_args = method->IsStatic() ? 1 : 0; // count jclass
227 // regular argument parameters and this
228 size_t param_args = method->NumArgs() +
229 method->NumLongOrDoubleArgs();
230 // count JNIEnv* less arguments in registers
231 return static_args + param_args + 1 - 4;
Ian Rogersb033c752011-07-20 12:22:35 -0700232}
233
Ian Rogers2c8f6532011-09-02 17:16:34 -0700234} // namespace arm
Ian Rogersb033c752011-07-20 12:22:35 -0700235} // namespace art