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