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 // Calling convention
InterproceduralScratchRegister()27 ManagedRegister MipsManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
28   return MipsManagedRegister::FromCoreRegister(T9);
29 }
30 
InterproceduralScratchRegister()31 ManagedRegister MipsJniCallingConvention::InterproceduralScratchRegister() {
32   return MipsManagedRegister::FromCoreRegister(T9);
33 }
34 
ReturnRegisterForShorty(const char * shorty)35 static ManagedRegister ReturnRegisterForShorty(const char* shorty) {
36   if (shorty[0] == 'F') {
37     return MipsManagedRegister::FromFRegister(F0);
38   } else if (shorty[0] == 'D') {
39     return MipsManagedRegister::FromDRegister(D0);
40   } else if (shorty[0] == 'J') {
41     return MipsManagedRegister::FromRegisterPair(V0_V1);
42   } else if (shorty[0] == 'V') {
43     return MipsManagedRegister::NoRegister();
44   } else {
45     return MipsManagedRegister::FromCoreRegister(V0);
46   }
47 }
48 
ReturnRegister()49 ManagedRegister MipsManagedRuntimeCallingConvention::ReturnRegister() {
50   return ReturnRegisterForShorty(GetShorty());
51 }
52 
ReturnRegister()53 ManagedRegister MipsJniCallingConvention::ReturnRegister() {
54   return ReturnRegisterForShorty(GetShorty());
55 }
56 
IntReturnRegister()57 ManagedRegister MipsJniCallingConvention::IntReturnRegister() {
58   return MipsManagedRegister::FromCoreRegister(V0);
59 }
60 
61 // Managed runtime calling convention
62 
MethodRegister()63 ManagedRegister MipsManagedRuntimeCallingConvention::MethodRegister() {
64   return MipsManagedRegister::FromCoreRegister(A0);
65 }
66 
IsCurrentParamInRegister()67 bool MipsManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
68   return false;  // Everything moved to stack on entry.
69 }
70 
IsCurrentParamOnStack()71 bool MipsManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
72   return true;
73 }
74 
CurrentParamRegister()75 ManagedRegister MipsManagedRuntimeCallingConvention::CurrentParamRegister() {
76   LOG(FATAL) << "Should not reach here";
77   return ManagedRegister::NoRegister();
78 }
79 
CurrentParamStackOffset()80 FrameOffset MipsManagedRuntimeCallingConvention::CurrentParamStackOffset() {
81   CHECK(IsCurrentParamOnStack());
82   FrameOffset result =
83       FrameOffset(displacement_.Int32Value() +        // displacement
84                   kFramePointerSize +                 // Method*
85                   (itr_slots_ * kFramePointerSize));  // offset into in args
86   return result;
87 }
88 
EntrySpills()89 const ManagedRegisterEntrySpills& MipsManagedRuntimeCallingConvention::EntrySpills() {
90   // We spill the argument registers on MIPS to free them up for scratch use, we then assume
91   // all arguments are on the stack.
92   if (entry_spills_.size() == 0) {
93     size_t num_spills = NumArgs() + NumLongOrDoubleArgs();
94     if (num_spills > 0) {
95       entry_spills_.push_back(MipsManagedRegister::FromCoreRegister(A1));
96       if (num_spills > 1) {
97         entry_spills_.push_back(MipsManagedRegister::FromCoreRegister(A2));
98         if (num_spills > 2) {
99           entry_spills_.push_back(MipsManagedRegister::FromCoreRegister(A3));
100         }
101       }
102     }
103   }
104   return entry_spills_;
105 }
106 // JNI calling convention
107 
MipsJniCallingConvention(bool is_static,bool is_synchronized,const char * shorty)108 MipsJniCallingConvention::MipsJniCallingConvention(bool is_static, bool is_synchronized,
109                                                    const char* shorty)
110     : JniCallingConvention(is_static, is_synchronized, shorty, kFramePointerSize) {
111   // Compute padding to ensure longs and doubles are not split in AAPCS. Ignore the 'this' jobject
112   // or jclass for static methods and the JNIEnv. We start at the aligned register A2.
113   size_t padding = 0;
114   for (size_t cur_arg = IsStatic() ? 0 : 1, cur_reg = 2; cur_arg < NumArgs(); cur_arg++) {
115     if (IsParamALongOrDouble(cur_arg)) {
116       if ((cur_reg & 1) != 0) {
117         padding += 4;
118         cur_reg++;  // additional bump to ensure alignment
119       }
120       cur_reg++;  // additional bump to skip extra long word
121     }
122     cur_reg++;  // bump the iterator for every argument
123   }
124   padding_ = padding;
125 
126   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T0));
127   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T1));
128   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T2));
129   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T3));
130   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T4));
131   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T5));
132   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T6));
133   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T7));
134   callee_save_regs_.push_back(MipsManagedRegister::FromCoreRegister(T8));
135 }
136 
CoreSpillMask() const137 uint32_t MipsJniCallingConvention::CoreSpillMask() const {
138   // Compute spill mask to agree with callee saves initialized in the constructor
139   uint32_t result = 0;
140   result = 1 << T0 | 1 << T1 | 1 << T2 | 1 << T3 | 1 << T4 | 1 << T5 | 1 << T6 |
141            1 << T7 | 1 << T8 | 1 << RA;
142   return result;
143 }
144 
ReturnScratchRegister() const145 ManagedRegister MipsJniCallingConvention::ReturnScratchRegister() const {
146   return MipsManagedRegister::FromCoreRegister(AT);
147 }
148 
FrameSize()149 size_t MipsJniCallingConvention::FrameSize() {
150   // Method*, LR and callee save area size, local reference segment state
151   size_t frame_data_size = kMipsPointerSize +
152       (2 + CalleeSaveRegisters().size()) * kFramePointerSize;
153   // References plus 2 words for HandleScope header
154   size_t handle_scope_size = HandleScope::SizeOf(kFramePointerSize, ReferenceCount());
155   // Plus return value spill area size
156   return RoundUp(frame_data_size + handle_scope_size + SizeOfReturnValue(), kStackAlignment);
157 }
158 
OutArgSize()159 size_t MipsJniCallingConvention::OutArgSize() {
160   return RoundUp(NumberOfOutgoingStackArgs() * kFramePointerSize + padding_, kStackAlignment);
161 }
162 
163 // JniCallingConvention ABI follows AAPCS where longs and doubles must occur
164 // in even register numbers and stack slots
Next()165 void MipsJniCallingConvention::Next() {
166   JniCallingConvention::Next();
167   size_t arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
168   if ((itr_args_ >= 2) &&
169       (arg_pos < NumArgs()) &&
170       IsParamALongOrDouble(arg_pos)) {
171     // itr_slots_ needs to be an even number, according to AAPCS.
172     if ((itr_slots_ & 0x1u) != 0) {
173       itr_slots_++;
174     }
175   }
176 }
177 
IsCurrentParamInRegister()178 bool MipsJniCallingConvention::IsCurrentParamInRegister() {
179   return itr_slots_ < 4;
180 }
181 
IsCurrentParamOnStack()182 bool MipsJniCallingConvention::IsCurrentParamOnStack() {
183   return !IsCurrentParamInRegister();
184 }
185 
186 static const Register kJniArgumentRegisters[] = {
187   A0, A1, A2, A3
188 };
CurrentParamRegister()189 ManagedRegister MipsJniCallingConvention::CurrentParamRegister() {
190   CHECK_LT(itr_slots_, 4u);
191   int arg_pos = itr_args_ - NumberOfExtraArgumentsForJni();
192   if ((itr_args_ >= 2) && IsParamALongOrDouble(arg_pos)) {
193     CHECK_EQ(itr_slots_, 2u);
194     return MipsManagedRegister::FromRegisterPair(A2_A3);
195   } else {
196     return
197       MipsManagedRegister::FromCoreRegister(kJniArgumentRegisters[itr_slots_]);
198   }
199 }
200 
CurrentParamStackOffset()201 FrameOffset MipsJniCallingConvention::CurrentParamStackOffset() {
202   CHECK_GE(itr_slots_, 4u);
203   size_t offset = displacement_.Int32Value() - OutArgSize() + (itr_slots_ * kFramePointerSize);
204   CHECK_LT(offset, OutArgSize());
205   return FrameOffset(offset);
206 }
207 
NumberOfOutgoingStackArgs()208 size_t MipsJniCallingConvention::NumberOfOutgoingStackArgs() {
209   size_t static_args = IsStatic() ? 1 : 0;  // count jclass
210   // regular argument parameters and this
211   size_t param_args = NumArgs() + NumLongOrDoubleArgs();
212   // count JNIEnv*
213   return static_args + param_args + 1;
214 }
215 }  // namespace mips
216 }  // namespace art
217