• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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() const = 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_fast_native,
295                                                       bool is_critical_native,
296                                                       const char* shorty,
297                                                       InstructionSet instruction_set);
298 
299   // Size of frame excluding space for outgoing args (its assumed Method* is
300   // always at the bottom of a frame, but this doesn't work for outgoing
301   // native args). Includes alignment.
302   virtual size_t FrameSize() const = 0;
303   // Size of outgoing frame, i.e. stack arguments, @CriticalNative return PC if needed, alignment.
304   // -- Arguments that are passed via registers are excluded from this size.
305   virtual size_t OutFrameSize() const = 0;
306   // Number of references in stack indirect reference table
307   size_t ReferenceCount() const;
308   // Register that holds result if it is integer.
309   virtual ManagedRegister IntReturnRegister() const = 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   // Subset of core callee save registers that can be used for arbitrary purposes after
317   // constructing the JNI transition frame. These should be managed callee-saves as well.
318   // These should not include special purpose registers such as thread register.
319   // JNI compiler currently requires at least 3 callee save scratch registers.
320   virtual ArrayRef<const ManagedRegister> CalleeSaveScratchRegisters() const = 0;
321 
322   // Subset of core argument registers that can be used for arbitrary purposes after
323   // calling the native function. These should exclude the return register(s).
324   virtual ArrayRef<const ManagedRegister> ArgumentScratchRegisters() const = 0;
325 
326   // Spill mask values
327   virtual uint32_t CoreSpillMask() const = 0;
328   virtual uint32_t FpSpillMask() const = 0;
329 
330   // Iterator interface
331   bool HasNext();
332   virtual void Next();
333   bool IsCurrentParamAReference();
334   bool IsCurrentParamAFloatOrDouble();
335   bool IsCurrentParamADouble();
336   bool IsCurrentParamALong();
IsCurrentParamALongOrDouble()337   bool IsCurrentParamALongOrDouble() {
338     return IsCurrentParamALong() || IsCurrentParamADouble();
339   }
340   bool IsCurrentParamJniEnv();
341   size_t CurrentParamSize() const;
342   virtual bool IsCurrentParamInRegister() = 0;
343   virtual bool IsCurrentParamOnStack() = 0;
344   virtual ManagedRegister CurrentParamRegister() = 0;
345   virtual FrameOffset CurrentParamStackOffset() = 0;
346 
~JniCallingConvention()347   virtual ~JniCallingConvention() {}
348 
SavedLocalReferenceCookieSize()349   static constexpr size_t SavedLocalReferenceCookieSize() {
350     return 4u;
351   }
352 
IsFastNative()353   bool IsFastNative() const {
354     return is_fast_native_;
355   }
356 
IsCriticalNative()357   bool IsCriticalNative() const {
358     return is_critical_native_;
359   }
360 
361   // Does the transition have a method pointer in the stack frame?
SpillsMethod()362   bool SpillsMethod() const {
363     // Exclude method pointer for @CriticalNative methods for optimization speed.
364     return !IsCriticalNative();
365   }
366 
367   // Locking argument register, used to pass the synchronization object for calls
368   // to `JniLockObject()` and `JniUnlockObject()`.
369   virtual ManagedRegister LockingArgumentRegister() const = 0;
370 
371   // Hidden argument register, used to pass the method pointer for @CriticalNative call.
372   virtual ManagedRegister HiddenArgumentRegister() const = 0;
373 
374   // Whether to use tail call (used only for @CriticalNative).
375   virtual bool UseTailCall() const = 0;
376 
377   // Whether the return type is small. Used for RequiresSmallResultTypeExtension()
378   // on architectures that require the sign/zero extension.
HasSmallReturnType()379   bool HasSmallReturnType() const {
380     Primitive::Type return_type = GetReturnType();
381     return return_type == Primitive::kPrimByte ||
382            return_type == Primitive::kPrimShort ||
383            return_type == Primitive::kPrimBoolean ||
384            return_type == Primitive::kPrimChar;
385   }
386 
387  protected:
388   // Named iterator positions
389   enum IteratorPos {
390     kJniEnv = 0,
391     kObjectOrClass = 1
392   };
393 
JniCallingConvention(bool is_static,bool is_synchronized,bool is_fast_native,bool is_critical_native,const char * shorty,PointerSize frame_pointer_size)394   JniCallingConvention(bool is_static,
395                        bool is_synchronized,
396                        bool is_fast_native,
397                        bool is_critical_native,
398                        const char* shorty,
399                        PointerSize frame_pointer_size)
400       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size),
401         is_fast_native_(is_fast_native),
402         is_critical_native_(is_critical_native) {}
403 
404  protected:
405   size_t NumberOfExtraArgumentsForJni() const;
406 
407   // Does the transition have a local reference segment state?
HasLocalReferenceSegmentState()408   bool HasLocalReferenceSegmentState() const {
409     // Exclude local reference segment states for @CriticalNative methods for optimization speed.
410     return !IsCriticalNative();
411   }
412 
413   // Are there extra JNI arguments (JNIEnv* and maybe jclass)?
HasExtraArgumentsForJni()414   bool HasExtraArgumentsForJni() const {
415     // @CriticalNative jni implementations exclude both JNIEnv* and the jclass/jobject parameters.
416     return !IsCriticalNative();
417   }
418 
419   // Has a JNIEnv* parameter implicitly?
HasJniEnv()420   bool HasJniEnv() const {
421     // Exclude "JNIEnv*" parameter for @CriticalNative methods.
422     return HasExtraArgumentsForJni();
423   }
424 
425   // Has a 'jclass' parameter implicitly?
426   bool HasSelfClass() const;
427 
428   // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments.
429   unsigned int GetIteratorPositionWithinShorty() const;
430 
431   // Is the current argument (at the iterator) an extra argument for JNI?
432   bool IsCurrentArgExtraForJni() const;
433 
434   const bool is_fast_native_;
435   const bool is_critical_native_;
436 
437  private:
438   // Shorthand for switching on the switch value but only IF there are extra JNI arguments.
439   //
440   // Puts the case value into return_value.
441   // * (switch_value == kJniEnv) => case_jni_env
442   // * (switch_value == kObjectOrClass) => case_object_or_class
443   //
444   // Returns false otherwise (or if there are no extra JNI arguments).
445   bool SwitchExtraJniArguments(size_t switch_value,
446                                bool case_jni_env,
447                                bool case_object_or_class,
448                                /* out parameters */
449                                bool* return_value) const;
450 };
451 
452 }  // namespace art
453 
454 #endif  // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
455