Home | History | Annotate | Download | only in mips
      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
     31 ManagedRegister MipsManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
     32   return MipsManagedRegister::FromCoreRegister(T9);
     33 }
     34 
     35 ManagedRegister MipsJniCallingConvention::InterproceduralScratchRegister() {
     36   return MipsManagedRegister::FromCoreRegister(T9);
     37 }
     38 
     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 
     53 ManagedRegister MipsManagedRuntimeCallingConvention::ReturnRegister() {
     54   return ReturnRegisterForShorty(GetShorty());
     55 }
     56 
     57 ManagedRegister MipsJniCallingConvention::ReturnRegister() {
     58   return ReturnRegisterForShorty(GetShorty());
     59 }
     60 
     61 ManagedRegister MipsJniCallingConvention::IntReturnRegister() {
     62   return MipsManagedRegister::FromCoreRegister(V0);
     63 }
     64 
     65 // Managed runtime calling convention
     66 
     67 ManagedRegister MipsManagedRuntimeCallingConvention::MethodRegister() {
     68   return MipsManagedRegister::FromCoreRegister(A0);
     69 }
     70 
     71 bool MipsManagedRuntimeCallingConvention::IsCurrentParamInRegister() {
     72   return false;  // Everything moved to stack on entry.
     73 }
     74 
     75 bool MipsManagedRuntimeCallingConvention::IsCurrentParamOnStack() {
     76   return true;
     77 }
     78 
     79 ManagedRegister MipsManagedRuntimeCallingConvention::CurrentParamRegister() {
     80   LOG(FATAL) << "Should not reach here";
     81   return ManagedRegister::NoRegister();
     82 }
     83 
     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 
     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 
    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 
    174 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 
    181 ManagedRegister MipsJniCallingConvention::ReturnScratchRegister() const {
    182   return MipsManagedRegister::FromCoreRegister(AT);
    183 }
    184 
    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 
    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
    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 
    214 bool MipsJniCallingConvention::IsCurrentParamInRegister() {
    215   return itr_slots_ < 4;
    216 }
    217 
    218 bool MipsJniCallingConvention::IsCurrentParamOnStack() {
    219   return !IsCurrentParamInRegister();
    220 }
    221 
    222 static const Register kJniArgumentRegisters[] = {
    223   A0, A1, A2, A3
    224 };
    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 
    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 
    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