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 // Register reserved for scratch usage during procedure calls. 52 virtual ManagedRegister InterproceduralScratchRegister() = 0; 53 54 // Offset of Method within the frame. MethodStackOffset()55 FrameOffset MethodStackOffset() { 56 return displacement_; 57 } 58 59 // Iterator interface 60 61 // Place iterator at start of arguments. The displacement is applied to 62 // frame offset methods to account for frames which may be on the stack 63 // below the one being iterated over. ResetIterator(FrameOffset displacement)64 void ResetIterator(FrameOffset displacement) { 65 displacement_ = displacement; 66 itr_slots_ = 0; 67 itr_args_ = 0; 68 itr_refs_ = 0; 69 itr_longs_and_doubles_ = 0; 70 itr_float_and_doubles_ = 0; 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 handle_scope_pointer_size_(sizeof(StackReference<mirror::Object>)), 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 // The size of a reference entry within the handle scope. 214 const size_t handle_scope_pointer_size_; 215 216 private: 217 const bool is_static_; 218 const bool is_synchronized_; 219 std::string shorty_; 220 size_t num_args_; 221 size_t num_ref_args_; 222 size_t num_float_or_double_args_; 223 size_t num_long_or_double_args_; 224 }; 225 226 // Abstraction for managed code's calling conventions 227 // | { Incoming stack args } | 228 // | { Prior Method* } | <-- Prior SP 229 // | { Return address } | 230 // | { Callee saves } | 231 // | { Spills ... } | 232 // | { Outgoing stack args } | 233 // | { Method* } | <-- SP 234 class ManagedRuntimeCallingConvention : public CallingConvention { 235 public: 236 static std::unique_ptr<ManagedRuntimeCallingConvention> Create(ArenaAllocator* allocator, 237 bool is_static, 238 bool is_synchronized, 239 const char* shorty, 240 InstructionSet instruction_set); 241 242 // Register that holds the incoming method argument 243 virtual ManagedRegister MethodRegister() = 0; 244 245 // Iterator interface 246 bool HasNext(); 247 void Next(); 248 bool IsCurrentParamAReference(); 249 bool IsCurrentParamAFloatOrDouble(); 250 bool IsCurrentParamADouble(); 251 bool IsCurrentParamALong(); 252 bool IsCurrentArgExplicit(); // ie a non-implict argument such as this 253 bool IsCurrentArgPossiblyNull(); 254 size_t CurrentParamSize(); 255 virtual bool IsCurrentParamInRegister() = 0; 256 virtual bool IsCurrentParamOnStack() = 0; 257 virtual ManagedRegister CurrentParamRegister() = 0; 258 virtual FrameOffset CurrentParamStackOffset() = 0; 259 ~ManagedRuntimeCallingConvention()260 virtual ~ManagedRuntimeCallingConvention() {} 261 262 // Registers to spill to caller's out registers on entry. 263 virtual const ManagedRegisterEntrySpills& EntrySpills() = 0; 264 265 protected: ManagedRuntimeCallingConvention(bool is_static,bool is_synchronized,const char * shorty,PointerSize frame_pointer_size)266 ManagedRuntimeCallingConvention(bool is_static, 267 bool is_synchronized, 268 const char* shorty, 269 PointerSize frame_pointer_size) 270 : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size) {} 271 }; 272 273 // Abstraction for JNI calling conventions 274 // | { Incoming stack args } | <-- Prior SP 275 // | { Return address } | 276 // | { Callee saves } | ([1]) 277 // | { Return value spill } | (live on return slow paths) 278 // | { Local Ref. Table State } | 279 // | { Stack Indirect Ref. Table | 280 // | num. refs./link } | (here to prior SP is frame size) 281 // | { Method* } | <-- Anchor SP written to thread 282 // | { Outgoing stack args } | <-- SP at point of call 283 // | Native frame | 284 // 285 // [1] We must save all callee saves here to enable any exception throws to restore 286 // callee saves for frames above this one. 287 class JniCallingConvention : public CallingConvention { 288 public: 289 static std::unique_ptr<JniCallingConvention> Create(ArenaAllocator* allocator, 290 bool is_static, 291 bool is_synchronized, 292 bool is_critical_native, 293 const char* shorty, 294 InstructionSet instruction_set); 295 296 // Size of frame excluding space for outgoing args (its assumed Method* is 297 // always at the bottom of a frame, but this doesn't work for outgoing 298 // native args). Includes alignment. 299 virtual size_t FrameSize() = 0; 300 // Size of outgoing arguments (stack portion), including alignment. 301 // -- Arguments that are passed via registers are excluded from this size. 302 virtual size_t OutArgSize() = 0; 303 // Number of references in stack indirect reference table 304 size_t ReferenceCount() const; 305 // Location where the segment state of the local indirect reference table is saved 306 FrameOffset SavedLocalReferenceCookieOffset() 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 // Spill mask values 319 virtual uint32_t CoreSpillMask() const = 0; 320 virtual uint32_t FpSpillMask() const = 0; 321 322 // An extra scratch register live after the call 323 virtual ManagedRegister ReturnScratchRegister() const = 0; 324 325 // Iterator interface 326 bool HasNext(); 327 virtual void Next(); 328 bool IsCurrentParamAReference(); 329 bool IsCurrentParamAFloatOrDouble(); 330 bool IsCurrentParamADouble(); 331 bool IsCurrentParamALong(); IsCurrentParamALongOrDouble()332 bool IsCurrentParamALongOrDouble() { 333 return IsCurrentParamALong() || IsCurrentParamADouble(); 334 } 335 bool IsCurrentParamJniEnv(); 336 size_t CurrentParamSize() const; 337 virtual bool IsCurrentParamInRegister() = 0; 338 virtual bool IsCurrentParamOnStack() = 0; 339 virtual ManagedRegister CurrentParamRegister() = 0; 340 virtual FrameOffset CurrentParamStackOffset() = 0; 341 342 // Iterator interface extension for JNI 343 FrameOffset CurrentParamHandleScopeEntryOffset(); 344 345 // Position of handle scope and interior fields HandleScopeOffset()346 FrameOffset HandleScopeOffset() const { 347 return FrameOffset(this->displacement_.Int32Value() + static_cast<size_t>(frame_pointer_size_)); 348 // above Method reference 349 } 350 HandleScopeLinkOffset()351 FrameOffset HandleScopeLinkOffset() const { 352 return FrameOffset(HandleScopeOffset().Int32Value() + 353 HandleScope::LinkOffset(frame_pointer_size_)); 354 } 355 HandleScopeNumRefsOffset()356 FrameOffset HandleScopeNumRefsOffset() const { 357 return FrameOffset(HandleScopeOffset().Int32Value() + 358 HandleScope::NumberOfReferencesOffset(frame_pointer_size_)); 359 } 360 HandleReferencesOffset()361 FrameOffset HandleReferencesOffset() const { 362 return FrameOffset(HandleScopeOffset().Int32Value() + 363 HandleScope::ReferencesOffset(frame_pointer_size_)); 364 } 365 ~JniCallingConvention()366 virtual ~JniCallingConvention() {} 367 368 protected: 369 // Named iterator positions 370 enum IteratorPos { 371 kJniEnv = 0, 372 kObjectOrClass = 1 373 }; 374 JniCallingConvention(bool is_static,bool is_synchronized,bool is_critical_native,const char * shorty,PointerSize frame_pointer_size)375 JniCallingConvention(bool is_static, 376 bool is_synchronized, 377 bool is_critical_native, 378 const char* shorty, 379 PointerSize frame_pointer_size) 380 : CallingConvention(is_static, is_synchronized, shorty, frame_pointer_size), 381 is_critical_native_(is_critical_native) {} 382 383 // Number of stack slots for outgoing arguments, above which the handle scope is 384 // located 385 virtual size_t NumberOfOutgoingStackArgs() = 0; 386 387 protected: 388 size_t NumberOfExtraArgumentsForJni() const; 389 390 // Does the transition have a StackHandleScope? 391 bool HasHandleScope() const; 392 // Does the transition have a local reference segment state? 393 bool HasLocalReferenceSegmentState() const; 394 // Has a JNIEnv* parameter implicitly? 395 bool HasJniEnv() const; 396 // Has a 'jclass' parameter implicitly? 397 bool HasSelfClass() const; 398 399 // Are there extra JNI arguments (JNIEnv* and maybe jclass)? 400 bool HasExtraArgumentsForJni() const; 401 402 // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments. 403 unsigned int GetIteratorPositionWithinShorty() const; 404 405 // Is the current argument (at the iterator) an extra argument for JNI? 406 bool IsCurrentArgExtraForJni() const; 407 408 const bool is_critical_native_; 409 410 private: 411 // Shorthand for switching on the switch value but only IF there are extra JNI arguments. 412 // 413 // Puts the case value into return_value. 414 // * (switch_value == kJniEnv) => case_jni_env 415 // * (switch_value == kObjectOrClass) => case_object_or_class 416 // 417 // Returns false otherwise (or if there are no extra JNI arguments). 418 bool SwitchExtraJniArguments(size_t switch_value, 419 bool case_jni_env, 420 bool case_object_or_class, 421 /* out parameters */ 422 bool* return_value) const; 423 }; 424 425 } // namespace art 426 427 #endif // ART_COMPILER_JNI_QUICK_CALLING_CONVENTION_H_ 428