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/macros.h"
23 #include "base/pointer_size.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,std::string_view shorty,PointerSize frame_pointer_size)77   CallingConvention(bool is_static,
78                     bool is_synchronized,
79                     std::string_view 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) + shorty.length() - 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 < shorty.length(); 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(size_t param,size_t reference_size)181   size_t ParamSize(size_t param, size_t reference_size) 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 reference_size;  // this argument
187     }
188     Primitive::Type type = Primitive::GetType(shorty_[param]);
189     if (type == Primitive::kPrimNot) {
190       return reference_size;
191     }
192     size_t result = Primitive::ComponentSize(type);
193     if (result >= 1 && result < 4) {
194       result = 4;
195     }
196     return result;
197   }
GetShorty()198   std::string_view GetShorty() const {
199     return shorty_;
200   }
201   // The slot number for current calling_convention argument.
202   // Note that each slot is 32-bit. When the current argument is bigger
203   // than 32 bits, return the first slot number for this argument.
204   unsigned int itr_slots_;
205   // The number of references iterated past.
206   unsigned int itr_refs_;
207   // The argument number along argument list for current argument.
208   unsigned int itr_args_;
209   // Number of longs and doubles seen along argument list.
210   unsigned int itr_longs_and_doubles_;
211   // Number of float and doubles seen along argument list.
212   unsigned int itr_float_and_doubles_;
213   // Space for frames below this on the stack.
214   FrameOffset displacement_;
215   // The size of a pointer.
216   const PointerSize frame_pointer_size_;
217 
218  private:
219   const bool is_static_;
220   const bool is_synchronized_;
221   std::string shorty_;
222   size_t num_args_;
223   size_t num_ref_args_;
224   size_t num_float_or_double_args_;
225   size_t num_long_or_double_args_;
226 };
227 
228 // Abstraction for managed code's calling conventions
229 // | { Incoming stack args } |
230 // | { Prior Method* }       | <-- Prior SP
231 // | { Return address }      |
232 // | { Callee saves }        |
233 // | { Spills ... }          |
234 // | { Outgoing stack args } |
235 // | { Method* }             | <-- SP
236 class ManagedRuntimeCallingConvention : public CallingConvention {
237  public:
238   static std::unique_ptr<ManagedRuntimeCallingConvention> Create(ArenaAllocator* allocator,
239                                                                  bool is_static,
240                                                                  bool is_synchronized,
241                                                                  std::string_view shorty,
242                                                                  InstructionSet instruction_set);
243 
244   // Offset of Method within the managed frame.
MethodStackOffset()245   FrameOffset MethodStackOffset() {
246     return FrameOffset(0u);
247   }
248 
249   // Register that holds the incoming method argument
250   virtual ManagedRegister MethodRegister() = 0;
251 
252   // Register that is used to pass frame size for method exit hook call. This
253   // shouldn't be the same as the return register since method exit hook also expects
254   // return values in the return register.
255   virtual ManagedRegister ArgumentRegisterForMethodExitHook() = 0;
256 
257   // Iterator interface
258   bool HasNext();
259   virtual void Next();
260   bool IsCurrentParamAReference();
261   bool IsCurrentParamAFloatOrDouble();
262   bool IsCurrentParamADouble();
263   bool IsCurrentParamALong();
IsCurrentParamALongOrDouble()264   bool IsCurrentParamALongOrDouble() {
265     return IsCurrentParamALong() || IsCurrentParamADouble();
266   }
267   bool IsCurrentArgExplicit();  // ie a non-implict argument such as this
268   bool IsCurrentArgPossiblyNull();
269   size_t CurrentParamSize();
270   virtual bool IsCurrentParamInRegister() = 0;
271   virtual bool IsCurrentParamOnStack() = 0;
272   virtual ManagedRegister CurrentParamRegister() = 0;
273   virtual FrameOffset CurrentParamStackOffset() = 0;
274 
~ManagedRuntimeCallingConvention()275   virtual ~ManagedRuntimeCallingConvention() {}
276 
277  protected:
ManagedRuntimeCallingConvention(bool is_static,bool is_synchronized,std::string_view shorty,PointerSize frame_pointer_size)278   ManagedRuntimeCallingConvention(bool is_static,
279                                   bool is_synchronized,
280                                   std::string_view shorty,
281                                   PointerSize frame_pointer_size)
282       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {}
283 };
284 
285 // Abstraction for JNI calling conventions
286 // | { Incoming stack args }         | <-- Prior SP
287 // | { Return address }              |
288 // | { Callee saves }                |     ([1])
289 // | { Return value spill }          |     (live on return slow paths)
290 // | { Local Ref. Table State }      |
291 // | { Stack Indirect Ref. Table     |
292 // |   num. refs./link }             |     (here to prior SP is frame size)
293 // | { Method* }                     | <-- Anchor SP written to thread
294 // | { Outgoing stack args }         | <-- SP at point of call
295 // | Native frame                    |
296 //
297 // [1] We must save all callee saves here to enable any exception throws to restore
298 // callee saves for frames above this one.
299 class JniCallingConvention : public CallingConvention {
300  public:
301   static std::unique_ptr<JniCallingConvention> Create(ArenaAllocator* allocator,
302                                                       bool is_static,
303                                                       bool is_synchronized,
304                                                       bool is_fast_native,
305                                                       bool is_critical_native,
306                                                       std::string_view shorty,
307                                                       InstructionSet instruction_set);
308 
309   // Size of frame excluding space for outgoing args (its assumed Method* is
310   // always at the bottom of a frame, but this doesn't work for outgoing
311   // native args). Includes alignment.
312   virtual size_t FrameSize() const = 0;
313   // Size of outgoing frame, i.e. stack arguments, @CriticalNative return PC if needed, alignment.
314   // -- Arguments that are passed via registers are excluded from this size.
315   virtual size_t OutFrameSize() const = 0;
316   // Number of references in stack indirect reference table
317   size_t ReferenceCount() const;
318   // Register that holds result if it is integer.
319   virtual ManagedRegister IntReturnRegister() const = 0;
320   // Whether the compiler needs to ensure zero-/sign-extension of a small result type
321   virtual bool RequiresSmallResultTypeExtension() const = 0;
322 
323   // Callee save registers to spill prior to native code (which may clobber)
324   virtual ArrayRef<const ManagedRegister> CalleeSaveRegisters() const = 0;
325 
326   // Subset of core callee save registers that can be used for arbitrary purposes after
327   // constructing the JNI transition frame. These should be both managed and native callee-saves.
328   // These should not include special purpose registers such as thread register.
329   // JNI compiler currently requires at least 4 callee save scratch registers, except for x86
330   // where we have only 3 such registers but all args are passed on stack, so the method register
331   // is never clobbered by argument moves and does not need to be preserved elsewhere.
332   virtual ArrayRef<const ManagedRegister> CalleeSaveScratchRegisters() const = 0;
333 
334   // Subset of core argument registers that can be used for arbitrary purposes after
335   // calling the native function. These should exclude the return register(s).
336   virtual ArrayRef<const ManagedRegister> ArgumentScratchRegisters() const = 0;
337 
338   // Spill mask values
339   virtual uint32_t CoreSpillMask() const = 0;
340   virtual uint32_t FpSpillMask() const = 0;
341 
342   // Iterator interface
343   bool HasNext();
344   virtual void Next();
345   bool IsCurrentParamAReference();
346   bool IsCurrentParamAFloatOrDouble();
347   bool IsCurrentParamADouble();
348   bool IsCurrentParamALong();
IsCurrentParamALongOrDouble()349   bool IsCurrentParamALongOrDouble() {
350     return IsCurrentParamALong() || IsCurrentParamADouble();
351   }
352   bool IsCurrentParamJniEnv();
353   virtual size_t CurrentParamSize() const;
354   virtual bool IsCurrentParamInRegister() = 0;
355   virtual bool IsCurrentParamOnStack() = 0;
356   virtual ManagedRegister CurrentParamRegister() = 0;
357   virtual FrameOffset CurrentParamStackOffset() = 0;
358 
~JniCallingConvention()359   virtual ~JniCallingConvention() {}
360 
IsFastNative()361   bool IsFastNative() const {
362     return is_fast_native_;
363   }
364 
IsCriticalNative()365   bool IsCriticalNative() const {
366     return is_critical_native_;
367   }
368 
369   // Does the transition have a method pointer in the stack frame?
SpillsMethod()370   bool SpillsMethod() const {
371     // Exclude method pointer for @CriticalNative methods for optimization speed.
372     return !IsCriticalNative();
373   }
374 
375   // Locking argument register, used to pass the synchronization object for calls
376   // to `JniLockObject()` and `JniUnlockObject()`.
377   virtual ManagedRegister LockingArgumentRegister() const = 0;
378 
379   // Hidden argument register, used to pass the method pointer for @CriticalNative call.
380   virtual ManagedRegister HiddenArgumentRegister() const = 0;
381 
382   // Whether to use tail call (used only for @CriticalNative).
383   virtual bool UseTailCall() const = 0;
384 
385   // Whether the return type is small. Used for RequiresSmallResultTypeExtension()
386   // on architectures that require the sign/zero extension.
HasSmallReturnType()387   bool HasSmallReturnType() const {
388     Primitive::Type return_type = GetReturnType();
389     return return_type == Primitive::kPrimByte ||
390            return_type == Primitive::kPrimShort ||
391            return_type == Primitive::kPrimBoolean ||
392            return_type == Primitive::kPrimChar;
393   }
394 
395  protected:
396   // Named iterator positions
397   enum IteratorPos {
398     kJniEnv = 0,
399     kObjectOrClass = 1
400   };
401 
JniCallingConvention(bool is_static,bool is_synchronized,bool is_fast_native,bool is_critical_native,std::string_view shorty,PointerSize frame_pointer_size)402   JniCallingConvention(bool is_static,
403                        bool is_synchronized,
404                        bool is_fast_native,
405                        bool is_critical_native,
406                        std::string_view shorty,
407                        PointerSize frame_pointer_size)
408       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size),
409         is_fast_native_(is_fast_native),
410         is_critical_native_(is_critical_native) {}
411 
412  protected:
413   size_t NumberOfExtraArgumentsForJni() const;
414 
415   // Does the transition have a local reference segment state?
HasLocalReferenceSegmentState()416   bool HasLocalReferenceSegmentState() const {
417     // Exclude local reference segment states for @CriticalNative methods for optimization speed.
418     return !IsCriticalNative();
419   }
420 
421   // Are there extra JNI arguments (JNIEnv* and maybe jclass)?
HasExtraArgumentsForJni()422   bool HasExtraArgumentsForJni() const {
423     // @CriticalNative jni implementations exclude both JNIEnv* and the jclass/jobject parameters.
424     return !IsCriticalNative();
425   }
426 
427   // Has a JNIEnv* parameter implicitly?
HasJniEnv()428   bool HasJniEnv() const {
429     // Exclude "JNIEnv*" parameter for @CriticalNative methods.
430     return HasExtraArgumentsForJni();
431   }
432 
433   // Has a 'jclass' parameter implicitly?
434   bool HasSelfClass() const;
435 
436   // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments.
437   size_t GetIteratorPositionWithinShorty() const;
438 
439   // Is the current argument (at the iterator) an extra argument for JNI?
440   bool IsCurrentArgExtraForJni() const;
441 
442   const bool is_fast_native_;
443   const bool is_critical_native_;
444 
445  private:
446   // Shorthand for switching on the switch value but only IF there are extra JNI arguments.
447   //
448   // Puts the case value into return_value.
449   // * (switch_value == kJniEnv) => case_jni_env
450   // * (switch_value == kObjectOrClass) => case_object_or_class
451   //
452   // Returns false otherwise (or if there are no extra JNI arguments).
453   bool SwitchExtraJniArguments(size_t switch_value,
454                                bool case_jni_env,
455                                bool case_object_or_class,
456                                /* out parameters */
457                                bool* return_value) const;
458 };
459 
460 }  // namespace art
461 
462 #endif  // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
463