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 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 101 bool ManagedRuntimeCallingConvention::HasNext() { 102 return itr_args_ < NumArgs(); 103 } 104 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 122 bool ManagedRuntimeCallingConvention::IsCurrentArgExplicit() { 123 // Static methods have no implicit arguments, others implicitly pass this 124 return IsStatic() || (itr_args_ != 0); 125 } 126 127 bool ManagedRuntimeCallingConvention::IsCurrentArgPossiblyNull() { 128 return IsCurrentArgExplicit(); // any user parameter may be null 129 } 130 131 size_t ManagedRuntimeCallingConvention::CurrentParamSize() { 132 return ParamSize(itr_args_); 133 } 134 135 bool ManagedRuntimeCallingConvention::IsCurrentParamAReference() { 136 return IsParamAReference(itr_args_); 137 } 138 139 bool ManagedRuntimeCallingConvention::IsCurrentParamAFloatOrDouble() { 140 return IsParamAFloatOrDouble(itr_args_); 141 } 142 143 bool ManagedRuntimeCallingConvention::IsCurrentParamADouble() { 144 return IsParamADouble(itr_args_); 145 } 146 147 bool ManagedRuntimeCallingConvention::IsCurrentParamALong() { 148 return IsParamALong(itr_args_); 149 } 150 151 // JNI calling convention 152 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 203 size_t JniCallingConvention::ReferenceCount() const { 204 return NumReferenceArgs() + (IsStatic() ? 1 : 0); 205 } 206 207 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 212 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 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 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 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 271 bool JniCallingConvention::IsCurrentParamJniEnv() { 272 if (UNLIKELY(!HasJniEnv())) { 273 return false; 274 } 275 return (itr_args_ == kJniEnv); 276 } 277 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 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 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 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 330 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 339 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 350 bool JniCallingConvention::HasHandleScope() const { 351 // Exclude HandleScope for @CriticalNative methods for optimization speed. 352 return is_critical_native_ == false; 353 } 354 355 bool JniCallingConvention::HasLocalReferenceSegmentState() const { 356 // Exclude local reference segment states for @CriticalNative methods for optimization speed. 357 return is_critical_native_ == false; 358 } 359 360 bool JniCallingConvention::HasJniEnv() const { 361 // Exclude "JNIEnv*" parameter for @CriticalNative methods. 362 return HasExtraArgumentsForJni(); 363 } 364 365 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 375 bool JniCallingConvention::HasExtraArgumentsForJni() const { 376 // @CriticalNative jni implementations exclude both JNIEnv* and the jclass/jobject parameters. 377 return is_critical_native_ == false; 378 } 379 380 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 388 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 396 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