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