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   // Register reserved for scratch usage during procedure calls.
52   virtual ManagedRegister InterproceduralScratchRegister() = 0;
53 
54   // Offset of Method within the frame.
MethodStackOffset()55   FrameOffset MethodStackOffset() {
56     return displacement_;
57   }
58 
59   // Iterator interface
60 
61   // Place iterator at start of arguments. The displacement is applied to
62   // frame offset methods to account for frames which may be on the stack
63   // below the one being iterated over.
ResetIterator(FrameOffset displacement)64   void ResetIterator(FrameOffset displacement) {
65     displacement_ = displacement;
66     itr_slots_ = 0;
67     itr_args_ = 0;
68     itr_refs_ = 0;
69     itr_longs_and_doubles_ = 0;
70     itr_float_and_doubles_ = 0;
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         handle_scope_pointer_size_(sizeof(StackReference<mirror::Object>)),
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   // The size of a reference entry within the handle scope.
214   const size_t handle_scope_pointer_size_;
215 
216  private:
217   const bool is_static_;
218   const bool is_synchronized_;
219   std::string shorty_;
220   size_t num_args_;
221   size_t num_ref_args_;
222   size_t num_float_or_double_args_;
223   size_t num_long_or_double_args_;
224 };
225 
226 // Abstraction for managed code's calling conventions
227 // | { Incoming stack args } |
228 // | { Prior Method* }       | <-- Prior SP
229 // | { Return address }      |
230 // | { Callee saves }        |
231 // | { Spills ... }          |
232 // | { Outgoing stack args } |
233 // | { Method* }             | <-- SP
234 class ManagedRuntimeCallingConvention : public CallingConvention {
235  public:
236   static std::unique_ptr<ManagedRuntimeCallingConvention> Create(ArenaAllocator* allocator,
237                                                                  bool is_static,
238                                                                  bool is_synchronized,
239                                                                  const char* shorty,
240                                                                  InstructionSet instruction_set);
241 
242   // Register that holds the incoming method argument
243   virtual ManagedRegister MethodRegister() = 0;
244 
245   // Iterator interface
246   bool HasNext();
247   void Next();
248   bool IsCurrentParamAReference();
249   bool IsCurrentParamAFloatOrDouble();
250   bool IsCurrentParamADouble();
251   bool IsCurrentParamALong();
252   bool IsCurrentArgExplicit();  // ie a non-implict argument such as this
253   bool IsCurrentArgPossiblyNull();
254   size_t CurrentParamSize();
255   virtual bool IsCurrentParamInRegister() = 0;
256   virtual bool IsCurrentParamOnStack() = 0;
257   virtual ManagedRegister CurrentParamRegister() = 0;
258   virtual FrameOffset CurrentParamStackOffset() = 0;
259 
~ManagedRuntimeCallingConvention()260   virtual ~ManagedRuntimeCallingConvention() {}
261 
262   // Registers to spill to caller's out registers on entry.
263   virtual const ManagedRegisterEntrySpills& EntrySpills() = 0;
264 
265  protected:
ManagedRuntimeCallingConvention(bool is_static,bool is_synchronized,const char * shorty,PointerSize frame_pointer_size)266   ManagedRuntimeCallingConvention(bool is_static,
267                                   bool is_synchronized,
268                                   const char* shorty,
269                                   PointerSize frame_pointer_size)
270       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {}
271 };
272 
273 // Abstraction for JNI calling conventions
274 // | { Incoming stack args }         | <-- Prior SP
275 // | { Return address }              |
276 // | { Callee saves }                |     ([1])
277 // | { Return value spill }          |     (live on return slow paths)
278 // | { Local Ref. Table State }      |
279 // | { Stack Indirect Ref. Table     |
280 // |   num. refs./link }             |     (here to prior SP is frame size)
281 // | { Method* }                     | <-- Anchor SP written to thread
282 // | { Outgoing stack args }         | <-- SP at point of call
283 // | Native frame                    |
284 //
285 // [1] We must save all callee saves here to enable any exception throws to restore
286 // callee saves for frames above this one.
287 class JniCallingConvention : public CallingConvention {
288  public:
289   static std::unique_ptr<JniCallingConvention> Create(ArenaAllocator* allocator,
290                                                       bool is_static,
291                                                       bool is_synchronized,
292                                                       bool is_critical_native,
293                                                       const char* shorty,
294                                                       InstructionSet instruction_set);
295 
296   // Size of frame excluding space for outgoing args (its assumed Method* is
297   // always at the bottom of a frame, but this doesn't work for outgoing
298   // native args). Includes alignment.
299   virtual size_t FrameSize() = 0;
300   // Size of outgoing arguments (stack portion), including alignment.
301   // -- Arguments that are passed via registers are excluded from this size.
302   virtual size_t OutArgSize() = 0;
303   // Number of references in stack indirect reference table
304   size_t ReferenceCount() const;
305   // Location where the segment state of the local indirect reference table is saved
306   FrameOffset SavedLocalReferenceCookieOffset() 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   // Spill mask values
319   virtual uint32_t CoreSpillMask() const = 0;
320   virtual uint32_t FpSpillMask() const = 0;
321 
322   // An extra scratch register live after the call
323   virtual ManagedRegister ReturnScratchRegister() const = 0;
324 
325   // Iterator interface
326   bool HasNext();
327   virtual void Next();
328   bool IsCurrentParamAReference();
329   bool IsCurrentParamAFloatOrDouble();
330   bool IsCurrentParamADouble();
331   bool IsCurrentParamALong();
IsCurrentParamALongOrDouble()332   bool IsCurrentParamALongOrDouble() {
333     return IsCurrentParamALong() || IsCurrentParamADouble();
334   }
335   bool IsCurrentParamJniEnv();
336   size_t CurrentParamSize() const;
337   virtual bool IsCurrentParamInRegister() = 0;
338   virtual bool IsCurrentParamOnStack() = 0;
339   virtual ManagedRegister CurrentParamRegister() = 0;
340   virtual FrameOffset CurrentParamStackOffset() = 0;
341 
342   // Iterator interface extension for JNI
343   FrameOffset CurrentParamHandleScopeEntryOffset();
344 
345   // Position of handle scope and interior fields
HandleScopeOffset()346   FrameOffset HandleScopeOffset() const {
347     return FrameOffset(this->displacement_.Int32Value() + static_cast<size_t>(frame_pointer_size_));
348     // above Method reference
349   }
350 
HandleScopeLinkOffset()351   FrameOffset HandleScopeLinkOffset() const {
352     return FrameOffset(HandleScopeOffset().Int32Value() +
353                        HandleScope::LinkOffset(frame_pointer_size_));
354   }
355 
HandleScopeNumRefsOffset()356   FrameOffset HandleScopeNumRefsOffset() const {
357     return FrameOffset(HandleScopeOffset().Int32Value() +
358                        HandleScope::NumberOfReferencesOffset(frame_pointer_size_));
359   }
360 
HandleReferencesOffset()361   FrameOffset HandleReferencesOffset() const {
362     return FrameOffset(HandleScopeOffset().Int32Value() +
363                        HandleScope::ReferencesOffset(frame_pointer_size_));
364   }
365 
~JniCallingConvention()366   virtual ~JniCallingConvention() {}
367 
368  protected:
369   // Named iterator positions
370   enum IteratorPos {
371     kJniEnv = 0,
372     kObjectOrClass = 1
373   };
374 
JniCallingConvention(bool is_static,bool is_synchronized,bool is_critical_native,const char * shorty,PointerSize frame_pointer_size)375   JniCallingConvention(bool is_static,
376                        bool is_synchronized,
377                        bool is_critical_native,
378                        const char* shorty,
379                        PointerSize frame_pointer_size)
380       : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size),
381         is_critical_native_(is_critical_native) {}
382 
383   // Number of stack slots for outgoing arguments, above which the handle scope is
384   // located
385   virtual size_t NumberOfOutgoingStackArgs() = 0;
386 
387  protected:
388   size_t NumberOfExtraArgumentsForJni() const;
389 
390   // Does the transition have a StackHandleScope?
391   bool HasHandleScope() const;
392   // Does the transition have a local reference segment state?
393   bool HasLocalReferenceSegmentState() const;
394   // Has a JNIEnv* parameter implicitly?
395   bool HasJniEnv() const;
396   // Has a 'jclass' parameter implicitly?
397   bool HasSelfClass() const;
398 
399   // Are there extra JNI arguments (JNIEnv* and maybe jclass)?
400   bool HasExtraArgumentsForJni() const;
401 
402   // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments.
403   unsigned int GetIteratorPositionWithinShorty() const;
404 
405   // Is the current argument (at the iterator) an extra argument for JNI?
406   bool IsCurrentArgExtraForJni() const;
407 
408   const bool is_critical_native_;
409 
410  private:
411   // Shorthand for switching on the switch value but only IF there are extra JNI arguments.
412   //
413   // Puts the case value into return_value.
414   // * (switch_value == kJniEnv) => case_jni_env
415   // * (switch_value == kObjectOrClass) => case_object_or_class
416   //
417   // Returns false otherwise (or if there are no extra JNI arguments).
418   bool SwitchExtraJniArguments(size_t switch_value,
419                                bool case_jni_env,
420                                bool case_object_or_class,
421                                /* out parameters */
422                                bool* return_value) const;
423 };
424 
425 }  // namespace art
426 
427 #endif  // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
428