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_mips.h"
18
19 #include "base/logging.h"
20 #include "handle_scope-inl.h"
21 #include "utils/mips/managed_register_mips.h"
22
23 namespace art {
24 namespace mips {
25
26 static const Register kCoreArgumentRegisters[] = { A0, A1, A2, A3 };
27 static const FRegister kFArgumentRegisters[] = { F12, F14 };
28 static const DRegister kDArgumentRegisters[] = { D6, D7 };
29
30 // Calling convention
InterproceduralScratchRegister()31 ManagedRegister MipsManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
32 return MipsManagedRegister::FromCoreRegister(T9);
33 }
34
InterproceduralScratchRegister()35 ManagedRegister MipsJniCallingConvention::InterproceduralScratchRegister() {
36 return MipsManagedRegister::FromCoreRegister(T9);
37 }
38
ReturnRegisterForShorty(const char * shorty)39 static ManagedRegister ReturnRegisterForShorty(const char* shorty) {
40 if (shorty[0] == 'F') {
41 return MipsManagedRegister::FromFRegister(F0);
42 } else if (shorty[0] == 'D') {
43 return MipsManagedRegister::FromDRegister(D0);
44 } else if (shorty[0] == 'J') {
45 return MipsManagedRegister::FromRegisterPair(V0_V1);
46 } else if (shorty[0] == 'V') {
47 return MipsManagedRegister::NoRegister();
48 } else {
49 return MipsManagedRegister::FromCoreRegister(V0);
50 }
51 }
52
ReturnRegister()53 ManagedRegister MipsManagedRuntimeCallingConvention::ReturnRegister() {
54 return ReturnRegisterForShorty(GetShorty());
55 }
56
ReturnRegister()57 ManagedRegister MipsJniCallingConvention::ReturnRegister() {
58 return ReturnRegisterForShorty(GetShorty());
59 }
60
IntReturnRegister()61 ManagedRegister MipsJniCallingConvention::IntReturnRegister() {
62 return MipsManagedRegister::FromCoreRegister(V0);
63 }
64
65 // Managed runtime calling convention
66
MethodRegister()67 ManagedRegister MipsManagedRuntimeCallingConvention::MethodRegister() {
68 return MipsManagedRegister::FromCoreRegister(A0);
69 }
70
IsCurrentParamInRegister()71 bool MipsManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
72 return false; // Everything moved to stack on entry.
73 }
74
IsCurrentParamOnStack()75 bool MipsManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
76 return true;
77 }
78
CurrentParamRegister()79 ManagedRegister MipsManagedRuntimeCallingConvention::CurrentParamRegister() {
80 LOG(FATAL) << "Should not reach here";
81 return ManagedRegister::NoRegister();
82 }
83
CurrentParamStackOffset()84 FrameOffset MipsManagedRuntimeCallingConvention::CurrentParamStackOffset() {
85 CHECK(IsCurrentParamOnStack());
86 FrameOffset result =
87 FrameOffset(displacement_.Int32Value() + // displacement
88 kFramePointerSize + // Method*
89 (itr_slots_ * kFramePointerSize)); // offset into in args
90 return result;
91 }
92
EntrySpills()93 const ManagedRegisterEntrySpills& MipsManagedRuntimeCallingConvention::EntrySpills() {
94 // We spill the argument registers on MIPS to free them up for scratch use, we then assume
95 // all arguments are on the stack.
96 if ((entry_spills_.size() == 0) && (NumArgs() > 0)) {
97 uint32_t gpr_index = 1; // Skip A0, it is used for ArtMethod*.
98 uint32_t fpr_index = 0;
99
100 for (ResetIterator(FrameOffset(0)); HasNext(); Next()) {
101 if (IsCurrentParamAFloatOrDouble()) {
102 if (IsCurrentParamADouble()) {
103 if (fpr_index < arraysize(kDArgumentRegisters)) {
104 entry_spills_.push_back(
105 MipsManagedRegister::FromDRegister(kDArgumentRegisters[fpr_index++]));
106 } else {
107 entry_spills_.push_back(ManagedRegister::NoRegister(), 8);
108 }
109 } else {
110 if (fpr_index < arraysize(kFArgumentRegisters)) {
111 entry_spills_.push_back(
112 MipsManagedRegister::FromFRegister(kFArgumentRegisters[fpr_index++]));
113 } else {
114 entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
115 }
116 }
117 } else {
118 if (IsCurrentParamALong() && !IsCurrentParamAReference()) {
119 if (gpr_index == 1) {
120 // Don't use a1-a2 as a register pair, move to a2-a3 instead.
121 gpr_index++;
122 }
123 if (gpr_index < arraysize(kCoreArgumentRegisters) - 1) {
124 entry_spills_.push_back(
125 MipsManagedRegister::FromCoreRegister(kCoreArgumentRegisters[gpr_index++]));
126 } else if (gpr_index == arraysize(kCoreArgumentRegisters) - 1) {
127 gpr_index++;
128 entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
129 } else {
130 entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
131 }
132 }
133
134 if (gpr_index < arraysize(kCoreArgumentRegisters)) {
135 entry_spills_.push_back(
136 MipsManagedRegister::FromCoreRegister(kCoreArgumentRegisters[gpr_index++]));
137 } else {
138 entry_spills_.push_back(ManagedRegister::NoRegister(), 4);
139 }
140 }
141 }
142 }
143 return entry_spills_;
144 }
145 // JNI calling convention
146
MipsJniCallingConvention(bool is_static,bool is_synchronized,const char * shorty)147 MipsJniCallingConvention::MipsJniCallingConvention(bool is_static, bool is_synchronized,
148 const char* shorty)
149 : JniCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) {
150 // Compute padding to ensure longs and doubles are not split in AAPCS. Ignore the 'this' jobject
151 // or jclass for static methods and the JNIEnv. We start at the aligned register A2.
152 size_t padding = 0;
153 for (size_t cur_arg = IsStatic() ? 0 : 1, cur_reg = 2; cur_arg < NumArgs(); cur_arg++) {
154 if (IsParamALongOrDouble(cur_arg)) {
155 if ((cur_reg & 1) != 0) {
156 padding += 4;
157 cur_reg++; // additional bump to ensure alignment
158 }
159 cur_reg++; // additional bump to skip extra long word
160 }
161 cur_reg++; // bump the iterator for every argument
162 }
163 padding_ = padding;
164
165 callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S2));
166 callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S3));
167 callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S4));
168 callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S5));
169 callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S6));
170 callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(S7));
171 callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(FP));
172 }
173
CoreSpillMask() const174 uint32_t MipsJniCallingConvention::CoreSpillMask() const {
175 // Compute spill mask to agree with callee saves initialized in the constructor
176 uint32_t result = 0;
177 result = 1 << S2 | 1 << S3 | 1 << S4 | 1 << S5 | 1 << S6 | 1 << S7 | 1 << FP | 1 << RA;
178 return result;
179 }
180
ReturnScratchRegister() const181 ManagedRegister MipsJniCallingConvention::ReturnScratchRegister() const {
182 return MipsManagedRegister::FromCoreRegister(AT);
183 }
184
FrameSize()185 size_t MipsJniCallingConvention::FrameSize() {
186 // ArtMethod*, RA and callee save area size, local reference segment state
187 size_t frame_data_size = kMipsPointerSize +
188 (2 + CalleeSaveRegisters().size()) * kFramePointerSize;
189 // References plus 2 words for HandleScope header
190 size_t handle_scope_size = HandleScope::SizeOf(kFramePointerSize, ReferenceCount());
191 // Plus return value spill area size
192 return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
193 }
194
OutArgSize()195 size_t MipsJniCallingConvention::OutArgSize() {
196 return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize + padding_, kStackAlignment);
197 }
198
199 // JniCallingConvention ABI follows AAPCS where longs and doubles must occur
200 // in even register numbers and stack slots
Next()201 void MipsJniCallingConvention::Next() {
202 JniCallingConvention::Next();
203 size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
204 if ((itr_args_ >= 2) &&
205 (arg_pos < NumArgs()) &&
206 IsParamALongOrDouble(arg_pos)) {
207 // itr_slots_ needs to be an even number, according to AAPCS.
208 if ((itr_slots_ & 0x1u) != 0) {
209 itr_slots_++;
210 }
211 }
212 }
213
IsCurrentParamInRegister()214 bool MipsJniCallingConvention::IsCurrentParamInRegister() {
215 return itr_slots_ < 4;
216 }
217
IsCurrentParamOnStack()218 bool MipsJniCallingConvention::IsCurrentParamOnStack() {
219 return !IsCurrentParamInRegister();
220 }
221
222 static const Register kJniArgumentRegisters[] = {
223 A0, A1, A2, A3
224 };
CurrentParamRegister()225 ManagedRegister MipsJniCallingConvention::CurrentParamRegister() {
226 CHECK_LT(itr_slots_, 4u);
227 int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
228 if ((itr_args_ >= 2) && IsParamALongOrDouble(arg_pos)) {
229 CHECK_EQ(itr_slots_, 2u);
230 return MipsManagedRegister::FromRegisterPair(A2_A3);
231 } else {
232 return
233 MipsManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
234 }
235 }
236
CurrentParamStackOffset()237 FrameOffset MipsJniCallingConvention::CurrentParamStackOffset() {
238 CHECK_GE(itr_slots_, 4u);
239 size_t offset = displacement_.Int32Value() - OutArgSize() + (itr_slots_ * kFramePointerSize);
240 CHECK_LT(offset, OutArgSize());
241 return FrameOffset(offset);
242 }
243
NumberOfOutgoingStackArgs()244 size_t MipsJniCallingConvention::NumberOfOutgoingStackArgs() {
245 size_t static_args = IsStatic() ? 1 : 0; // count jclass
246 // regular argument parameters and this
247 size_t param_args = NumArgs() + NumLongOrDoubleArgs();
248 // count JNIEnv*
249 return static_args + param_args + 1;
250 }
251 } // namespace mips
252 } // namespace art
253