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