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 #ifndef ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
18 #define ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
19 
20 #include <vector>
21 #include "handle_scope.h"
22 #include "primitive.h"
23 #include "thread.h"
24 #include "utils/managed_register.h"
25 
26 namespace art {
27 
28 // Top-level abstraction for different calling conventions.
29 class CallingConvention {
30  public:
IsReturnAReference()31   bool IsReturnAReference() const { return shorty_[0] == 'L'; }
32 
GetReturnType()33   Primitive::Type GetReturnType() const {
34     return Primitive::GetType(shorty_[0]);
35   }
36 
SizeOfReturnValue()37   size_t SizeOfReturnValue() const {
38     size_t result = Primitive::ComponentSize(Primitive::GetType(shorty_[0]));
39     if (result >= 1 && result < 4) {
40       result = 4;
41     }
42     return result;
43   }
44 
45   // Register that holds result of this method invocation.
46   virtual ManagedRegister ReturnRegister() = 0;
47   // Register reserved for scratch usage during procedure calls.
48   virtual ManagedRegister InterproceduralScratchRegister() = 0;
49 
50   // Offset of Method within the frame.
MethodStackOffset()51   FrameOffset MethodStackOffset() {
52     return displacement_;
53   }
54 
55   // Iterator interface
56 
57   // Place iterator at start of arguments. The displacement is applied to
58   // frame offset methods to account for frames which may be on the stack
59   // below the one being iterated over.
ResetIterator(FrameOffset displacement)60   void ResetIterator(FrameOffset displacement) {
61     displacement_ = displacement;
62     itr_slots_ = 0;
63     itr_args_ = 0;
64     itr_refs_ = 0;
65     itr_longs_and_doubles_ = 0;
66     itr_float_and_doubles_ = 0;
67   }
68 
~CallingConvention()69   virtual ~CallingConvention() {}
70 
71  protected:
CallingConvention(bool is_static,bool is_synchronized,const char * shorty,size_t frame_pointer_size)72   CallingConvention(bool is_static, bool is_synchronized, const char* shorty,
73                     size_t frame_pointer_size)
74       : itr_slots_(0), itr_refs_(0), itr_args_(0), itr_longs_and_doubles_(0),
75         itr_float_and_doubles_(0), displacement_(0),
76         frame_pointer_size_(frame_pointer_size),
77         handle_scope_pointer_size_(sizeof(StackReference<mirror::Object>)),
78         is_static_(is_static), is_synchronized_(is_synchronized),
79         shorty_(shorty) {
80     num_args_ = (is_static ? 0 : 1) + strlen(shorty) - 1;
81     num_ref_args_ = is_static ? 0 : 1;  // The implicit this pointer.
82     num_float_or_double_args_ = 0;
83     num_long_or_double_args_ = 0;
84     for (size_t i = 1; i < strlen(shorty); i++) {
85       char ch = shorty_[i];
86       switch (ch) {
87       case 'L':
88         num_ref_args_++;
89         break;
90       case 'J':
91         num_long_or_double_args_++;
92         break;
93       case 'D':
94         num_long_or_double_args_++;
95         num_float_or_double_args_++;
96         break;
97       case 'F':
98         num_float_or_double_args_++;
99         break;
100       }
101     }
102   }
103 
IsStatic()104   bool IsStatic() const {
105     return is_static_;
106   }
IsSynchronized()107   bool IsSynchronized() const {
108     return is_synchronized_;
109   }
IsParamALongOrDouble(unsigned int param)110   bool IsParamALongOrDouble(unsigned int param) const {
111     DCHECK_LT(param, NumArgs());
112     if (IsStatic()) {
113       param++;  // 0th argument must skip return value at start of the shorty
114     } else if (param == 0) {
115       return false;  // this argument
116     }
117     char ch = shorty_[param];
118     return (ch == 'J' || ch == 'D');
119   }
IsParamAFloatOrDouble(unsigned int param)120   bool IsParamAFloatOrDouble(unsigned int param) const {
121     DCHECK_LT(param, NumArgs());
122     if (IsStatic()) {
123       param++;  // 0th argument must skip return value at start of the shorty
124     } else if (param == 0) {
125       return false;  // this argument
126     }
127     char ch = shorty_[param];
128     return (ch == 'F' || ch == 'D');
129   }
IsParamADouble(unsigned int param)130   bool IsParamADouble(unsigned int param) const {
131     DCHECK_LT(param, NumArgs());
132     if (IsStatic()) {
133       param++;  // 0th argument must skip return value at start of the shorty
134     } else if (param == 0) {
135       return false;  // this argument
136     }
137     return shorty_[param] == 'D';
138   }
IsParamALong(unsigned int param)139   bool IsParamALong(unsigned int param) const {
140     DCHECK_LT(param, NumArgs());
141     if (IsStatic()) {
142       param++;  // 0th argument must skip return value at start of the shorty
143     } else if (param == 0) {
144       return true;  // this argument
145     }
146     return shorty_[param] == 'J';
147   }
IsParamAReference(unsigned int param)148   bool IsParamAReference(unsigned int param) const {
149     DCHECK_LT(param, NumArgs());
150     if (IsStatic()) {
151       param++;  // 0th argument must skip return value at start of the shorty
152     } else if (param == 0) {
153       return true;  // this argument
154     }
155     return shorty_[param] == 'L';
156   }
NumArgs()157   size_t NumArgs() const {
158     return num_args_;
159   }
NumLongOrDoubleArgs()160   size_t NumLongOrDoubleArgs() const {
161     return num_long_or_double_args_;
162   }
NumFloatOrDoubleArgs()163   size_t NumFloatOrDoubleArgs() const {
164     return num_float_or_double_args_;
165   }
NumReferenceArgs()166   size_t NumReferenceArgs() const {
167     return num_ref_args_;
168   }
ParamSize(unsigned int param)169   size_t ParamSize(unsigned int param) const {
170     DCHECK_LT(param, NumArgs());
171     if (IsStatic()) {
172       param++;  // 0th argument must skip return value at start of the shorty
173     } else if (param == 0) {
174       return frame_pointer_size_;  // this argument
175     }
176     size_t result = Primitive::ComponentSize(Primitive::GetType(shorty_[param]));
177     if (result >= 1 && result < 4) {
178       result = 4;
179     }
180     return result;
181   }
GetShorty()182   const char* GetShorty() const {
183     return shorty_.c_str();
184   }
185   // The slot number for current calling_convention argument.
186   // Note that each slot is 32-bit. When the current argument is bigger
187   // than 32 bits, return the first slot number for this argument.
188   unsigned int itr_slots_;
189   // The number of references iterated past.
190   unsigned int itr_refs_;
191   // The argument number along argument list for current argument.
192   unsigned int itr_args_;
193   // Number of longs and doubles seen along argument list.
194   unsigned int itr_longs_and_doubles_;
195   // Number of float and doubles seen along argument list.
196   unsigned int itr_float_and_doubles_;
197   // Space for frames below this on the stack.
198   FrameOffset displacement_;
199   // The size of a reference.
200   const size_t frame_pointer_size_;
201   // The size of a reference entry within the handle scope.
202   const size_t handle_scope_pointer_size_;
203 
204  private:
205   const bool is_static_;
206   const bool is_synchronized_;
207   std::string shorty_;
208   size_t num_args_;
209   size_t num_ref_args_;
210   size_t num_float_or_double_args_;
211   size_t num_long_or_double_args_;
212 };
213 
214 // Abstraction for managed code's calling conventions
215 // | { Incoming stack args } |
216 // | { Prior Method* }       | <-- Prior SP
217 // | { Return address }      |
218 // | { Callee saves }        |
219 // | { Spills ... }          |
220 // | { Outgoing stack args } |
221 // | { Method* }             | <-- SP
222 class ManagedRuntimeCallingConvention : public CallingConvention {
223  public:
224   static ManagedRuntimeCallingConvention* Create(bool is_static, bool is_synchronized,
225                                                  const char* shorty,
226                                                  InstructionSet instruction_set);
227 
228   // Register that holds the incoming method argument
229   virtual ManagedRegister MethodRegister() = 0;
230 
231   // Iterator interface
232   bool HasNext();
233   void Next();
234   bool IsCurrentParamAReference();
235   bool IsCurrentParamAFloatOrDouble();
236   bool IsCurrentParamADouble();
237   bool IsCurrentParamALong();
238   bool IsCurrentArgExplicit();  // ie a non-implict argument such as this
239   bool IsCurrentArgPossiblyNull();
240   size_t CurrentParamSize();
241   virtual bool IsCurrentParamInRegister() = 0;
242   virtual bool IsCurrentParamOnStack() = 0;
243   virtual ManagedRegister CurrentParamRegister() = 0;
244   virtual FrameOffset CurrentParamStackOffset() = 0;
245 
~ManagedRuntimeCallingConvention()246   virtual ~ManagedRuntimeCallingConvention() {}
247 
248   // Registers to spill to caller's out registers on entry.
249   virtual const ManagedRegisterEntrySpills& EntrySpills() = 0;
250 
251  protected:
ManagedRuntimeCallingConvention(bool is_static,bool is_synchronized,const char * shorty,size_t frame_pointer_size)252   ManagedRuntimeCallingConvention(bool is_static, bool is_synchronized, const char* shorty,
253                                   size_t frame_pointer_size)
254       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {}
255 };
256 
257 // Abstraction for JNI calling conventions
258 // | { Incoming stack args }         | <-- Prior SP
259 // | { Return address }              |
260 // | { Callee saves }                |     ([1])
261 // | { Return value spill }          |     (live on return slow paths)
262 // | { Local Ref. Table State }      |
263 // | { Stack Indirect Ref. Table     |
264 // |   num. refs./link }             |     (here to prior SP is frame size)
265 // | { Method* }                     | <-- Anchor SP written to thread
266 // | { Outgoing stack args }         | <-- SP at point of call
267 // | Native frame                    |
268 //
269 // [1] We must save all callee saves here to enable any exception throws to restore
270 // callee saves for frames above this one.
271 class JniCallingConvention : public CallingConvention {
272  public:
273   static JniCallingConvention* Create(bool is_static, bool is_synchronized, const char* shorty,
274                                       InstructionSet instruction_set);
275 
276   // Size of frame excluding space for outgoing args (its assumed Method* is
277   // always at the bottom of a frame, but this doesn't work for outgoing
278   // native args). Includes alignment.
279   virtual size_t FrameSize() = 0;
280   // Size of outgoing arguments, including alignment
281   virtual size_t OutArgSize() = 0;
282   // Number of references in stack indirect reference table
283   size_t ReferenceCount() const;
284   // Location where the segment state of the local indirect reference table is saved
285   FrameOffset SavedLocalReferenceCookieOffset() const;
286   // Location where the return value of a call can be squirreled if another
287   // call is made following the native call
288   FrameOffset ReturnValueSaveLocation() const;
289   // Register that holds result if it is integer.
290   virtual ManagedRegister IntReturnRegister() = 0;
291   // Whether the compiler needs to ensure zero-/sign-extension of a small result type
292   virtual bool RequiresSmallResultTypeExtension() const = 0;
293 
294   // Callee save registers to spill prior to native code (which may clobber)
295   virtual const std::vector<ManagedRegister>& CalleeSaveRegisters() const = 0;
296 
297   // Spill mask values
298   virtual uint32_t CoreSpillMask() const = 0;
299   virtual uint32_t FpSpillMask() const = 0;
300 
301   // An extra scratch register live after the call
302   virtual ManagedRegister ReturnScratchRegister() const = 0;
303 
304   // Iterator interface
305   bool HasNext();
306   virtual void Next();
307   bool IsCurrentParamAReference();
308   bool IsCurrentParamAFloatOrDouble();
309   bool IsCurrentParamADouble();
310   bool IsCurrentParamALong();
311   bool IsCurrentParamJniEnv();
312   size_t CurrentParamSize();
313   virtual bool IsCurrentParamInRegister() = 0;
314   virtual bool IsCurrentParamOnStack() = 0;
315   virtual ManagedRegister CurrentParamRegister() = 0;
316   virtual FrameOffset CurrentParamStackOffset() = 0;
317 
318   // Iterator interface extension for JNI
319   FrameOffset CurrentParamHandleScopeEntryOffset();
320 
321   // Position of handle scope and interior fields
HandleScopeOffset()322   FrameOffset HandleScopeOffset() const {
323     return FrameOffset(this->displacement_.Int32Value() + sizeof(StackReference<mirror::ArtMethod>));
324     // above Method reference
325   }
326 
HandleScopeLinkOffset()327   FrameOffset HandleScopeLinkOffset() const {
328     return FrameOffset(HandleScopeOffset().Int32Value() + HandleScope::LinkOffset(frame_pointer_size_));
329   }
330 
HandleScopeNumRefsOffset()331   FrameOffset HandleScopeNumRefsOffset() const {
332     return FrameOffset(HandleScopeOffset().Int32Value() +
333                        HandleScope::NumberOfReferencesOffset(frame_pointer_size_));
334   }
335 
HandleerencesOffset()336   FrameOffset HandleerencesOffset() const {
337     return FrameOffset(HandleScopeOffset().Int32Value() +
338                        HandleScope::ReferencesOffset(frame_pointer_size_));
339   }
340 
~JniCallingConvention()341   virtual ~JniCallingConvention() {}
342 
343  protected:
344   // Named iterator positions
345   enum IteratorPos {
346     kJniEnv = 0,
347     kObjectOrClass = 1
348   };
349 
JniCallingConvention(bool is_static,bool is_synchronized,const char * shorty,size_t frame_pointer_size)350   explicit JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty,
351                                 size_t frame_pointer_size)
352       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {}
353 
354   // Number of stack slots for outgoing arguments, above which the handle scope is
355   // located
356   virtual size_t NumberOfOutgoingStackArgs() = 0;
357 
358  protected:
359   size_t NumberOfExtraArgumentsForJni();
360 };
361 
362 }  // namespace art
363 
364 #endif  // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
365