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