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