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 "base/arena_object.h"
21 #include "base/array_ref.h"
22 #include "base/enums.h"
23 #include "dex/primitive.h"
24 #include "handle_scope.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,PointerSize frame_pointer_size)74   CallingConvention(bool is_static,
75                     bool is_synchronized,
76                     const char* shorty,
77                     PointerSize frame_pointer_size)
78       : itr_slots_(0), itr_refs_(0), itr_args_(0), itr_longs_and_doubles_(0),
79         itr_float_and_doubles_(0), displacement_(0),
80         frame_pointer_size_(frame_pointer_size),
81         handle_scope_pointer_size_(sizeof(StackReference<mirror::Object>)),
82         is_static_(is_static), is_synchronized_(is_synchronized),
83         shorty_(shorty) {
84     num_args_ = (is_static ? 0 : 1) + strlen(shorty) - 1;
85     num_ref_args_ = is_static ? 0 : 1;  // The implicit this pointer.
86     num_float_or_double_args_ = 0;
87     num_long_or_double_args_ = 0;
88     for (size_t i = 1; i < strlen(shorty); i++) {
89       char ch = shorty_[i];
90       switch (ch) {
91       case 'L':
92         num_ref_args_++;
93         break;
94       case 'J':
95         num_long_or_double_args_++;
96         break;
97       case 'D':
98         num_long_or_double_args_++;
99         num_float_or_double_args_++;
100         break;
101       case 'F':
102         num_float_or_double_args_++;
103         break;
104       }
105     }
106   }
107 
IsStatic()108   bool IsStatic() const {
109     return is_static_;
110   }
IsSynchronized()111   bool IsSynchronized() const {
112     return is_synchronized_;
113   }
IsParamALongOrDouble(unsigned int param)114   bool IsParamALongOrDouble(unsigned int param) const {
115     DCHECK_LT(param, NumArgs());
116     if (IsStatic()) {
117       param++;  // 0th argument must skip return value at start of the shorty
118     } else if (param == 0) {
119       return false;  // this argument
120     }
121     char ch = shorty_[param];
122     return (ch == 'J' || ch == 'D');
123   }
IsParamAFloatOrDouble(unsigned int param)124   bool IsParamAFloatOrDouble(unsigned int param) const {
125     DCHECK_LT(param, NumArgs());
126     if (IsStatic()) {
127       param++;  // 0th argument must skip return value at start of the shorty
128     } else if (param == 0) {
129       return false;  // this argument
130     }
131     char ch = shorty_[param];
132     return (ch == 'F' || ch == 'D');
133   }
IsParamADouble(unsigned int param)134   bool IsParamADouble(unsigned int param) const {
135     DCHECK_LT(param, NumArgs());
136     if (IsStatic()) {
137       param++;  // 0th argument must skip return value at start of the shorty
138     } else if (param == 0) {
139       return false;  // this argument
140     }
141     return shorty_[param] == 'D';
142   }
IsParamALong(unsigned int param)143   bool IsParamALong(unsigned int param) const {
144     DCHECK_LT(param, NumArgs());
145     if (IsStatic()) {
146       param++;  // 0th argument must skip return value at start of the shorty
147     } else if (param == 0) {
148       return false;  // this argument
149     }
150     return shorty_[param] == 'J';
151   }
IsParamAReference(unsigned int param)152   bool IsParamAReference(unsigned int param) const {
153     DCHECK_LT(param, NumArgs());
154     if (IsStatic()) {
155       param++;  // 0th argument must skip return value at start of the shorty
156     } else if (param == 0) {
157       return true;  // this argument
158     }
159     return shorty_[param] == 'L';
160   }
NumArgs()161   size_t NumArgs() const {
162     return num_args_;
163   }
164   // Implicit argument count: 1 for instance functions, 0 for static functions.
165   // (The implicit argument is only relevant to the shorty, i.e.
166   // the 0th arg is not in the shorty if it's implicit).
NumImplicitArgs()167   size_t NumImplicitArgs() const {
168     return IsStatic() ? 0 : 1;
169   }
NumLongOrDoubleArgs()170   size_t NumLongOrDoubleArgs() const {
171     return num_long_or_double_args_;
172   }
NumFloatOrDoubleArgs()173   size_t NumFloatOrDoubleArgs() const {
174     return num_float_or_double_args_;
175   }
NumReferenceArgs()176   size_t NumReferenceArgs() const {
177     return num_ref_args_;
178   }
ParamSize(unsigned int param)179   size_t ParamSize(unsigned int param) const {
180     DCHECK_LT(param, NumArgs());
181     if (IsStatic()) {
182       param++;  // 0th argument must skip return value at start of the shorty
183     } else if (param == 0) {
184       return sizeof(mirror::HeapReference<mirror::Object>);  // this argument
185     }
186     size_t result = Primitive::ComponentSize(Primitive::GetType(shorty_[param]));
187     if (result >= 1 && result < 4) {
188       result = 4;
189     }
190     return result;
191   }
GetShorty()192   const char* GetShorty() const {
193     return shorty_.c_str();
194   }
195   // The slot number for current calling_convention argument.
196   // Note that each slot is 32-bit. When the current argument is bigger
197   // than 32 bits, return the first slot number for this argument.
198   unsigned int itr_slots_;
199   // The number of references iterated past.
200   unsigned int itr_refs_;
201   // The argument number along argument list for current argument.
202   unsigned int itr_args_;
203   // Number of longs and doubles seen along argument list.
204   unsigned int itr_longs_and_doubles_;
205   // Number of float and doubles seen along argument list.
206   unsigned int itr_float_and_doubles_;
207   // Space for frames below this on the stack.
208   FrameOffset displacement_;
209   // The size of a pointer.
210   const PointerSize frame_pointer_size_;
211   // The size of a reference entry within the handle scope.
212   const size_t handle_scope_pointer_size_;
213 
214  private:
215   const bool is_static_;
216   const bool is_synchronized_;
217   std::string shorty_;
218   size_t num_args_;
219   size_t num_ref_args_;
220   size_t num_float_or_double_args_;
221   size_t num_long_or_double_args_;
222 };
223 
224 // Abstraction for managed code's calling conventions
225 // | { Incoming stack args } |
226 // | { Prior Method* }       | <-- Prior SP
227 // | { Return address }      |
228 // | { Callee saves }        |
229 // | { Spills ... }          |
230 // | { Outgoing stack args } |
231 // | { Method* }             | <-- SP
232 class ManagedRuntimeCallingConvention : public CallingConvention {
233  public:
234   static std::unique_ptr<ManagedRuntimeCallingConvention> Create(ArenaAllocator* allocator,
235                                                                  bool is_static,
236                                                                  bool is_synchronized,
237                                                                  const char* shorty,
238                                                                  InstructionSet instruction_set);
239 
240   // Register that holds the incoming method argument
241   virtual ManagedRegister MethodRegister() = 0;
242 
243   // Iterator interface
244   bool HasNext();
245   void Next();
246   bool IsCurrentParamAReference();
247   bool IsCurrentParamAFloatOrDouble();
248   bool IsCurrentParamADouble();
249   bool IsCurrentParamALong();
250   bool IsCurrentArgExplicit();  // ie a non-implict argument such as this
251   bool IsCurrentArgPossiblyNull();
252   size_t CurrentParamSize();
253   virtual bool IsCurrentParamInRegister() = 0;
254   virtual bool IsCurrentParamOnStack() = 0;
255   virtual ManagedRegister CurrentParamRegister() = 0;
256   virtual FrameOffset CurrentParamStackOffset() = 0;
257 
~ManagedRuntimeCallingConvention()258   virtual ~ManagedRuntimeCallingConvention() {}
259 
260   // Registers to spill to caller's out registers on entry.
261   virtual const ManagedRegisterEntrySpills& EntrySpills() = 0;
262 
263  protected:
ManagedRuntimeCallingConvention(bool is_static,bool is_synchronized,const char * shorty,PointerSize frame_pointer_size)264   ManagedRuntimeCallingConvention(bool is_static,
265                                   bool is_synchronized,
266                                   const char* shorty,
267                                   PointerSize frame_pointer_size)
268       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {}
269 };
270 
271 // Abstraction for JNI calling conventions
272 // | { Incoming stack args }         | <-- Prior SP
273 // | { Return address }              |
274 // | { Callee saves }                |     ([1])
275 // | { Return value spill }          |     (live on return slow paths)
276 // | { Local Ref. Table State }      |
277 // | { Stack Indirect Ref. Table     |
278 // |   num. refs./link }             |     (here to prior SP is frame size)
279 // | { Method* }                     | <-- Anchor SP written to thread
280 // | { Outgoing stack args }         | <-- SP at point of call
281 // | Native frame                    |
282 //
283 // [1] We must save all callee saves here to enable any exception throws to restore
284 // callee saves for frames above this one.
285 class JniCallingConvention : public CallingConvention {
286  public:
287   static std::unique_ptr<JniCallingConvention> Create(ArenaAllocator* allocator,
288                                                       bool is_static,
289                                                       bool is_synchronized,
290                                                       bool is_critical_native,
291                                                       const char* shorty,
292                                                       InstructionSet instruction_set);
293 
294   // Size of frame excluding space for outgoing args (its assumed Method* is
295   // always at the bottom of a frame, but this doesn't work for outgoing
296   // native args). Includes alignment.
297   virtual size_t FrameSize() = 0;
298   // Size of outgoing arguments (stack portion), including alignment.
299   // -- Arguments that are passed via registers are excluded from this size.
300   virtual size_t OutArgSize() = 0;
301   // Number of references in stack indirect reference table
302   size_t ReferenceCount() const;
303   // Location where the segment state of the local indirect reference table is saved
304   FrameOffset SavedLocalReferenceCookieOffset() const;
305   // Location where the return value of a call can be squirreled if another
306   // call is made following the native call
307   FrameOffset ReturnValueSaveLocation() const;
308   // Register that holds result if it is integer.
309   virtual ManagedRegister IntReturnRegister() = 0;
310   // Whether the compiler needs to ensure zero-/sign-extension of a small result type
311   virtual bool RequiresSmallResultTypeExtension() const = 0;
312 
313   // Callee save registers to spill prior to native code (which may clobber)
314   virtual ArrayRef<const ManagedRegister> CalleeSaveRegisters() const = 0;
315 
316   // Spill mask values
317   virtual uint32_t CoreSpillMask() const = 0;
318   virtual uint32_t FpSpillMask() const = 0;
319 
320   // An extra scratch register live after the call
321   virtual ManagedRegister ReturnScratchRegister() const = 0;
322 
323   // Iterator interface
324   bool HasNext();
325   virtual void Next();
326   bool IsCurrentParamAReference();
327   bool IsCurrentParamAFloatOrDouble();
328   bool IsCurrentParamADouble();
329   bool IsCurrentParamALong();
IsCurrentParamALongOrDouble()330   bool IsCurrentParamALongOrDouble() {
331     return IsCurrentParamALong() || IsCurrentParamADouble();
332   }
333   bool IsCurrentParamJniEnv();
334   size_t CurrentParamSize() const;
335   virtual bool IsCurrentParamInRegister() = 0;
336   virtual bool IsCurrentParamOnStack() = 0;
337   virtual ManagedRegister CurrentParamRegister() = 0;
338   virtual FrameOffset CurrentParamStackOffset() = 0;
339 
340   // Iterator interface extension for JNI
341   FrameOffset CurrentParamHandleScopeEntryOffset();
342 
343   // Position of handle scope and interior fields
HandleScopeOffset()344   FrameOffset HandleScopeOffset() const {
345     return FrameOffset(this->displacement_.Int32Value() + static_cast<size_t>(frame_pointer_size_));
346     // above Method reference
347   }
348 
HandleScopeLinkOffset()349   FrameOffset HandleScopeLinkOffset() const {
350     return FrameOffset(HandleScopeOffset().Int32Value() +
351                        HandleScope::LinkOffset(frame_pointer_size_));
352   }
353 
HandleScopeNumRefsOffset()354   FrameOffset HandleScopeNumRefsOffset() const {
355     return FrameOffset(HandleScopeOffset().Int32Value() +
356                        HandleScope::NumberOfReferencesOffset(frame_pointer_size_));
357   }
358 
HandleReferencesOffset()359   FrameOffset HandleReferencesOffset() const {
360     return FrameOffset(HandleScopeOffset().Int32Value() +
361                        HandleScope::ReferencesOffset(frame_pointer_size_));
362   }
363 
~JniCallingConvention()364   virtual ~JniCallingConvention() {}
365 
366  protected:
367   // Named iterator positions
368   enum IteratorPos {
369     kJniEnv = 0,
370     kObjectOrClass = 1
371   };
372 
JniCallingConvention(bool is_static,bool is_synchronized,bool is_critical_native,const char * shorty,PointerSize frame_pointer_size)373   JniCallingConvention(bool is_static,
374                        bool is_synchronized,
375                        bool is_critical_native,
376                        const char* shorty,
377                        PointerSize frame_pointer_size)
378       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size),
379         is_critical_native_(is_critical_native) {}
380 
381   // Number of stack slots for outgoing arguments, above which the handle scope is
382   // located
383   virtual size_t NumberOfOutgoingStackArgs() = 0;
384 
385  protected:
386   size_t NumberOfExtraArgumentsForJni() const;
387 
388   // Does the transition have a StackHandleScope?
389   bool HasHandleScope() const;
390   // Does the transition have a local reference segment state?
391   bool HasLocalReferenceSegmentState() const;
392   // Has a JNIEnv* parameter implicitly?
393   bool HasJniEnv() const;
394   // Has a 'jclass' parameter implicitly?
395   bool HasSelfClass() const;
396 
397   // Are there extra JNI arguments (JNIEnv* and maybe jclass)?
398   bool HasExtraArgumentsForJni() const;
399 
400   // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments.
401   unsigned int GetIteratorPositionWithinShorty() const;
402 
403   // Is the current argument (at the iterator) an extra argument for JNI?
404   bool IsCurrentArgExtraForJni() const;
405 
406   const bool is_critical_native_;
407 
408  private:
409   // Shorthand for switching on the switch value but only IF there are extra JNI arguments.
410   //
411   // Puts the case value into return_value.
412   // * (switch_value == kJniEnv) => case_jni_env
413   // * (switch_value == kObjectOrClass) => case_object_or_class
414   //
415   // Returns false otherwise (or if there are no extra JNI arguments).
416   bool SwitchExtraJniArguments(size_t switch_value,
417                                bool case_jni_env,
418                                bool case_object_or_class,
419                                /* out parameters */
420                                bool* return_value) const;
421 };
422 
423 }  // namespace art
424 
425 #endif  // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
426