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