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