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