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