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