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 #ifndef ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_ 18 #define ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_ 19 20 #include "base/arena_object.h" 21 #include "base/array_ref.h" 22 #include "base/enums.h" 23 #include "dex/primitive.h" 24 #include "thread.h" 25 #include "utils/managed_register.h" 26 27 namespace art { 28 29 enum class InstructionSet; 30 31 // Top-level abstraction for different calling conventions. 32 class CallingConvention : public DeletableArenaObject<kArenaAllocCallingConvention> { 33 public: IsReturnAReference()34 bool IsReturnAReference() const { return shorty_[0] == 'L'; } 35 GetReturnType()36 Primitive::Type GetReturnType() const { 37 return Primitive::GetType(shorty_[0]); 38 } 39 SizeOfReturnValue()40 size_t SizeOfReturnValue() const { 41 size_t result = Primitive::ComponentSize(Primitive::GetType(shorty_[0])); 42 if (result >= 1 && result < 4) { 43 result = 4; 44 } 45 return result; 46 } 47 48 // Register that holds result of this method invocation. 49 virtual ManagedRegister ReturnRegister() const = 0; 50 51 // Iterator interface 52 53 // Place iterator at start of arguments. The displacement is applied to 54 // frame offset methods to account for frames which may be on the stack 55 // below the one being iterated over. ResetIterator(FrameOffset displacement)56 virtual void ResetIterator(FrameOffset displacement) { 57 displacement_ = displacement; 58 itr_slots_ = 0; 59 itr_args_ = 0; 60 itr_refs_ = 0; 61 itr_longs_and_doubles_ = 0; 62 itr_float_and_doubles_ = 0; 63 } 64 GetDisplacement()65 FrameOffset GetDisplacement() const { 66 return displacement_; 67 } 68 GetFramePointerSize()69 PointerSize GetFramePointerSize() const { 70 return frame_pointer_size_; 71 } 72 ~CallingConvention()73 virtual ~CallingConvention() {} 74 75 protected: CallingConvention(bool is_static,bool is_synchronized,const char * shorty,PointerSize frame_pointer_size)76 CallingConvention(bool is_static, 77 bool is_synchronized, 78 const char* shorty, 79 PointerSize frame_pointer_size) 80 : itr_slots_(0), itr_refs_(0), itr_args_(0), itr_longs_and_doubles_(0), 81 itr_float_and_doubles_(0), displacement_(0), 82 frame_pointer_size_(frame_pointer_size), 83 is_static_(is_static), is_synchronized_(is_synchronized), 84 shorty_(shorty) { 85 num_args_ = (is_static ? 0 : 1) + strlen(shorty) - 1; 86 num_ref_args_ = is_static ? 0 : 1; // The implicit this pointer. 87 num_float_or_double_args_ = 0; 88 num_long_or_double_args_ = 0; 89 for (size_t i = 1; i < strlen(shorty); i++) { 90 char ch = shorty_[i]; 91 switch (ch) { 92 case 'L': 93 num_ref_args_++; 94 break; 95 case 'J': 96 num_long_or_double_args_++; 97 break; 98 case 'D': 99 num_long_or_double_args_++; 100 num_float_or_double_args_++; 101 break; 102 case 'F': 103 num_float_or_double_args_++; 104 break; 105 } 106 } 107 } 108 IsStatic()109 bool IsStatic() const { 110 return is_static_; 111 } IsSynchronized()112 bool IsSynchronized() const { 113 return is_synchronized_; 114 } IsParamALongOrDouble(unsigned int param)115 bool IsParamALongOrDouble(unsigned int param) const { 116 DCHECK_LT(param, NumArgs()); 117 if (IsStatic()) { 118 param++; // 0th argument must skip return value at start of the shorty 119 } else if (param == 0) { 120 return false; // this argument 121 } 122 char ch = shorty_[param]; 123 return (ch == 'J' || ch == 'D'); 124 } IsParamAFloatOrDouble(unsigned int param)125 bool IsParamAFloatOrDouble(unsigned int param) const { 126 DCHECK_LT(param, NumArgs()); 127 if (IsStatic()) { 128 param++; // 0th argument must skip return value at start of the shorty 129 } else if (param == 0) { 130 return false; // this argument 131 } 132 char ch = shorty_[param]; 133 return (ch == 'F' || ch == 'D'); 134 } IsParamADouble(unsigned int param)135 bool IsParamADouble(unsigned int param) const { 136 DCHECK_LT(param, NumArgs()); 137 if (IsStatic()) { 138 param++; // 0th argument must skip return value at start of the shorty 139 } else if (param == 0) { 140 return false; // this argument 141 } 142 return shorty_[param] == 'D'; 143 } IsParamALong(unsigned int param)144 bool IsParamALong(unsigned int param) const { 145 DCHECK_LT(param, NumArgs()); 146 if (IsStatic()) { 147 param++; // 0th argument must skip return value at start of the shorty 148 } else if (param == 0) { 149 return false; // this argument 150 } 151 return shorty_[param] == 'J'; 152 } IsParamAReference(unsigned int param)153 bool IsParamAReference(unsigned int param) const { 154 DCHECK_LT(param, NumArgs()); 155 if (IsStatic()) { 156 param++; // 0th argument must skip return value at start of the shorty 157 } else if (param == 0) { 158 return true; // this argument 159 } 160 return shorty_[param] == 'L'; 161 } NumArgs()162 size_t NumArgs() const { 163 return num_args_; 164 } 165 // Implicit argument count: 1 for instance functions, 0 for static functions. 166 // (The implicit argument is only relevant to the shorty, i.e. 167 // the 0th arg is not in the shorty if it's implicit). NumImplicitArgs()168 size_t NumImplicitArgs() const { 169 return IsStatic() ? 0 : 1; 170 } NumLongOrDoubleArgs()171 size_t NumLongOrDoubleArgs() const { 172 return num_long_or_double_args_; 173 } NumFloatOrDoubleArgs()174 size_t NumFloatOrDoubleArgs() const { 175 return num_float_or_double_args_; 176 } NumReferenceArgs()177 size_t NumReferenceArgs() const { 178 return num_ref_args_; 179 } ParamSize(unsigned int param)180 size_t ParamSize(unsigned int param) const { 181 DCHECK_LT(param, NumArgs()); 182 if (IsStatic()) { 183 param++; // 0th argument must skip return value at start of the shorty 184 } else if (param == 0) { 185 return sizeof(mirror::HeapReference<mirror::Object>); // this argument 186 } 187 size_t result = Primitive::ComponentSize(Primitive::GetType(shorty_[param])); 188 if (result >= 1 && result < 4) { 189 result = 4; 190 } 191 return result; 192 } GetShorty()193 const char* GetShorty() const { 194 return shorty_.c_str(); 195 } 196 // The slot number for current calling_convention argument. 197 // Note that each slot is 32-bit. When the current argument is bigger 198 // than 32 bits, return the first slot number for this argument. 199 unsigned int itr_slots_; 200 // The number of references iterated past. 201 unsigned int itr_refs_; 202 // The argument number along argument list for current argument. 203 unsigned int itr_args_; 204 // Number of longs and doubles seen along argument list. 205 unsigned int itr_longs_and_doubles_; 206 // Number of float and doubles seen along argument list. 207 unsigned int itr_float_and_doubles_; 208 // Space for frames below this on the stack. 209 FrameOffset displacement_; 210 // The size of a pointer. 211 const PointerSize frame_pointer_size_; 212 213 private: 214 const bool is_static_; 215 const bool is_synchronized_; 216 std::string shorty_; 217 size_t num_args_; 218 size_t num_ref_args_; 219 size_t num_float_or_double_args_; 220 size_t num_long_or_double_args_; 221 }; 222 223 // Abstraction for managed code's calling conventions 224 // | { Incoming stack args } | 225 // | { Prior Method* } | <-- Prior SP 226 // | { Return address } | 227 // | { Callee saves } | 228 // | { Spills ... } | 229 // | { Outgoing stack args } | 230 // | { Method* } | <-- SP 231 class ManagedRuntimeCallingConvention : public CallingConvention { 232 public: 233 static std::unique_ptr<ManagedRuntimeCallingConvention> Create(ArenaAllocator* allocator, 234 bool is_static, 235 bool is_synchronized, 236 const char* shorty, 237 InstructionSet instruction_set); 238 239 // Offset of Method within the managed frame. MethodStackOffset()240 FrameOffset MethodStackOffset() { 241 return FrameOffset(0u); 242 } 243 244 // Register that holds the incoming method argument 245 virtual ManagedRegister MethodRegister() = 0; 246 247 // Iterator interface 248 bool HasNext(); 249 virtual void Next(); 250 bool IsCurrentParamAReference(); 251 bool IsCurrentParamAFloatOrDouble(); 252 bool IsCurrentParamADouble(); 253 bool IsCurrentParamALong(); IsCurrentParamALongOrDouble()254 bool IsCurrentParamALongOrDouble() { 255 return IsCurrentParamALong() || IsCurrentParamADouble(); 256 } 257 bool IsCurrentArgExplicit(); // ie a non-implict argument such as this 258 bool IsCurrentArgPossiblyNull(); 259 size_t CurrentParamSize(); 260 virtual bool IsCurrentParamInRegister() = 0; 261 virtual bool IsCurrentParamOnStack() = 0; 262 virtual ManagedRegister CurrentParamRegister() = 0; 263 virtual FrameOffset CurrentParamStackOffset() = 0; 264 ~ManagedRuntimeCallingConvention()265 virtual ~ManagedRuntimeCallingConvention() {} 266 267 protected: ManagedRuntimeCallingConvention(bool is_static,bool is_synchronized,const char * shorty,PointerSize frame_pointer_size)268 ManagedRuntimeCallingConvention(bool is_static, 269 bool is_synchronized, 270 const char* shorty, 271 PointerSize frame_pointer_size) 272 : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {} 273 }; 274 275 // Abstraction for JNI calling conventions 276 // | { Incoming stack args } | <-- Prior SP 277 // | { Return address } | 278 // | { Callee saves } | ([1]) 279 // | { Return value spill } | (live on return slow paths) 280 // | { Local Ref. Table State } | 281 // | { Stack Indirect Ref. Table | 282 // | num. refs./link } | (here to prior SP is frame size) 283 // | { Method* } | <-- Anchor SP written to thread 284 // | { Outgoing stack args } | <-- SP at point of call 285 // | Native frame | 286 // 287 // [1] We must save all callee saves here to enable any exception throws to restore 288 // callee saves for frames above this one. 289 class JniCallingConvention : public CallingConvention { 290 public: 291 static std::unique_ptr<JniCallingConvention> Create(ArenaAllocator* allocator, 292 bool is_static, 293 bool is_synchronized, 294 bool is_fast_native, 295 bool is_critical_native, 296 const char* shorty, 297 InstructionSet instruction_set); 298 299 // Size of frame excluding space for outgoing args (its assumed Method* is 300 // always at the bottom of a frame, but this doesn't work for outgoing 301 // native args). Includes alignment. 302 virtual size_t FrameSize() const = 0; 303 // Size of outgoing frame, i.e. stack arguments, @CriticalNative return PC if needed, alignment. 304 // -- Arguments that are passed via registers are excluded from this size. 305 virtual size_t OutFrameSize() const = 0; 306 // Number of references in stack indirect reference table 307 size_t ReferenceCount() const; 308 // Register that holds result if it is integer. 309 virtual ManagedRegister IntReturnRegister() const = 0; 310 // Whether the compiler needs to ensure zero-/sign-extension of a small result type 311 virtual bool RequiresSmallResultTypeExtension() const = 0; 312 313 // Callee save registers to spill prior to native code (which may clobber) 314 virtual ArrayRef<const ManagedRegister> CalleeSaveRegisters() const = 0; 315 316 // Subset of core callee save registers that can be used for arbitrary purposes after 317 // constructing the JNI transition frame. These should be managed callee-saves as well. 318 // These should not include special purpose registers such as thread register. 319 // JNI compiler currently requires at least 3 callee save scratch registers. 320 virtual ArrayRef<const ManagedRegister> CalleeSaveScratchRegisters() const = 0; 321 322 // Subset of core argument registers that can be used for arbitrary purposes after 323 // calling the native function. These should exclude the return register(s). 324 virtual ArrayRef<const ManagedRegister> ArgumentScratchRegisters() const = 0; 325 326 // Spill mask values 327 virtual uint32_t CoreSpillMask() const = 0; 328 virtual uint32_t FpSpillMask() const = 0; 329 330 // Iterator interface 331 bool HasNext(); 332 virtual void Next(); 333 bool IsCurrentParamAReference(); 334 bool IsCurrentParamAFloatOrDouble(); 335 bool IsCurrentParamADouble(); 336 bool IsCurrentParamALong(); IsCurrentParamALongOrDouble()337 bool IsCurrentParamALongOrDouble() { 338 return IsCurrentParamALong() || IsCurrentParamADouble(); 339 } 340 bool IsCurrentParamJniEnv(); 341 size_t CurrentParamSize() const; 342 virtual bool IsCurrentParamInRegister() = 0; 343 virtual bool IsCurrentParamOnStack() = 0; 344 virtual ManagedRegister CurrentParamRegister() = 0; 345 virtual FrameOffset CurrentParamStackOffset() = 0; 346 ~JniCallingConvention()347 virtual ~JniCallingConvention() {} 348 SavedLocalReferenceCookieSize()349 static constexpr size_t SavedLocalReferenceCookieSize() { 350 return 4u; 351 } 352 IsFastNative()353 bool IsFastNative() const { 354 return is_fast_native_; 355 } 356 IsCriticalNative()357 bool IsCriticalNative() const { 358 return is_critical_native_; 359 } 360 361 // Does the transition have a method pointer in the stack frame? SpillsMethod()362 bool SpillsMethod() const { 363 // Exclude method pointer for @CriticalNative methods for optimization speed. 364 return !IsCriticalNative(); 365 } 366 367 // Locking argument register, used to pass the synchronization object for calls 368 // to `JniLockObject()` and `JniUnlockObject()`. 369 virtual ManagedRegister LockingArgumentRegister() const = 0; 370 371 // Hidden argument register, used to pass the method pointer for @CriticalNative call. 372 virtual ManagedRegister HiddenArgumentRegister() const = 0; 373 374 // Whether to use tail call (used only for @CriticalNative). 375 virtual bool UseTailCall() const = 0; 376 377 // Whether the return type is small. Used for RequiresSmallResultTypeExtension() 378 // on architectures that require the sign/zero extension. HasSmallReturnType()379 bool HasSmallReturnType() const { 380 Primitive::Type return_type = GetReturnType(); 381 return return_type == Primitive::kPrimByte || 382 return_type == Primitive::kPrimShort || 383 return_type == Primitive::kPrimBoolean || 384 return_type == Primitive::kPrimChar; 385 } 386 387 protected: 388 // Named iterator positions 389 enum IteratorPos { 390 kJniEnv = 0, 391 kObjectOrClass = 1 392 }; 393 JniCallingConvention(bool is_static,bool is_synchronized,bool is_fast_native,bool is_critical_native,const char * shorty,PointerSize frame_pointer_size)394 JniCallingConvention(bool is_static, 395 bool is_synchronized, 396 bool is_fast_native, 397 bool is_critical_native, 398 const char* shorty, 399 PointerSize frame_pointer_size) 400 : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size), 401 is_fast_native_(is_fast_native), 402 is_critical_native_(is_critical_native) {} 403 404 protected: 405 size_t NumberOfExtraArgumentsForJni() const; 406 407 // Does the transition have a local reference segment state? HasLocalReferenceSegmentState()408 bool HasLocalReferenceSegmentState() const { 409 // Exclude local reference segment states for @CriticalNative methods for optimization speed. 410 return !IsCriticalNative(); 411 } 412 413 // Are there extra JNI arguments (JNIEnv* and maybe jclass)? HasExtraArgumentsForJni()414 bool HasExtraArgumentsForJni() const { 415 // @CriticalNative jni implementations exclude both JNIEnv* and the jclass/jobject parameters. 416 return !IsCriticalNative(); 417 } 418 419 // Has a JNIEnv* parameter implicitly? HasJniEnv()420 bool HasJniEnv() const { 421 // Exclude "JNIEnv*" parameter for @CriticalNative methods. 422 return HasExtraArgumentsForJni(); 423 } 424 425 // Has a 'jclass' parameter implicitly? 426 bool HasSelfClass() const; 427 428 // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments. 429 unsigned int GetIteratorPositionWithinShorty() const; 430 431 // Is the current argument (at the iterator) an extra argument for JNI? 432 bool IsCurrentArgExtraForJni() const; 433 434 const bool is_fast_native_; 435 const bool is_critical_native_; 436 437 private: 438 // Shorthand for switching on the switch value but only IF there are extra JNI arguments. 439 // 440 // Puts the case value into return_value. 441 // * (switch_value == kJniEnv) => case_jni_env 442 // * (switch_value == kObjectOrClass) => case_object_or_class 443 // 444 // Returns false otherwise (or if there are no extra JNI arguments). 445 bool SwitchExtraJniArguments(size_t switch_value, 446 bool case_jni_env, 447 bool case_object_or_class, 448 /* out parameters */ 449 bool* return_value) const; 450 }; 451 452 } // namespace art 453 454 #endif // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_ 455