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