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/macros.h" 23 #include "base/pointer_size.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,std::string_view shorty,PointerSize frame_pointer_size)77 CallingConvention(bool is_static, 78 bool is_synchronized, 79 std::string_view 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) + shorty.length() - 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 < shorty.length(); 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(size_t param,size_t reference_size)181 size_t ParamSize(size_t param, size_t reference_size) 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 reference_size; // this argument 187 } 188 Primitive::Type type = Primitive::GetType(shorty_[param]); 189 if (type == Primitive::kPrimNot) { 190 return reference_size; 191 } 192 size_t result = Primitive::ComponentSize(type); 193 if (result >= 1 && result < 4) { 194 result = 4; 195 } 196 return result; 197 } GetShorty()198 std::string_view GetShorty() const { 199 return shorty_; 200 } 201 // The slot number for current calling_convention argument. 202 // Note that each slot is 32-bit. When the current argument is bigger 203 // than 32 bits, return the first slot number for this argument. 204 unsigned int itr_slots_; 205 // The number of references iterated past. 206 unsigned int itr_refs_; 207 // The argument number along argument list for current argument. 208 unsigned int itr_args_; 209 // Number of longs and doubles seen along argument list. 210 unsigned int itr_longs_and_doubles_; 211 // Number of float and doubles seen along argument list. 212 unsigned int itr_float_and_doubles_; 213 // Space for frames below this on the stack. 214 FrameOffset displacement_; 215 // The size of a pointer. 216 const PointerSize frame_pointer_size_; 217 218 private: 219 const bool is_static_; 220 const bool is_synchronized_; 221 std::string shorty_; 222 size_t num_args_; 223 size_t num_ref_args_; 224 size_t num_float_or_double_args_; 225 size_t num_long_or_double_args_; 226 }; 227 228 // Abstraction for managed code's calling conventions 229 // | { Incoming stack args } | 230 // | { Prior Method* } | <-- Prior SP 231 // | { Return address } | 232 // | { Callee saves } | 233 // | { Spills ... } | 234 // | { Outgoing stack args } | 235 // | { Method* } | <-- SP 236 class ManagedRuntimeCallingConvention : public CallingConvention { 237 public: 238 static std::unique_ptr<ManagedRuntimeCallingConvention> Create(ArenaAllocator* allocator, 239 bool is_static, 240 bool is_synchronized, 241 std::string_view shorty, 242 InstructionSet instruction_set); 243 244 // Offset of Method within the managed frame. MethodStackOffset()245 FrameOffset MethodStackOffset() { 246 return FrameOffset(0u); 247 } 248 249 // Register that holds the incoming method argument 250 virtual ManagedRegister MethodRegister() = 0; 251 252 // Register that is used to pass frame size for method exit hook call. This 253 // shouldn't be the same as the return register since method exit hook also expects 254 // return values in the return register. 255 virtual ManagedRegister ArgumentRegisterForMethodExitHook() = 0; 256 257 // Iterator interface 258 bool HasNext(); 259 virtual void Next(); 260 bool IsCurrentParamAReference(); 261 bool IsCurrentParamAFloatOrDouble(); 262 bool IsCurrentParamADouble(); 263 bool IsCurrentParamALong(); IsCurrentParamALongOrDouble()264 bool IsCurrentParamALongOrDouble() { 265 return IsCurrentParamALong() || IsCurrentParamADouble(); 266 } 267 bool IsCurrentArgExplicit(); // ie a non-implict argument such as this 268 bool IsCurrentArgPossiblyNull(); 269 size_t CurrentParamSize(); 270 virtual bool IsCurrentParamInRegister() = 0; 271 virtual bool IsCurrentParamOnStack() = 0; 272 virtual ManagedRegister CurrentParamRegister() = 0; 273 virtual FrameOffset CurrentParamStackOffset() = 0; 274 ~ManagedRuntimeCallingConvention()275 virtual ~ManagedRuntimeCallingConvention() {} 276 277 protected: ManagedRuntimeCallingConvention(bool is_static,bool is_synchronized,std::string_view shorty,PointerSize frame_pointer_size)278 ManagedRuntimeCallingConvention(bool is_static, 279 bool is_synchronized, 280 std::string_view shorty, 281 PointerSize frame_pointer_size) 282 : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {} 283 }; 284 285 // Abstraction for JNI calling conventions 286 // | { Incoming stack args } | <-- Prior SP 287 // | { Return address } | 288 // | { Callee saves } | ([1]) 289 // | { Return value spill } | (live on return slow paths) 290 // | { Local Ref. Table State } | 291 // | { Stack Indirect Ref. Table | 292 // | num. refs./link } | (here to prior SP is frame size) 293 // | { Method* } | <-- Anchor SP written to thread 294 // | { Outgoing stack args } | <-- SP at point of call 295 // | Native frame | 296 // 297 // [1] We must save all callee saves here to enable any exception throws to restore 298 // callee saves for frames above this one. 299 class JniCallingConvention : public CallingConvention { 300 public: 301 static std::unique_ptr<JniCallingConvention> Create(ArenaAllocator* allocator, 302 bool is_static, 303 bool is_synchronized, 304 bool is_fast_native, 305 bool is_critical_native, 306 std::string_view shorty, 307 InstructionSet instruction_set); 308 309 // Size of frame excluding space for outgoing args (its assumed Method* is 310 // always at the bottom of a frame, but this doesn't work for outgoing 311 // native args). Includes alignment. 312 virtual size_t FrameSize() const = 0; 313 // Size of outgoing frame, i.e. stack arguments, @CriticalNative return PC if needed, alignment. 314 // -- Arguments that are passed via registers are excluded from this size. 315 virtual size_t OutFrameSize() const = 0; 316 // Number of references in stack indirect reference table 317 size_t ReferenceCount() const; 318 // Register that holds result if it is integer. 319 virtual ManagedRegister IntReturnRegister() const = 0; 320 // Whether the compiler needs to ensure zero-/sign-extension of a small result type 321 virtual bool RequiresSmallResultTypeExtension() const = 0; 322 323 // Callee save registers to spill prior to native code (which may clobber) 324 virtual ArrayRef<const ManagedRegister> CalleeSaveRegisters() const = 0; 325 326 // Subset of core callee save registers that can be used for arbitrary purposes after 327 // constructing the JNI transition frame. These should be both managed and native callee-saves. 328 // These should not include special purpose registers such as thread register. 329 // JNI compiler currently requires at least 4 callee save scratch registers, except for x86 330 // where we have only 3 such registers but all args are passed on stack, so the method register 331 // is never clobbered by argument moves and does not need to be preserved elsewhere. 332 virtual ArrayRef<const ManagedRegister> CalleeSaveScratchRegisters() const = 0; 333 334 // Subset of core argument registers that can be used for arbitrary purposes after 335 // calling the native function. These should exclude the return register(s). 336 virtual ArrayRef<const ManagedRegister> ArgumentScratchRegisters() const = 0; 337 338 // Spill mask values 339 virtual uint32_t CoreSpillMask() const = 0; 340 virtual uint32_t FpSpillMask() const = 0; 341 342 // Iterator interface 343 bool HasNext(); 344 virtual void Next(); 345 bool IsCurrentParamAReference(); 346 bool IsCurrentParamAFloatOrDouble(); 347 bool IsCurrentParamADouble(); 348 bool IsCurrentParamALong(); IsCurrentParamALongOrDouble()349 bool IsCurrentParamALongOrDouble() { 350 return IsCurrentParamALong() || IsCurrentParamADouble(); 351 } 352 bool IsCurrentParamJniEnv(); 353 virtual size_t CurrentParamSize() const; 354 virtual bool IsCurrentParamInRegister() = 0; 355 virtual bool IsCurrentParamOnStack() = 0; 356 virtual ManagedRegister CurrentParamRegister() = 0; 357 virtual FrameOffset CurrentParamStackOffset() = 0; 358 ~JniCallingConvention()359 virtual ~JniCallingConvention() {} 360 IsFastNative()361 bool IsFastNative() const { 362 return is_fast_native_; 363 } 364 IsCriticalNative()365 bool IsCriticalNative() const { 366 return is_critical_native_; 367 } 368 369 // Does the transition have a method pointer in the stack frame? SpillsMethod()370 bool SpillsMethod() const { 371 // Exclude method pointer for @CriticalNative methods for optimization speed. 372 return !IsCriticalNative(); 373 } 374 375 // Locking argument register, used to pass the synchronization object for calls 376 // to `JniLockObject()` and `JniUnlockObject()`. 377 virtual ManagedRegister LockingArgumentRegister() const = 0; 378 379 // Hidden argument register, used to pass the method pointer for @CriticalNative call. 380 virtual ManagedRegister HiddenArgumentRegister() const = 0; 381 382 // Whether to use tail call (used only for @CriticalNative). 383 virtual bool UseTailCall() const = 0; 384 385 // Whether the return type is small. Used for RequiresSmallResultTypeExtension() 386 // on architectures that require the sign/zero extension. HasSmallReturnType()387 bool HasSmallReturnType() const { 388 Primitive::Type return_type = GetReturnType(); 389 return return_type == Primitive::kPrimByte || 390 return_type == Primitive::kPrimShort || 391 return_type == Primitive::kPrimBoolean || 392 return_type == Primitive::kPrimChar; 393 } 394 395 protected: 396 // Named iterator positions 397 enum IteratorPos { 398 kJniEnv = 0, 399 kObjectOrClass = 1 400 }; 401 JniCallingConvention(bool is_static,bool is_synchronized,bool is_fast_native,bool is_critical_native,std::string_view shorty,PointerSize frame_pointer_size)402 JniCallingConvention(bool is_static, 403 bool is_synchronized, 404 bool is_fast_native, 405 bool is_critical_native, 406 std::string_view shorty, 407 PointerSize frame_pointer_size) 408 : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size), 409 is_fast_native_(is_fast_native), 410 is_critical_native_(is_critical_native) {} 411 412 protected: 413 size_t NumberOfExtraArgumentsForJni() const; 414 415 // Does the transition have a local reference segment state? HasLocalReferenceSegmentState()416 bool HasLocalReferenceSegmentState() const { 417 // Exclude local reference segment states for @CriticalNative methods for optimization speed. 418 return !IsCriticalNative(); 419 } 420 421 // Are there extra JNI arguments (JNIEnv* and maybe jclass)? HasExtraArgumentsForJni()422 bool HasExtraArgumentsForJni() const { 423 // @CriticalNative jni implementations exclude both JNIEnv* and the jclass/jobject parameters. 424 return !IsCriticalNative(); 425 } 426 427 // Has a JNIEnv* parameter implicitly? HasJniEnv()428 bool HasJniEnv() const { 429 // Exclude "JNIEnv*" parameter for @CriticalNative methods. 430 return HasExtraArgumentsForJni(); 431 } 432 433 // Has a 'jclass' parameter implicitly? 434 bool HasSelfClass() const; 435 436 // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments. 437 size_t GetIteratorPositionWithinShorty() const; 438 439 // Is the current argument (at the iterator) an extra argument for JNI? 440 bool IsCurrentArgExtraForJni() const; 441 442 const bool is_fast_native_; 443 const bool is_critical_native_; 444 445 private: 446 // Shorthand for switching on the switch value but only IF there are extra JNI arguments. 447 // 448 // Puts the case value into return_value. 449 // * (switch_value == kJniEnv) => case_jni_env 450 // * (switch_value == kObjectOrClass) => case_object_or_class 451 // 452 // Returns false otherwise (or if there are no extra JNI arguments). 453 bool SwitchExtraJniArguments(size_t switch_value, 454 bool case_jni_env, 455 bool case_object_or_class, 456 /* out parameters */ 457 bool* return_value) const; 458 }; 459 460 } // namespace art 461 462 #endif // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_ 463