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 #include "calling_convention.h"
18 
19 #include <android-base/logging.h>
20 
21 #include "arch/instruction_set.h"
22 
23 #ifdef ART_ENABLE_CODEGEN_arm
24 #include "jni/quick/arm/calling_convention_arm.h"
25 #endif
26 
27 #ifdef ART_ENABLE_CODEGEN_arm64
28 #include "jni/quick/arm64/calling_convention_arm64.h"
29 #endif
30 
31 #ifdef ART_ENABLE_CODEGEN_x86
32 #include "jni/quick/x86/calling_convention_x86.h"
33 #endif
34 
35 #ifdef ART_ENABLE_CODEGEN_x86_64
36 #include "jni/quick/x86_64/calling_convention_x86_64.h"
37 #endif
38 
39 namespace art {
40 
41 // Managed runtime calling convention
42 
Create(ArenaAllocator * allocator,bool is_static,bool is_synchronized,const char * shorty,InstructionSet instruction_set)43 std::unique_ptr<ManagedRuntimeCallingConvention> ManagedRuntimeCallingConvention::Create(
44     ArenaAllocator* allocator,
45     bool is_static,
46     bool is_synchronized,
47     const char* shorty,
48     InstructionSet instruction_set) {
49   switch (instruction_set) {
50 #ifdef ART_ENABLE_CODEGEN_arm
51     case InstructionSet::kArm:
52     case InstructionSet::kThumb2:
53       return std::unique_ptr<ManagedRuntimeCallingConvention>(
54           new (allocator) arm::ArmManagedRuntimeCallingConvention(
55               is_static, is_synchronized, shorty));
56 #endif
57 #ifdef ART_ENABLE_CODEGEN_arm64
58     case InstructionSet::kArm64:
59       return std::unique_ptr<ManagedRuntimeCallingConvention>(
60           new (allocator) arm64::Arm64ManagedRuntimeCallingConvention(
61               is_static, is_synchronized, shorty));
62 #endif
63 #ifdef ART_ENABLE_CODEGEN_x86
64     case InstructionSet::kX86:
65       return std::unique_ptr<ManagedRuntimeCallingConvention>(
66           new (allocator) x86::X86ManagedRuntimeCallingConvention(
67               is_static, is_synchronized, shorty));
68 #endif
69 #ifdef ART_ENABLE_CODEGEN_x86_64
70     case InstructionSet::kX86_64:
71       return std::unique_ptr<ManagedRuntimeCallingConvention>(
72           new (allocator) x86_64::X86_64ManagedRuntimeCallingConvention(
73               is_static, is_synchronized, shorty));
74 #endif
75     default:
76       LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
77       UNREACHABLE();
78   }
79 }
80 
HasNext()81 bool ManagedRuntimeCallingConvention::HasNext() {
82   return itr_args_ < NumArgs();
83 }
84 
Next()85 void ManagedRuntimeCallingConvention::Next() {
86   CHECK(HasNext());
87   if (IsCurrentArgExplicit() &&  // don't query parameter type of implicit args
88       IsParamALongOrDouble(itr_args_)) {
89     itr_longs_and_doubles_++;
90     itr_slots_++;
91   }
92   if (IsParamAFloatOrDouble(itr_args_)) {
93     itr_float_and_doubles_++;
94   }
95   if (IsCurrentParamAReference()) {
96     itr_refs_++;
97   }
98   itr_args_++;
99   itr_slots_++;
100 }
101 
IsCurrentArgExplicit()102 bool ManagedRuntimeCallingConvention::IsCurrentArgExplicit() {
103   // Static methods have no implicit arguments, others implicitly pass this
104   return IsStatic() || (itr_args_ != 0);
105 }
106 
IsCurrentArgPossiblyNull()107 bool ManagedRuntimeCallingConvention::IsCurrentArgPossiblyNull() {
108   return IsCurrentArgExplicit();  // any user parameter may be null
109 }
110 
CurrentParamSize()111 size_t ManagedRuntimeCallingConvention::CurrentParamSize() {
112   return ParamSize(itr_args_);
113 }
114 
IsCurrentParamAReference()115 bool ManagedRuntimeCallingConvention::IsCurrentParamAReference() {
116   return IsParamAReference(itr_args_);
117 }
118 
IsCurrentParamAFloatOrDouble()119 bool ManagedRuntimeCallingConvention::IsCurrentParamAFloatOrDouble() {
120   return IsParamAFloatOrDouble(itr_args_);
121 }
122 
IsCurrentParamADouble()123 bool ManagedRuntimeCallingConvention::IsCurrentParamADouble() {
124   return IsParamADouble(itr_args_);
125 }
126 
IsCurrentParamALong()127 bool ManagedRuntimeCallingConvention::IsCurrentParamALong() {
128   return IsParamALong(itr_args_);
129 }
130 
131 // JNI calling convention
132 
Create(ArenaAllocator * allocator,bool is_static,bool is_synchronized,bool is_critical_native,const char * shorty,InstructionSet instruction_set)133 std::unique_ptr<JniCallingConvention> JniCallingConvention::Create(ArenaAllocator* allocator,
134                                                                    bool is_static,
135                                                                    bool is_synchronized,
136                                                                    bool is_critical_native,
137                                                                    const char* shorty,
138                                                                    InstructionSet instruction_set) {
139   switch (instruction_set) {
140 #ifdef ART_ENABLE_CODEGEN_arm
141     case InstructionSet::kArm:
142     case InstructionSet::kThumb2:
143       return std::unique_ptr<JniCallingConvention>(
144           new (allocator) arm::ArmJniCallingConvention(
145               is_static, is_synchronized, is_critical_native, shorty));
146 #endif
147 #ifdef ART_ENABLE_CODEGEN_arm64
148     case InstructionSet::kArm64:
149       return std::unique_ptr<JniCallingConvention>(
150           new (allocator) arm64::Arm64JniCallingConvention(
151               is_static, is_synchronized, is_critical_native, shorty));
152 #endif
153 #ifdef ART_ENABLE_CODEGEN_x86
154     case InstructionSet::kX86:
155       return std::unique_ptr<JniCallingConvention>(
156           new (allocator) x86::X86JniCallingConvention(
157               is_static, is_synchronized, is_critical_native, shorty));
158 #endif
159 #ifdef ART_ENABLE_CODEGEN_x86_64
160     case InstructionSet::kX86_64:
161       return std::unique_ptr<JniCallingConvention>(
162           new (allocator) x86_64::X86_64JniCallingConvention(
163               is_static, is_synchronized, is_critical_native, shorty));
164 #endif
165     default:
166       LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
167       UNREACHABLE();
168   }
169 }
170 
ReferenceCount() const171 size_t JniCallingConvention::ReferenceCount() const {
172   return NumReferenceArgs() + (IsStatic() ? 1 : 0);
173 }
174 
SavedLocalReferenceCookieOffset() const175 FrameOffset JniCallingConvention::SavedLocalReferenceCookieOffset() const {
176   size_t references_size = handle_scope_pointer_size_ * ReferenceCount();  // size excluding header
177   return FrameOffset(HandleReferencesOffset().Int32Value() + references_size);
178 }
179 
ReturnValueSaveLocation() const180 FrameOffset JniCallingConvention::ReturnValueSaveLocation() const {
181   if (LIKELY(HasHandleScope())) {
182     // Initial offset already includes the displacement.
183     // -- Remove the additional local reference cookie offset if we don't have a handle scope.
184     const size_t saved_local_reference_cookie_offset =
185         SavedLocalReferenceCookieOffset().Int32Value();
186     // Segment state is 4 bytes long
187     const size_t segment_state_size = 4;
188     return FrameOffset(saved_local_reference_cookie_offset + segment_state_size);
189   } else {
190     // Include only the initial Method* as part of the offset.
191     CHECK_LT(displacement_.SizeValue(),
192              static_cast<size_t>(std::numeric_limits<int32_t>::max()));
193     return FrameOffset(displacement_.Int32Value() + static_cast<size_t>(frame_pointer_size_));
194   }
195 }
196 
HasNext()197 bool JniCallingConvention::HasNext() {
198   if (IsCurrentArgExtraForJni()) {
199     return true;
200   } else {
201     unsigned int arg_pos = GetIteratorPositionWithinShorty();
202     return arg_pos < NumArgs();
203   }
204 }
205 
Next()206 void JniCallingConvention::Next() {
207   CHECK(HasNext());
208   if (IsCurrentParamALong() || IsCurrentParamADouble()) {
209     itr_longs_and_doubles_++;
210     itr_slots_++;
211   }
212   if (IsCurrentParamAFloatOrDouble()) {
213     itr_float_and_doubles_++;
214   }
215   if (IsCurrentParamAReference()) {
216     itr_refs_++;
217   }
218   // This default/fallthrough case also covers the extra JNIEnv* argument,
219   // as well as any other single-slot primitives.
220   itr_args_++;
221   itr_slots_++;
222 }
223 
IsCurrentParamAReference()224 bool JniCallingConvention::IsCurrentParamAReference() {
225   bool return_value;
226   if (SwitchExtraJniArguments(itr_args_,
227                               false,  // JNIEnv*
228                               true,   // jobject or jclass
229                               /* out parameters */
230                               &return_value)) {
231     return return_value;
232   } else {
233     int arg_pos = GetIteratorPositionWithinShorty();
234     return IsParamAReference(arg_pos);
235   }
236 }
237 
238 
IsCurrentParamJniEnv()239 bool JniCallingConvention::IsCurrentParamJniEnv() {
240   if (UNLIKELY(!HasJniEnv())) {
241     return false;
242   }
243   return (itr_args_ == kJniEnv);
244 }
245 
IsCurrentParamAFloatOrDouble()246 bool JniCallingConvention::IsCurrentParamAFloatOrDouble() {
247   bool return_value;
248   if (SwitchExtraJniArguments(itr_args_,
249                               false,  // jnienv*
250                               false,  // jobject or jclass
251                               /* out parameters */
252                               &return_value)) {
253     return return_value;
254   } else {
255     int arg_pos = GetIteratorPositionWithinShorty();
256     return IsParamAFloatOrDouble(arg_pos);
257   }
258 }
259 
IsCurrentParamADouble()260 bool JniCallingConvention::IsCurrentParamADouble() {
261   bool return_value;
262   if (SwitchExtraJniArguments(itr_args_,
263                               false,  // jnienv*
264                               false,  // jobject or jclass
265                               /* out parameters */
266                               &return_value)) {
267     return return_value;
268   } else {
269     int arg_pos = GetIteratorPositionWithinShorty();
270     return IsParamADouble(arg_pos);
271   }
272 }
273 
IsCurrentParamALong()274 bool JniCallingConvention::IsCurrentParamALong() {
275   bool return_value;
276   if (SwitchExtraJniArguments(itr_args_,
277                               false,  // jnienv*
278                               false,  // jobject or jclass
279                               /* out parameters */
280                               &return_value)) {
281     return return_value;
282   } else {
283     int arg_pos = GetIteratorPositionWithinShorty();
284     return IsParamALong(arg_pos);
285   }
286 }
287 
288 // Return position of handle scope entry holding reference at the current iterator
289 // position
CurrentParamHandleScopeEntryOffset()290 FrameOffset JniCallingConvention::CurrentParamHandleScopeEntryOffset() {
291   CHECK(IsCurrentParamAReference());
292   CHECK_LT(HandleScopeLinkOffset(), HandleScopeNumRefsOffset());
293   int result = HandleReferencesOffset().Int32Value() + itr_refs_ * handle_scope_pointer_size_;
294   CHECK_GT(result, HandleScopeNumRefsOffset().Int32Value());
295   return FrameOffset(result);
296 }
297 
CurrentParamSize() const298 size_t JniCallingConvention::CurrentParamSize() const {
299   if (IsCurrentArgExtraForJni()) {
300     return static_cast<size_t>(frame_pointer_size_);  // JNIEnv or jobject/jclass
301   } else {
302     int arg_pos = GetIteratorPositionWithinShorty();
303     return ParamSize(arg_pos);
304   }
305 }
306 
NumberOfExtraArgumentsForJni() const307 size_t JniCallingConvention::NumberOfExtraArgumentsForJni() const {
308   if (LIKELY(HasExtraArgumentsForJni())) {
309     // The first argument is the JNIEnv*.
310     // Static methods have an extra argument which is the jclass.
311     return IsStatic() ? 2 : 1;
312   } else {
313     // Critical natives exclude the JNIEnv and the jclass/this parameters.
314     return 0;
315   }
316 }
317 
HasSelfClass() const318 bool JniCallingConvention::HasSelfClass() const {
319   if (!IsStatic()) {
320     // Virtual functions: There is never an implicit jclass parameter.
321     return false;
322   } else {
323     // Static functions: There is an implicit jclass parameter unless it's @CriticalNative.
324     return HasExtraArgumentsForJni();
325   }
326 }
327 
GetIteratorPositionWithinShorty() const328 unsigned int JniCallingConvention::GetIteratorPositionWithinShorty() const {
329   // We need to subtract out the extra JNI arguments if we want to use this iterator position
330   // with the inherited CallingConvention member functions, which rely on scanning the shorty.
331   // Note that our shorty does *not* include the JNIEnv, jclass/jobject parameters.
332   DCHECK_GE(itr_args_, NumberOfExtraArgumentsForJni());
333   return itr_args_ - NumberOfExtraArgumentsForJni();
334 }
335 
IsCurrentArgExtraForJni() const336 bool JniCallingConvention::IsCurrentArgExtraForJni() const {
337   if (UNLIKELY(!HasExtraArgumentsForJni())) {
338     return false;  // If there are no extra args, we can never be an extra.
339   }
340   // Only parameters kJniEnv and kObjectOrClass are considered extra.
341   return itr_args_ <= kObjectOrClass;
342 }
343 
SwitchExtraJniArguments(size_t switch_value,bool case_jni_env,bool case_object_or_class,bool * return_value) const344 bool JniCallingConvention::SwitchExtraJniArguments(size_t switch_value,
345                                                    bool case_jni_env,
346                                                    bool case_object_or_class,
347                                                    /* out parameters */
348                                                    bool* return_value) const {
349   DCHECK(return_value != nullptr);
350   if (UNLIKELY(!HasExtraArgumentsForJni())) {
351     return false;
352   }
353 
354   switch (switch_value) {
355     case kJniEnv:
356       *return_value = case_jni_env;
357       return true;
358     case kObjectOrClass:
359       *return_value = case_object_or_class;
360       return true;
361     default:
362       return false;
363   }
364 }
365 
366 
367 }  // namespace art
368