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