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