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
23namespace art {
24namespace mips {
25
26// Calling convention
27ManagedRegister MipsManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
28  return MipsManagedRegister::FromCoreRegister(T9);
29}
30
31ManagedRegister MipsJniCallingConvention::InterproceduralScratchRegister() {
32  return MipsManagedRegister::FromCoreRegister(T9);
33}
34
35static 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
49ManagedRegister MipsManagedRuntimeCallingConvention::ReturnRegister() {
50  return ReturnRegisterForShorty(GetShorty());
51}
52
53ManagedRegister MipsJniCallingConvention::ReturnRegister() {
54  return ReturnRegisterForShorty(GetShorty());
55}
56
57ManagedRegister MipsJniCallingConvention::IntReturnRegister() {
58  return MipsManagedRegister::FromCoreRegister(V0);
59}
60
61// Managed runtime calling convention
62
63ManagedRegister MipsManagedRuntimeCallingConvention::MethodRegister() {
64  return MipsManagedRegister::FromCoreRegister(A0);
65}
66
67bool MipsManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
68  return false;  // Everything moved to stack on entry.
69}
70
71bool MipsManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
72  return true;
73}
74
75ManagedRegister MipsManagedRuntimeCallingConvention::CurrentParamRegister() {
76  LOG(FATAL) << "Should not reach here";
77  return ManagedRegister::NoRegister();
78}
79
80FrameOffset 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
89const 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
108MipsJniCallingConvention::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
137uint32_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
145ManagedRegister MipsJniCallingConvention::ReturnScratchRegister() const {
146  return MipsManagedRegister::FromCoreRegister(AT);
147}
148
149size_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
159size_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
165void 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
178bool MipsJniCallingConvention::IsCurrentParamInRegister() {
179  return itr_slots_ < 4;
180}
181
182bool MipsJniCallingConvention::IsCurrentParamOnStack() {
183  return !IsCurrentParamInRegister();
184}
185
186static const Register kJniArgumentRegisters[] = {
187  A0, A1, A2, A3
188};
189ManagedRegister 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
201FrameOffset 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
208size_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