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