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 <vector>
21
22#include "base/arena_object.h"
23#include "handle_scope.h"
24#include "primitive.h"
25#include "thread.h"
26#include "utils/managed_register.h"
27
28namespace art {
29
30// Top-level abstraction for different calling conventions.
31class CallingConvention : public DeletableArenaObject<kArenaAllocCallingConvention> {
32 public:
33  bool IsReturnAReference() const { return shorty_[0] == 'L'; }
34
35  Primitive::Type GetReturnType() const {
36    return Primitive::GetType(shorty_[0]);
37  }
38
39  size_t SizeOfReturnValue() const {
40    size_t result = Primitive::ComponentSize(Primitive::GetType(shorty_[0]));
41    if (result >= 1 && result < 4) {
42      result = 4;
43    }
44    return result;
45  }
46
47  // Register that holds result of this method invocation.
48  virtual ManagedRegister ReturnRegister() = 0;
49  // Register reserved for scratch usage during procedure calls.
50  virtual ManagedRegister InterproceduralScratchRegister() = 0;
51
52  // Offset of Method within the frame.
53  FrameOffset MethodStackOffset() {
54    return displacement_;
55  }
56
57  // Iterator interface
58
59  // Place iterator at start of arguments. The displacement is applied to
60  // frame offset methods to account for frames which may be on the stack
61  // below the one being iterated over.
62  void ResetIterator(FrameOffset displacement) {
63    displacement_ = displacement;
64    itr_slots_ = 0;
65    itr_args_ = 0;
66    itr_refs_ = 0;
67    itr_longs_and_doubles_ = 0;
68    itr_float_and_doubles_ = 0;
69  }
70
71  virtual ~CallingConvention() {}
72
73 protected:
74  CallingConvention(bool is_static, bool is_synchronized, const char* shorty,
75                    size_t frame_pointer_size)
76      : itr_slots_(0), itr_refs_(0), itr_args_(0), itr_longs_and_doubles_(0),
77        itr_float_and_doubles_(0), displacement_(0),
78        frame_pointer_size_(frame_pointer_size),
79        handle_scope_pointer_size_(sizeof(StackReference<mirror::Object>)),
80        is_static_(is_static), is_synchronized_(is_synchronized),
81        shorty_(shorty) {
82    num_args_ = (is_static ? 0 : 1) + strlen(shorty) - 1;
83    num_ref_args_ = is_static ? 0 : 1;  // The implicit this pointer.
84    num_float_or_double_args_ = 0;
85    num_long_or_double_args_ = 0;
86    for (size_t i = 1; i < strlen(shorty); i++) {
87      char ch = shorty_[i];
88      switch (ch) {
89      case 'L':
90        num_ref_args_++;
91        break;
92      case 'J':
93        num_long_or_double_args_++;
94        break;
95      case 'D':
96        num_long_or_double_args_++;
97        num_float_or_double_args_++;
98        break;
99      case 'F':
100        num_float_or_double_args_++;
101        break;
102      }
103    }
104  }
105
106  bool IsStatic() const {
107    return is_static_;
108  }
109  bool IsSynchronized() const {
110    return is_synchronized_;
111  }
112  bool IsParamALongOrDouble(unsigned int param) const {
113    DCHECK_LT(param, NumArgs());
114    if (IsStatic()) {
115      param++;  // 0th argument must skip return value at start of the shorty
116    } else if (param == 0) {
117      return false;  // this argument
118    }
119    char ch = shorty_[param];
120    return (ch == 'J' || ch == 'D');
121  }
122  bool IsParamAFloatOrDouble(unsigned int param) const {
123    DCHECK_LT(param, NumArgs());
124    if (IsStatic()) {
125      param++;  // 0th argument must skip return value at start of the shorty
126    } else if (param == 0) {
127      return false;  // this argument
128    }
129    char ch = shorty_[param];
130    return (ch == 'F' || ch == 'D');
131  }
132  bool IsParamADouble(unsigned int param) const {
133    DCHECK_LT(param, NumArgs());
134    if (IsStatic()) {
135      param++;  // 0th argument must skip return value at start of the shorty
136    } else if (param == 0) {
137      return false;  // this argument
138    }
139    return shorty_[param] == 'D';
140  }
141  bool IsParamALong(unsigned int param) const {
142    DCHECK_LT(param, NumArgs());
143    if (IsStatic()) {
144      param++;  // 0th argument must skip return value at start of the shorty
145    } else if (param == 0) {
146      return false;  // this argument
147    }
148    return shorty_[param] == 'J';
149  }
150  bool IsParamAReference(unsigned int param) const {
151    DCHECK_LT(param, NumArgs());
152    if (IsStatic()) {
153      param++;  // 0th argument must skip return value at start of the shorty
154    } else if (param == 0) {
155      return true;  // this argument
156    }
157    return shorty_[param] == 'L';
158  }
159  size_t NumArgs() const {
160    return num_args_;
161  }
162  size_t NumLongOrDoubleArgs() const {
163    return num_long_or_double_args_;
164  }
165  size_t NumFloatOrDoubleArgs() const {
166    return num_float_or_double_args_;
167  }
168  size_t NumReferenceArgs() const {
169    return num_ref_args_;
170  }
171  size_t ParamSize(unsigned int param) const {
172    DCHECK_LT(param, NumArgs());
173    if (IsStatic()) {
174      param++;  // 0th argument must skip return value at start of the shorty
175    } else if (param == 0) {
176      return sizeof(mirror::HeapReference<mirror::Object>);  // this argument
177    }
178    size_t result = Primitive::ComponentSize(Primitive::GetType(shorty_[param]));
179    if (result >= 1 && result < 4) {
180      result = 4;
181    }
182    return result;
183  }
184  const char* GetShorty() const {
185    return shorty_.c_str();
186  }
187  // The slot number for current calling_convention argument.
188  // Note that each slot is 32-bit. When the current argument is bigger
189  // than 32 bits, return the first slot number for this argument.
190  unsigned int itr_slots_;
191  // The number of references iterated past.
192  unsigned int itr_refs_;
193  // The argument number along argument list for current argument.
194  unsigned int itr_args_;
195  // Number of longs and doubles seen along argument list.
196  unsigned int itr_longs_and_doubles_;
197  // Number of float and doubles seen along argument list.
198  unsigned int itr_float_and_doubles_;
199  // Space for frames below this on the stack.
200  FrameOffset displacement_;
201  // The size of a pointer.
202  const size_t frame_pointer_size_;
203  // The size of a reference entry within the handle scope.
204  const size_t handle_scope_pointer_size_;
205
206 private:
207  const bool is_static_;
208  const bool is_synchronized_;
209  std::string shorty_;
210  size_t num_args_;
211  size_t num_ref_args_;
212  size_t num_float_or_double_args_;
213  size_t num_long_or_double_args_;
214};
215
216// Abstraction for managed code's calling conventions
217// | { Incoming stack args } |
218// | { Prior Method* }       | <-- Prior SP
219// | { Return address }      |
220// | { Callee saves }        |
221// | { Spills ... }          |
222// | { Outgoing stack args } |
223// | { Method* }             | <-- SP
224class ManagedRuntimeCallingConvention : public CallingConvention {
225 public:
226  static std::unique_ptr<ManagedRuntimeCallingConvention> Create(ArenaAllocator* arena,
227                                                                 bool is_static,
228                                                                 bool is_synchronized,
229                                                                 const char* shorty,
230                                                                 InstructionSet instruction_set);
231
232  // Register that holds the incoming method argument
233  virtual ManagedRegister MethodRegister() = 0;
234
235  // Iterator interface
236  bool HasNext();
237  void Next();
238  bool IsCurrentParamAReference();
239  bool IsCurrentParamAFloatOrDouble();
240  bool IsCurrentParamADouble();
241  bool IsCurrentParamALong();
242  bool IsCurrentArgExplicit();  // ie a non-implict argument such as this
243  bool IsCurrentArgPossiblyNull();
244  size_t CurrentParamSize();
245  virtual bool IsCurrentParamInRegister() = 0;
246  virtual bool IsCurrentParamOnStack() = 0;
247  virtual ManagedRegister CurrentParamRegister() = 0;
248  virtual FrameOffset CurrentParamStackOffset() = 0;
249
250  virtual ~ManagedRuntimeCallingConvention() {}
251
252  // Registers to spill to caller's out registers on entry.
253  virtual const ManagedRegisterEntrySpills& EntrySpills() = 0;
254
255 protected:
256  ManagedRuntimeCallingConvention(bool is_static,
257                                  bool is_synchronized,
258                                  const char* shorty,
259                                  size_t frame_pointer_size)
260      : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {}
261};
262
263// Abstraction for JNI calling conventions
264// | { Incoming stack args }         | <-- Prior SP
265// | { Return address }              |
266// | { Callee saves }                |     ([1])
267// | { Return value spill }          |     (live on return slow paths)
268// | { Local Ref. Table State }      |
269// | { Stack Indirect Ref. Table     |
270// |   num. refs./link }             |     (here to prior SP is frame size)
271// | { Method* }                     | <-- Anchor SP written to thread
272// | { Outgoing stack args }         | <-- SP at point of call
273// | Native frame                    |
274//
275// [1] We must save all callee saves here to enable any exception throws to restore
276// callee saves for frames above this one.
277class JniCallingConvention : public CallingConvention {
278 public:
279  static std::unique_ptr<JniCallingConvention> Create(ArenaAllocator* arena,
280                                                      bool is_static,
281                                                      bool is_synchronized,
282                                                      const char* shorty,
283                                                      InstructionSet instruction_set);
284
285  // Size of frame excluding space for outgoing args (its assumed Method* is
286  // always at the bottom of a frame, but this doesn't work for outgoing
287  // native args). Includes alignment.
288  virtual size_t FrameSize() = 0;
289  // Size of outgoing arguments, including alignment
290  virtual size_t OutArgSize() = 0;
291  // Number of references in stack indirect reference table
292  size_t ReferenceCount() const;
293  // Location where the segment state of the local indirect reference table is saved
294  FrameOffset SavedLocalReferenceCookieOffset() const;
295  // Location where the return value of a call can be squirreled if another
296  // call is made following the native call
297  FrameOffset ReturnValueSaveLocation() const;
298  // Register that holds result if it is integer.
299  virtual ManagedRegister IntReturnRegister() = 0;
300  // Whether the compiler needs to ensure zero-/sign-extension of a small result type
301  virtual bool RequiresSmallResultTypeExtension() const = 0;
302
303  // Callee save registers to spill prior to native code (which may clobber)
304  virtual const std::vector<ManagedRegister>& CalleeSaveRegisters() const = 0;
305
306  // Spill mask values
307  virtual uint32_t CoreSpillMask() const = 0;
308  virtual uint32_t FpSpillMask() const = 0;
309
310  // An extra scratch register live after the call
311  virtual ManagedRegister ReturnScratchRegister() const = 0;
312
313  // Iterator interface
314  bool HasNext();
315  virtual void Next();
316  bool IsCurrentParamAReference();
317  bool IsCurrentParamAFloatOrDouble();
318  bool IsCurrentParamADouble();
319  bool IsCurrentParamALong();
320  bool IsCurrentParamJniEnv();
321  size_t CurrentParamSize();
322  virtual bool IsCurrentParamInRegister() = 0;
323  virtual bool IsCurrentParamOnStack() = 0;
324  virtual ManagedRegister CurrentParamRegister() = 0;
325  virtual FrameOffset CurrentParamStackOffset() = 0;
326
327  // Iterator interface extension for JNI
328  FrameOffset CurrentParamHandleScopeEntryOffset();
329
330  // Position of handle scope and interior fields
331  FrameOffset HandleScopeOffset() const {
332    return FrameOffset(this->displacement_.Int32Value() + frame_pointer_size_);
333    // above Method reference
334  }
335
336  FrameOffset HandleScopeLinkOffset() const {
337    return FrameOffset(HandleScopeOffset().Int32Value() +
338                       HandleScope::LinkOffset(frame_pointer_size_));
339  }
340
341  FrameOffset HandleScopeNumRefsOffset() const {
342    return FrameOffset(HandleScopeOffset().Int32Value() +
343                       HandleScope::NumberOfReferencesOffset(frame_pointer_size_));
344  }
345
346  FrameOffset HandleReferencesOffset() const {
347    return FrameOffset(HandleScopeOffset().Int32Value() +
348                       HandleScope::ReferencesOffset(frame_pointer_size_));
349  }
350
351  virtual ~JniCallingConvention() {}
352
353 protected:
354  // Named iterator positions
355  enum IteratorPos {
356    kJniEnv = 0,
357    kObjectOrClass = 1
358  };
359
360  JniCallingConvention(bool is_static, bool is_synchronized, const char* shorty,
361                       size_t frame_pointer_size)
362      : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {}
363
364  // Number of stack slots for outgoing arguments, above which the handle scope is
365  // located
366  virtual size_t NumberOfOutgoingStackArgs() = 0;
367
368 protected:
369  size_t NumberOfExtraArgumentsForJni();
370};
371
372}  // namespace art
373
374#endif  // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_
375