1 // Copyright 2014 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_COMPILER_LINKAGE_H_ 6 #define V8_COMPILER_LINKAGE_H_ 7 8 #include "src/base/flags.h" 9 #include "src/compiler/frame.h" 10 #include "src/compiler/operator.h" 11 #include "src/frames.h" 12 #include "src/machine-type.h" 13 #include "src/runtime/runtime.h" 14 #include "src/zone.h" 15 16 namespace v8 { 17 namespace internal { 18 19 class CallInterfaceDescriptor; 20 class CompilationInfo; 21 22 namespace compiler { 23 24 const RegList kNoCalleeSaved = 0; 25 26 class Node; 27 class OsrHelper; 28 29 // Describes the location for a parameter or a return value to a call. 30 class LinkageLocation { 31 public: 32 bool operator==(const LinkageLocation& other) const { 33 return bit_field_ == other.bit_field_; 34 } 35 36 bool operator!=(const LinkageLocation& other) const { 37 return !(*this == other); 38 } 39 ForAnyRegister()40 static LinkageLocation ForAnyRegister() { 41 return LinkageLocation(REGISTER, ANY_REGISTER); 42 } 43 ForRegister(int32_t reg)44 static LinkageLocation ForRegister(int32_t reg) { 45 DCHECK(reg >= 0); 46 return LinkageLocation(REGISTER, reg); 47 } 48 ForCallerFrameSlot(int32_t slot)49 static LinkageLocation ForCallerFrameSlot(int32_t slot) { 50 DCHECK(slot < 0); 51 return LinkageLocation(STACK_SLOT, slot); 52 } 53 ForCalleeFrameSlot(int32_t slot)54 static LinkageLocation ForCalleeFrameSlot(int32_t slot) { 55 // TODO(titzer): bailout instead of crashing here. 56 DCHECK(slot >= 0 && slot < LinkageLocation::MAX_STACK_SLOT); 57 return LinkageLocation(STACK_SLOT, slot); 58 } 59 ForSavedCallerReturnAddress()60 static LinkageLocation ForSavedCallerReturnAddress() { 61 return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset - 62 StandardFrameConstants::kCallerPCOffset) / 63 kPointerSize); 64 } 65 ForSavedCallerFramePtr()66 static LinkageLocation ForSavedCallerFramePtr() { 67 return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset - 68 StandardFrameConstants::kCallerFPOffset) / 69 kPointerSize); 70 } 71 ForSavedCallerConstantPool()72 static LinkageLocation ForSavedCallerConstantPool() { 73 DCHECK(V8_EMBEDDED_CONSTANT_POOL); 74 return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset - 75 StandardFrameConstants::kConstantPoolOffset) / 76 kPointerSize); 77 } 78 ConvertToTailCallerLocation(LinkageLocation caller_location,int stack_param_delta)79 static LinkageLocation ConvertToTailCallerLocation( 80 LinkageLocation caller_location, int stack_param_delta) { 81 if (!caller_location.IsRegister()) { 82 return LinkageLocation(STACK_SLOT, 83 caller_location.GetLocation() - stack_param_delta); 84 } 85 return caller_location; 86 } 87 88 private: 89 friend class CallDescriptor; 90 friend class OperandGenerator; 91 92 enum LocationType { REGISTER, STACK_SLOT }; 93 94 class TypeField : public BitField<LocationType, 0, 1> {}; 95 class LocationField : public BitField<int32_t, TypeField::kNext, 31> {}; 96 97 static const int32_t ANY_REGISTER = -1; 98 static const int32_t MAX_STACK_SLOT = 32767; 99 LinkageLocation(LocationType type,int32_t location)100 LinkageLocation(LocationType type, int32_t location) { 101 bit_field_ = TypeField::encode(type) | 102 ((location << LocationField::kShift) & LocationField::kMask); 103 } 104 GetLocation()105 int32_t GetLocation() const { 106 return static_cast<int32_t>(bit_field_ & LocationField::kMask) >> 107 LocationField::kShift; 108 } 109 IsRegister()110 bool IsRegister() const { return TypeField::decode(bit_field_) == REGISTER; } IsAnyRegister()111 bool IsAnyRegister() const { 112 return IsRegister() && GetLocation() == ANY_REGISTER; 113 } IsCallerFrameSlot()114 bool IsCallerFrameSlot() const { return !IsRegister() && GetLocation() < 0; } IsCalleeFrameSlot()115 bool IsCalleeFrameSlot() const { return !IsRegister() && GetLocation() >= 0; } 116 AsRegister()117 int32_t AsRegister() const { 118 DCHECK(IsRegister()); 119 return GetLocation(); 120 } AsCallerFrameSlot()121 int32_t AsCallerFrameSlot() const { 122 DCHECK(IsCallerFrameSlot()); 123 return GetLocation(); 124 } AsCalleeFrameSlot()125 int32_t AsCalleeFrameSlot() const { 126 DCHECK(IsCalleeFrameSlot()); 127 return GetLocation(); 128 } 129 130 int32_t bit_field_; 131 }; 132 133 typedef Signature<LinkageLocation> LocationSignature; 134 135 // Describes a call to various parts of the compiler. Every call has the notion 136 // of a "target", which is the first input to the call. 137 class CallDescriptor final : public ZoneObject { 138 public: 139 // Describes the kind of this call, which determines the target. 140 enum Kind { 141 kCallCodeObject, // target is a Code object 142 kCallJSFunction, // target is a JSFunction object 143 kCallAddress, // target is a machine pointer 144 kLazyBailout // the call is no-op, only used for lazy bailout 145 }; 146 147 enum Flag { 148 kNoFlags = 0u, 149 kNeedsFrameState = 1u << 0, 150 kPatchableCallSite = 1u << 1, 151 kNeedsNopAfterCall = 1u << 2, 152 kHasExceptionHandler = 1u << 3, 153 kHasLocalCatchHandler = 1u << 4, 154 kSupportsTailCalls = 1u << 5, 155 kCanUseRoots = 1u << 6, 156 // Indicates that the native stack should be used for a code object. This 157 // information is important for native calls on arm64. 158 kUseNativeStack = 1u << 7, 159 kPatchableCallSiteWithNop = kPatchableCallSite | kNeedsNopAfterCall 160 }; 161 typedef base::Flags<Flag> Flags; 162 163 CallDescriptor(Kind kind, MachineType target_type, LinkageLocation target_loc, 164 const MachineSignature* machine_sig, 165 LocationSignature* location_sig, size_t stack_param_count, 166 Operator::Properties properties, 167 RegList callee_saved_registers, 168 RegList callee_saved_fp_registers, Flags flags, 169 const char* debug_name = "") kind_(kind)170 : kind_(kind), 171 target_type_(target_type), 172 target_loc_(target_loc), 173 machine_sig_(machine_sig), 174 location_sig_(location_sig), 175 stack_param_count_(stack_param_count), 176 properties_(properties), 177 callee_saved_registers_(callee_saved_registers), 178 callee_saved_fp_registers_(callee_saved_fp_registers), 179 flags_(flags), 180 debug_name_(debug_name) { 181 DCHECK(machine_sig->return_count() == location_sig->return_count()); 182 DCHECK(machine_sig->parameter_count() == location_sig->parameter_count()); 183 } 184 185 // Returns the kind of this call. kind()186 Kind kind() const { return kind_; } 187 188 // Returns {true} if this descriptor is a call to a C function. IsCFunctionCall()189 bool IsCFunctionCall() const { return kind_ == kCallAddress; } 190 191 // Returns {true} if this descriptor is a call to a JSFunction. IsJSFunctionCall()192 bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; } 193 RequiresFrameAsIncoming()194 bool RequiresFrameAsIncoming() const { 195 return IsCFunctionCall() || IsJSFunctionCall(); 196 } 197 198 // The number of return values from this call. ReturnCount()199 size_t ReturnCount() const { return machine_sig_->return_count(); } 200 201 // The number of C parameters to this call. CParameterCount()202 size_t CParameterCount() const { return machine_sig_->parameter_count(); } 203 204 // The number of stack parameters to the call. StackParameterCount()205 size_t StackParameterCount() const { return stack_param_count_; } 206 207 // The number of parameters to the JS function call. JSParameterCount()208 size_t JSParameterCount() const { 209 DCHECK(IsJSFunctionCall()); 210 return stack_param_count_; 211 } 212 213 // The total number of inputs to this call, which includes the target, 214 // receiver, context, etc. 215 // TODO(titzer): this should input the framestate input too. InputCount()216 size_t InputCount() const { return 1 + machine_sig_->parameter_count(); } 217 FrameStateCount()218 size_t FrameStateCount() const { return NeedsFrameState() ? 1 : 0; } 219 flags()220 Flags flags() const { return flags_; } 221 NeedsFrameState()222 bool NeedsFrameState() const { return flags() & kNeedsFrameState; } SupportsTailCalls()223 bool SupportsTailCalls() const { return flags() & kSupportsTailCalls; } UseNativeStack()224 bool UseNativeStack() const { return flags() & kUseNativeStack; } 225 GetReturnLocation(size_t index)226 LinkageLocation GetReturnLocation(size_t index) const { 227 return location_sig_->GetReturn(index); 228 } 229 GetInputLocation(size_t index)230 LinkageLocation GetInputLocation(size_t index) const { 231 if (index == 0) return target_loc_; 232 return location_sig_->GetParam(index - 1); 233 } 234 GetMachineSignature()235 const MachineSignature* GetMachineSignature() const { return machine_sig_; } 236 GetReturnType(size_t index)237 MachineType GetReturnType(size_t index) const { 238 return machine_sig_->GetReturn(index); 239 } 240 GetInputType(size_t index)241 MachineType GetInputType(size_t index) const { 242 if (index == 0) return target_type_; 243 return machine_sig_->GetParam(index - 1); 244 } 245 246 // Operator properties describe how this call can be optimized, if at all. properties()247 Operator::Properties properties() const { return properties_; } 248 249 // Get the callee-saved registers, if any, across this call. CalleeSavedRegisters()250 RegList CalleeSavedRegisters() const { return callee_saved_registers_; } 251 252 // Get the callee-saved FP registers, if any, across this call. CalleeSavedFPRegisters()253 RegList CalleeSavedFPRegisters() const { return callee_saved_fp_registers_; } 254 debug_name()255 const char* debug_name() const { return debug_name_; } 256 257 bool UsesOnlyRegisters() const; 258 259 bool HasSameReturnLocationsAs(const CallDescriptor* other) const; 260 261 bool CanTailCall(const Node* call, int* stack_param_delta) const; 262 263 private: 264 friend class Linkage; 265 266 const Kind kind_; 267 const MachineType target_type_; 268 const LinkageLocation target_loc_; 269 const MachineSignature* const machine_sig_; 270 const LocationSignature* const location_sig_; 271 const size_t stack_param_count_; 272 const Operator::Properties properties_; 273 const RegList callee_saved_registers_; 274 const RegList callee_saved_fp_registers_; 275 const Flags flags_; 276 const char* const debug_name_; 277 278 DISALLOW_COPY_AND_ASSIGN(CallDescriptor); 279 }; 280 281 DEFINE_OPERATORS_FOR_FLAGS(CallDescriptor::Flags) 282 283 std::ostream& operator<<(std::ostream& os, const CallDescriptor& d); 284 std::ostream& operator<<(std::ostream& os, const CallDescriptor::Kind& k); 285 286 // Defines the linkage for a compilation, including the calling conventions 287 // for incoming parameters and return value(s) as well as the outgoing calling 288 // convention for any kind of call. Linkage is generally architecture-specific. 289 // 290 // Can be used to translate {arg_index} (i.e. index of the call node input) as 291 // well as {param_index} (i.e. as stored in parameter nodes) into an operator 292 // representing the architecture-specific location. The following call node 293 // layouts are supported (where {n} is the number of value inputs): 294 // 295 // #0 #1 #2 #3 [...] #n 296 // Call[CodeStub] code, arg 1, arg 2, arg 3, [...], context 297 // Call[JSFunction] function, rcvr, arg 1, arg 2, [...], new, #arg, context 298 // Call[Runtime] CEntryStub, arg 1, arg 2, arg 3, [...], fun, #arg, context 299 class Linkage : public ZoneObject { 300 public: Linkage(CallDescriptor * incoming)301 explicit Linkage(CallDescriptor* incoming) : incoming_(incoming) {} 302 303 static CallDescriptor* ComputeIncoming(Zone* zone, CompilationInfo* info); 304 305 // The call descriptor for this compilation unit describes the locations 306 // of incoming parameters and the outgoing return value(s). GetIncomingDescriptor()307 CallDescriptor* GetIncomingDescriptor() const { return incoming_; } 308 static CallDescriptor* GetJSCallDescriptor(Zone* zone, bool is_osr, 309 int parameter_count, 310 CallDescriptor::Flags flags); 311 312 static CallDescriptor* GetRuntimeCallDescriptor( 313 Zone* zone, Runtime::FunctionId function, int parameter_count, 314 Operator::Properties properties, CallDescriptor::Flags flags); 315 316 static CallDescriptor* GetLazyBailoutDescriptor(Zone* zone); 317 318 static CallDescriptor* GetStubCallDescriptor( 319 Isolate* isolate, Zone* zone, const CallInterfaceDescriptor& descriptor, 320 int stack_parameter_count, CallDescriptor::Flags flags, 321 Operator::Properties properties = Operator::kNoProperties, 322 MachineType return_type = MachineType::AnyTagged(), 323 size_t return_count = 1); 324 325 // Creates a call descriptor for simplified C calls that is appropriate 326 // for the host platform. This simplified calling convention only supports 327 // integers and pointers of one word size each, i.e. no floating point, 328 // structs, pointers to members, etc. 329 static CallDescriptor* GetSimplifiedCDescriptor(Zone* zone, 330 const MachineSignature* sig); 331 332 // Creates a call descriptor for interpreter handler code stubs. These are not 333 // intended to be called directly but are instead dispatched to by the 334 // interpreter. 335 static CallDescriptor* GetInterpreterDispatchDescriptor(Zone* zone); 336 337 // Get the location of an (incoming) parameter to this function. GetParameterLocation(int index)338 LinkageLocation GetParameterLocation(int index) const { 339 return incoming_->GetInputLocation(index + 1); // + 1 to skip target. 340 } 341 342 // Get the machine type of an (incoming) parameter to this function. GetParameterType(int index)343 MachineType GetParameterType(int index) const { 344 return incoming_->GetInputType(index + 1); // + 1 to skip target. 345 } 346 347 // Get the location where this function should place its return value. 348 LinkageLocation GetReturnLocation(size_t index = 0) const { 349 return incoming_->GetReturnLocation(index); 350 } 351 352 // Get the machine type of this function's return value. 353 MachineType GetReturnType(size_t index = 0) const { 354 return incoming_->GetReturnType(index); 355 } 356 357 bool ParameterHasSecondaryLocation(int index) const; 358 LinkageLocation GetParameterSecondaryLocation(int index) const; 359 360 static int FrameStateInputCount(Runtime::FunctionId function); 361 362 // Get the location where an incoming OSR value is stored. 363 LinkageLocation GetOsrValueLocation(int index) const; 364 365 // A special {Parameter} index for JSCalls that represents the new target. GetJSCallNewTargetParamIndex(int parameter_count)366 static int GetJSCallNewTargetParamIndex(int parameter_count) { 367 return parameter_count + 0; // Parameter (arity + 0) is special. 368 } 369 370 // A special {Parameter} index for JSCalls that represents the argument count. GetJSCallArgCountParamIndex(int parameter_count)371 static int GetJSCallArgCountParamIndex(int parameter_count) { 372 return parameter_count + 1; // Parameter (arity + 1) is special. 373 } 374 375 // A special {Parameter} index for JSCalls that represents the context. GetJSCallContextParamIndex(int parameter_count)376 static int GetJSCallContextParamIndex(int parameter_count) { 377 return parameter_count + 2; // Parameter (arity + 2) is special. 378 } 379 380 // A special {Parameter} index for JSCalls that represents the closure. 381 static const int kJSCallClosureParamIndex = -1; 382 383 // A special {OsrValue} index to indicate the context spill slot. 384 static const int kOsrContextSpillSlotIndex = -1; 385 386 // Special parameter indices used to pass fixed register data through 387 // interpreter dispatches. 388 static const int kInterpreterAccumulatorParameter = 0; 389 static const int kInterpreterRegisterFileParameter = 1; 390 static const int kInterpreterBytecodeOffsetParameter = 2; 391 static const int kInterpreterBytecodeArrayParameter = 3; 392 static const int kInterpreterDispatchTableParameter = 4; 393 static const int kInterpreterContextParameter = 5; 394 395 private: 396 CallDescriptor* const incoming_; 397 398 DISALLOW_COPY_AND_ASSIGN(Linkage); 399 }; 400 401 } // namespace compiler 402 } // namespace internal 403 } // namespace v8 404 405 #endif // V8_COMPILER_LINKAGE_H_ 406