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 HIDDEN {
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 UNUSED(allocator);
78 UNUSED(is_static);
79 UNUSED(is_synchronized);
80 UNUSED(shorty);
81 LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
82 UNREACHABLE();
83 }
84 }
85
HasNext()86 bool ManagedRuntimeCallingConvention::HasNext() {
87 return itr_args_ < NumArgs();
88 }
89
Next()90 void ManagedRuntimeCallingConvention::Next() {
91 CHECK(HasNext());
92 if (IsCurrentArgExplicit() && // don't query parameter type of implicit args
93 IsParamALongOrDouble(itr_args_)) {
94 itr_longs_and_doubles_++;
95 itr_slots_++;
96 }
97 if (IsParamAFloatOrDouble(itr_args_)) {
98 itr_float_and_doubles_++;
99 }
100 if (IsCurrentParamAReference()) {
101 itr_refs_++;
102 }
103 itr_args_++;
104 itr_slots_++;
105 }
106
IsCurrentArgExplicit()107 bool ManagedRuntimeCallingConvention::IsCurrentArgExplicit() {
108 // Static methods have no implicit arguments, others implicitly pass this
109 return IsStatic() || (itr_args_ != 0);
110 }
111
IsCurrentArgPossiblyNull()112 bool ManagedRuntimeCallingConvention::IsCurrentArgPossiblyNull() {
113 return IsCurrentArgExplicit(); // any user parameter may be null
114 }
115
CurrentParamSize()116 size_t ManagedRuntimeCallingConvention::CurrentParamSize() {
117 return ParamSize(itr_args_);
118 }
119
IsCurrentParamAReference()120 bool ManagedRuntimeCallingConvention::IsCurrentParamAReference() {
121 return IsParamAReference(itr_args_);
122 }
123
IsCurrentParamAFloatOrDouble()124 bool ManagedRuntimeCallingConvention::IsCurrentParamAFloatOrDouble() {
125 return IsParamAFloatOrDouble(itr_args_);
126 }
127
IsCurrentParamADouble()128 bool ManagedRuntimeCallingConvention::IsCurrentParamADouble() {
129 return IsParamADouble(itr_args_);
130 }
131
IsCurrentParamALong()132 bool ManagedRuntimeCallingConvention::IsCurrentParamALong() {
133 return IsParamALong(itr_args_);
134 }
135
136 // JNI calling convention
137
Create(ArenaAllocator * allocator,bool is_static,bool is_synchronized,bool is_fast_native,bool is_critical_native,const char * shorty,InstructionSet instruction_set)138 std::unique_ptr<JniCallingConvention> JniCallingConvention::Create(ArenaAllocator* allocator,
139 bool is_static,
140 bool is_synchronized,
141 bool is_fast_native,
142 bool is_critical_native,
143 const char* shorty,
144 InstructionSet instruction_set) {
145 switch (instruction_set) {
146 #ifdef ART_ENABLE_CODEGEN_arm
147 case InstructionSet::kArm:
148 case InstructionSet::kThumb2:
149 return std::unique_ptr<JniCallingConvention>(
150 new (allocator) arm::ArmJniCallingConvention(
151 is_static, is_synchronized, is_fast_native, is_critical_native, shorty));
152 #endif
153 #ifdef ART_ENABLE_CODEGEN_arm64
154 case InstructionSet::kArm64:
155 return std::unique_ptr<JniCallingConvention>(
156 new (allocator) arm64::Arm64JniCallingConvention(
157 is_static, is_synchronized, is_fast_native, is_critical_native, shorty));
158 #endif
159 #ifdef ART_ENABLE_CODEGEN_x86
160 case InstructionSet::kX86:
161 return std::unique_ptr<JniCallingConvention>(
162 new (allocator) x86::X86JniCallingConvention(
163 is_static, is_synchronized, is_fast_native, is_critical_native, shorty));
164 #endif
165 #ifdef ART_ENABLE_CODEGEN_x86_64
166 case InstructionSet::kX86_64:
167 return std::unique_ptr<JniCallingConvention>(
168 new (allocator) x86_64::X86_64JniCallingConvention(
169 is_static, is_synchronized, is_fast_native, is_critical_native, shorty));
170 #endif
171 default:
172 UNUSED(allocator);
173 UNUSED(is_static);
174 UNUSED(is_synchronized);
175 UNUSED(is_fast_native);
176 UNUSED(is_critical_native);
177 UNUSED(shorty);
178 LOG(FATAL) << "Unknown InstructionSet: " << instruction_set;
179 UNREACHABLE();
180 }
181 }
182
ReferenceCount() const183 size_t JniCallingConvention::ReferenceCount() const {
184 return NumReferenceArgs() + (IsStatic() ? 1 : 0);
185 }
186
HasNext()187 bool JniCallingConvention::HasNext() {
188 if (IsCurrentArgExtraForJni()) {
189 return true;
190 } else {
191 unsigned int arg_pos = GetIteratorPositionWithinShorty();
192 return arg_pos < NumArgs();
193 }
194 }
195
Next()196 void JniCallingConvention::Next() {
197 CHECK(HasNext());
198 if (IsCurrentParamALong() || IsCurrentParamADouble()) {
199 itr_longs_and_doubles_++;
200 itr_slots_++;
201 }
202 if (IsCurrentParamAFloatOrDouble()) {
203 itr_float_and_doubles_++;
204 }
205 if (IsCurrentParamAReference()) {
206 itr_refs_++;
207 }
208 // This default/fallthrough case also covers the extra JNIEnv* argument,
209 // as well as any other single-slot primitives.
210 itr_args_++;
211 itr_slots_++;
212 }
213
IsCurrentParamAReference()214 bool JniCallingConvention::IsCurrentParamAReference() {
215 bool return_value;
216 if (SwitchExtraJniArguments(itr_args_,
217 false, // JNIEnv*
218 true, // jobject or jclass
219 /* out parameters */
220 &return_value)) {
221 return return_value;
222 } else {
223 int arg_pos = GetIteratorPositionWithinShorty();
224 return IsParamAReference(arg_pos);
225 }
226 }
227
228
IsCurrentParamJniEnv()229 bool JniCallingConvention::IsCurrentParamJniEnv() {
230 if (UNLIKELY(!HasJniEnv())) {
231 return false;
232 }
233 return (itr_args_ == kJniEnv);
234 }
235
IsCurrentParamAFloatOrDouble()236 bool JniCallingConvention::IsCurrentParamAFloatOrDouble() {
237 bool return_value;
238 if (SwitchExtraJniArguments(itr_args_,
239 false, // jnienv*
240 false, // jobject or jclass
241 /* out parameters */
242 &return_value)) {
243 return return_value;
244 } else {
245 int arg_pos = GetIteratorPositionWithinShorty();
246 return IsParamAFloatOrDouble(arg_pos);
247 }
248 }
249
IsCurrentParamADouble()250 bool JniCallingConvention::IsCurrentParamADouble() {
251 bool return_value;
252 if (SwitchExtraJniArguments(itr_args_,
253 false, // jnienv*
254 false, // jobject or jclass
255 /* out parameters */
256 &return_value)) {
257 return return_value;
258 } else {
259 int arg_pos = GetIteratorPositionWithinShorty();
260 return IsParamADouble(arg_pos);
261 }
262 }
263
IsCurrentParamALong()264 bool JniCallingConvention::IsCurrentParamALong() {
265 bool return_value;
266 if (SwitchExtraJniArguments(itr_args_,
267 false, // jnienv*
268 false, // jobject or jclass
269 /* out parameters */
270 &return_value)) {
271 return return_value;
272 } else {
273 int arg_pos = GetIteratorPositionWithinShorty();
274 return IsParamALong(arg_pos);
275 }
276 }
277
CurrentParamSize() const278 size_t JniCallingConvention::CurrentParamSize() const {
279 if (IsCurrentArgExtraForJni()) {
280 return static_cast<size_t>(frame_pointer_size_); // JNIEnv or jobject/jclass
281 } else {
282 int arg_pos = GetIteratorPositionWithinShorty();
283 return ParamSize(arg_pos);
284 }
285 }
286
NumberOfExtraArgumentsForJni() const287 size_t JniCallingConvention::NumberOfExtraArgumentsForJni() const {
288 if (LIKELY(HasExtraArgumentsForJni())) {
289 // The first argument is the JNIEnv*.
290 // Static methods have an extra argument which is the jclass.
291 return IsStatic() ? 2 : 1;
292 } else {
293 // Critical natives exclude the JNIEnv and the jclass/this parameters.
294 return 0;
295 }
296 }
297
HasSelfClass() const298 bool JniCallingConvention::HasSelfClass() const {
299 if (!IsStatic()) {
300 // Virtual functions: There is never an implicit jclass parameter.
301 return false;
302 } else {
303 // Static functions: There is an implicit jclass parameter unless it's @CriticalNative.
304 return HasExtraArgumentsForJni();
305 }
306 }
307
GetIteratorPositionWithinShorty() const308 unsigned int JniCallingConvention::GetIteratorPositionWithinShorty() const {
309 // We need to subtract out the extra JNI arguments if we want to use this iterator position
310 // with the inherited CallingConvention member functions, which rely on scanning the shorty.
311 // Note that our shorty does *not* include the JNIEnv, jclass/jobject parameters.
312 DCHECK_GE(itr_args_, NumberOfExtraArgumentsForJni());
313 return itr_args_ - NumberOfExtraArgumentsForJni();
314 }
315
IsCurrentArgExtraForJni() const316 bool JniCallingConvention::IsCurrentArgExtraForJni() const {
317 if (UNLIKELY(!HasExtraArgumentsForJni())) {
318 return false; // If there are no extra args, we can never be an extra.
319 }
320 // Only parameters kJniEnv and kObjectOrClass are considered extra.
321 return itr_args_ <= kObjectOrClass;
322 }
323
SwitchExtraJniArguments(size_t switch_value,bool case_jni_env,bool case_object_or_class,bool * return_value) const324 bool JniCallingConvention::SwitchExtraJniArguments(size_t switch_value,
325 bool case_jni_env,
326 bool case_object_or_class,
327 /* out parameters */
328 bool* return_value) const {
329 DCHECK(return_value != nullptr);
330 if (UNLIKELY(!HasExtraArgumentsForJni())) {
331 return false;
332 }
333
334 switch (switch_value) {
335 case kJniEnv:
336 *return_value = case_jni_env;
337 return true;
338 case kObjectOrClass:
339 *return_value = case_object_or_class;
340 return true;
341 default:
342 return false;
343 }
344 }
345
346
347 } // namespace art
348