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() = 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_critical_native, 295 const char* shorty, 296 InstructionSet instruction_set); 297 298 // Size of frame excluding space for outgoing args (its assumed Method* is 299 // always at the bottom of a frame, but this doesn't work for outgoing 300 // native args). Includes alignment. 301 virtual size_t FrameSize() const = 0; 302 // Size of outgoing frame, i.e. stack arguments, @CriticalNative return PC if needed, alignment. 303 // -- Arguments that are passed via registers are excluded from this size. 304 virtual size_t OutFrameSize() const = 0; 305 // Number of references in stack indirect reference table 306 size_t ReferenceCount() const; 307 // Location where the return value of a call can be squirreled if another 308 // call is made following the native call 309 FrameOffset ReturnValueSaveLocation() const; 310 // Register that holds result if it is integer. 311 virtual ManagedRegister IntReturnRegister() = 0; 312 // Whether the compiler needs to ensure zero-/sign-extension of a small result type 313 virtual bool RequiresSmallResultTypeExtension() const = 0; 314 315 // Callee save registers to spill prior to native code (which may clobber) 316 virtual ArrayRef<const ManagedRegister> CalleeSaveRegisters() const = 0; 317 318 // Register where the segment state of the local indirect reference table is saved. 319 // This must be a native callee-save register without another special purpose. 320 virtual ManagedRegister SavedLocalReferenceCookieRegister() const = 0; 321 322 // An extra scratch register live after the call 323 virtual ManagedRegister ReturnScratchRegister() const = 0; 324 325 // Spill mask values 326 virtual uint32_t CoreSpillMask() const = 0; 327 virtual uint32_t FpSpillMask() const = 0; 328 329 // Iterator interface 330 bool HasNext(); 331 virtual void Next(); 332 bool IsCurrentParamAReference(); 333 bool IsCurrentParamAFloatOrDouble(); 334 bool IsCurrentParamADouble(); 335 bool IsCurrentParamALong(); IsCurrentParamALongOrDouble()336 bool IsCurrentParamALongOrDouble() { 337 return IsCurrentParamALong() || IsCurrentParamADouble(); 338 } 339 bool IsCurrentParamJniEnv(); 340 size_t CurrentParamSize() const; 341 virtual bool IsCurrentParamInRegister() = 0; 342 virtual bool IsCurrentParamOnStack() = 0; 343 virtual ManagedRegister CurrentParamRegister() = 0; 344 virtual FrameOffset CurrentParamStackOffset() = 0; 345 ~JniCallingConvention()346 virtual ~JniCallingConvention() {} 347 SavedLocalReferenceCookieSize()348 static constexpr size_t SavedLocalReferenceCookieSize() { 349 return 4u; 350 } 351 IsCriticalNative()352 bool IsCriticalNative() const { 353 return is_critical_native_; 354 } 355 356 // Does the transition have a method pointer in the stack frame? SpillsMethod()357 bool SpillsMethod() const { 358 // Exclude method pointer for @CriticalNative methods for optimization speed. 359 return !IsCriticalNative(); 360 } 361 362 // Hidden argument register, used to pass the method pointer for @CriticalNative call. 363 virtual ManagedRegister HiddenArgumentRegister() const = 0; 364 365 // Whether to use tail call (used only for @CriticalNative). 366 virtual bool UseTailCall() const = 0; 367 368 // Whether the return type is small. Used for RequiresSmallResultTypeExtension() 369 // on architectures that require the sign/zero extension. HasSmallReturnType()370 bool HasSmallReturnType() const { 371 Primitive::Type return_type = GetReturnType(); 372 return return_type == Primitive::kPrimByte || 373 return_type == Primitive::kPrimShort || 374 return_type == Primitive::kPrimBoolean || 375 return_type == Primitive::kPrimChar; 376 } 377 378 // Does the transition back spill the return value in the stack frame? SpillsReturnValue()379 bool SpillsReturnValue() const { 380 // Exclude return value for @CriticalNative methods for optimization speed. 381 // References are passed directly to the "end method" and there is nothing to save for `void`. 382 return !IsCriticalNative() && !IsReturnAReference() && SizeOfReturnValue() != 0u; 383 } 384 385 protected: 386 // Named iterator positions 387 enum IteratorPos { 388 kJniEnv = 0, 389 kObjectOrClass = 1 390 }; 391 JniCallingConvention(bool is_static,bool is_synchronized,bool is_critical_native,const char * shorty,PointerSize frame_pointer_size)392 JniCallingConvention(bool is_static, 393 bool is_synchronized, 394 bool is_critical_native, 395 const char* shorty, 396 PointerSize frame_pointer_size) 397 : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size), 398 is_critical_native_(is_critical_native) {} 399 400 protected: 401 size_t NumberOfExtraArgumentsForJni() const; 402 403 // Does the transition have a local reference segment state? HasLocalReferenceSegmentState()404 bool HasLocalReferenceSegmentState() const { 405 // Exclude local reference segment states for @CriticalNative methods for optimization speed. 406 return !IsCriticalNative(); 407 } 408 409 // Are there extra JNI arguments (JNIEnv* and maybe jclass)? HasExtraArgumentsForJni()410 bool HasExtraArgumentsForJni() const { 411 // @CriticalNative jni implementations exclude both JNIEnv* and the jclass/jobject parameters. 412 return !IsCriticalNative(); 413 } 414 415 // Has a JNIEnv* parameter implicitly? HasJniEnv()416 bool HasJniEnv() const { 417 // Exclude "JNIEnv*" parameter for @CriticalNative methods. 418 return HasExtraArgumentsForJni(); 419 } 420 421 // Has a 'jclass' parameter implicitly? 422 bool HasSelfClass() const; 423 424 // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments. 425 unsigned int GetIteratorPositionWithinShorty() const; 426 427 // Is the current argument (at the iterator) an extra argument for JNI? 428 bool IsCurrentArgExtraForJni() const; 429 430 const bool is_critical_native_; 431 432 private: 433 // Shorthand for switching on the switch value but only IF there are extra JNI arguments. 434 // 435 // Puts the case value into return_value. 436 // * (switch_value == kJniEnv) => case_jni_env 437 // * (switch_value == kObjectOrClass) => case_object_or_class 438 // 439 // Returns false otherwise (or if there are no extra JNI arguments). 440 bool SwitchExtraJniArguments(size_t switch_value, 441 bool case_jni_env, 442 bool case_object_or_class, 443 /* out parameters */ 444 bool* return_value) const; 445 }; 446 447 } // namespace art 448 449 #endif // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_ 450