// Copyright 2014 the V8 project authors. All rights reserved.
|
// Use of this source code is governed by a BSD-style license that can be
|
// found in the LICENSE file.
|
|
#ifndef V8_COMPILER_LINKAGE_H_
|
#define V8_COMPILER_LINKAGE_H_
|
|
#include "src/base/compiler-specific.h"
|
#include "src/base/flags.h"
|
#include "src/compiler/frame.h"
|
#include "src/compiler/operator.h"
|
#include "src/globals.h"
|
#include "src/interface-descriptors.h"
|
#include "src/machine-type.h"
|
#include "src/reglist.h"
|
#include "src/runtime/runtime.h"
|
#include "src/signature.h"
|
#include "src/zone/zone.h"
|
|
namespace v8 {
|
namespace internal {
|
|
class CallInterfaceDescriptor;
|
class OptimizedCompilationInfo;
|
|
namespace compiler {
|
|
const RegList kNoCalleeSaved = 0;
|
|
class Node;
|
class OsrHelper;
|
|
// Describes the location for a parameter or a return value to a call.
|
class LinkageLocation {
|
public:
|
bool operator==(const LinkageLocation& other) const {
|
return bit_field_ == other.bit_field_;
|
}
|
|
bool operator!=(const LinkageLocation& other) const {
|
return !(*this == other);
|
}
|
|
static LinkageLocation ForAnyRegister(
|
MachineType type = MachineType::None()) {
|
return LinkageLocation(REGISTER, ANY_REGISTER, type);
|
}
|
|
static LinkageLocation ForRegister(int32_t reg,
|
MachineType type = MachineType::None()) {
|
DCHECK_LE(0, reg);
|
return LinkageLocation(REGISTER, reg, type);
|
}
|
|
static LinkageLocation ForCallerFrameSlot(int32_t slot, MachineType type) {
|
DCHECK_GT(0, slot);
|
return LinkageLocation(STACK_SLOT, slot, type);
|
}
|
|
static LinkageLocation ForCalleeFrameSlot(int32_t slot, MachineType type) {
|
// TODO(titzer): bailout instead of crashing here.
|
DCHECK(slot >= 0 && slot < LinkageLocation::MAX_STACK_SLOT);
|
return LinkageLocation(STACK_SLOT, slot, type);
|
}
|
|
static LinkageLocation ForSavedCallerReturnAddress() {
|
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
|
StandardFrameConstants::kCallerPCOffset) /
|
kPointerSize,
|
MachineType::Pointer());
|
}
|
|
static LinkageLocation ForSavedCallerFramePtr() {
|
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
|
StandardFrameConstants::kCallerFPOffset) /
|
kPointerSize,
|
MachineType::Pointer());
|
}
|
|
static LinkageLocation ForSavedCallerConstantPool() {
|
DCHECK(V8_EMBEDDED_CONSTANT_POOL);
|
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
|
StandardFrameConstants::kConstantPoolOffset) /
|
kPointerSize,
|
MachineType::AnyTagged());
|
}
|
|
static LinkageLocation ForSavedCallerFunction() {
|
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
|
StandardFrameConstants::kFunctionOffset) /
|
kPointerSize,
|
MachineType::AnyTagged());
|
}
|
|
static LinkageLocation ConvertToTailCallerLocation(
|
LinkageLocation caller_location, int stack_param_delta) {
|
if (!caller_location.IsRegister()) {
|
return LinkageLocation(STACK_SLOT,
|
caller_location.GetLocation() + stack_param_delta,
|
caller_location.GetType());
|
}
|
return caller_location;
|
}
|
|
MachineType GetType() const { return machine_type_; }
|
|
int GetSize() const {
|
return 1 << ElementSizeLog2Of(GetType().representation());
|
}
|
|
int GetSizeInPointers() const {
|
// Round up
|
return (GetSize() + kPointerSize - 1) / kPointerSize;
|
}
|
|
int32_t GetLocation() const {
|
// We can't use LocationField::decode here because it doesn't work for
|
// negative values!
|
return static_cast<int32_t>(bit_field_ & LocationField::kMask) >>
|
LocationField::kShift;
|
}
|
|
bool IsRegister() const { return TypeField::decode(bit_field_) == REGISTER; }
|
bool IsAnyRegister() const {
|
return IsRegister() && GetLocation() == ANY_REGISTER;
|
}
|
bool IsCallerFrameSlot() const { return !IsRegister() && GetLocation() < 0; }
|
bool IsCalleeFrameSlot() const { return !IsRegister() && GetLocation() >= 0; }
|
|
int32_t AsRegister() const {
|
DCHECK(IsRegister());
|
return GetLocation();
|
}
|
int32_t AsCallerFrameSlot() const {
|
DCHECK(IsCallerFrameSlot());
|
return GetLocation();
|
}
|
int32_t AsCalleeFrameSlot() const {
|
DCHECK(IsCalleeFrameSlot());
|
return GetLocation();
|
}
|
|
private:
|
enum LocationType { REGISTER, STACK_SLOT };
|
|
class TypeField : public BitField<LocationType, 0, 1> {};
|
class LocationField : public BitField<int32_t, TypeField::kNext, 31> {};
|
|
static constexpr int32_t ANY_REGISTER = -1;
|
static constexpr int32_t MAX_STACK_SLOT = 32767;
|
|
LinkageLocation(LocationType type, int32_t location,
|
MachineType machine_type) {
|
bit_field_ = TypeField::encode(type) |
|
((location << LocationField::kShift) & LocationField::kMask);
|
machine_type_ = machine_type;
|
}
|
|
int32_t bit_field_;
|
MachineType machine_type_;
|
};
|
|
typedef Signature<LinkageLocation> LocationSignature;
|
|
// Describes a call to various parts of the compiler. Every call has the notion
|
// of a "target", which is the first input to the call.
|
class V8_EXPORT_PRIVATE CallDescriptor final
|
: public NON_EXPORTED_BASE(ZoneObject) {
|
public:
|
// Describes the kind of this call, which determines the target.
|
enum Kind {
|
kCallCodeObject, // target is a Code object
|
kCallJSFunction, // target is a JSFunction object
|
kCallAddress, // target is a machine pointer
|
kCallWasmFunction // target is a wasm function
|
};
|
|
enum Flag {
|
kNoFlags = 0u,
|
kNeedsFrameState = 1u << 0,
|
kHasExceptionHandler = 1u << 1,
|
kCanUseRoots = 1u << 2,
|
// Causes the code generator to initialize the root register.
|
kInitializeRootRegister = 1u << 3,
|
// Does not ever try to allocate space on our heap.
|
kNoAllocate = 1u << 4,
|
// Push argument count as part of function prologue.
|
kPushArgumentCount = 1u << 5,
|
// Use retpoline for this call if indirect.
|
kRetpoline = 1u << 6,
|
// Use the kJavaScriptCallCodeStartRegister (fixed) register for the
|
// indirect target address when calling.
|
kFixedTargetRegister = 1u << 7
|
};
|
typedef base::Flags<Flag> Flags;
|
|
CallDescriptor(Kind kind, MachineType target_type, LinkageLocation target_loc,
|
LocationSignature* location_sig, size_t stack_param_count,
|
Operator::Properties properties,
|
RegList callee_saved_registers,
|
RegList callee_saved_fp_registers, Flags flags,
|
const char* debug_name = "",
|
const RegList allocatable_registers = 0,
|
size_t stack_return_count = 0)
|
: kind_(kind),
|
target_type_(target_type),
|
target_loc_(target_loc),
|
location_sig_(location_sig),
|
stack_param_count_(stack_param_count),
|
stack_return_count_(stack_return_count),
|
properties_(properties),
|
callee_saved_registers_(callee_saved_registers),
|
callee_saved_fp_registers_(callee_saved_fp_registers),
|
allocatable_registers_(allocatable_registers),
|
flags_(flags),
|
debug_name_(debug_name) {}
|
|
// Returns the kind of this call.
|
Kind kind() const { return kind_; }
|
|
// Returns {true} if this descriptor is a call to a C function.
|
bool IsCFunctionCall() const { return kind_ == kCallAddress; }
|
|
// Returns {true} if this descriptor is a call to a JSFunction.
|
bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; }
|
|
// Returns {true} if this descriptor is a call to a WebAssembly function.
|
bool IsWasmFunctionCall() const { return kind_ == kCallWasmFunction; }
|
|
bool RequiresFrameAsIncoming() const {
|
return IsCFunctionCall() || IsJSFunctionCall() || IsWasmFunctionCall();
|
}
|
|
// The number of return values from this call.
|
size_t ReturnCount() const { return location_sig_->return_count(); }
|
|
// The number of C parameters to this call.
|
size_t ParameterCount() const { return location_sig_->parameter_count(); }
|
|
// The number of stack parameters to the call.
|
size_t StackParameterCount() const { return stack_param_count_; }
|
|
// The number of stack return values from the call.
|
size_t StackReturnCount() const { return stack_return_count_; }
|
|
// The number of parameters to the JS function call.
|
size_t JSParameterCount() const {
|
DCHECK(IsJSFunctionCall());
|
return stack_param_count_;
|
}
|
|
// The total number of inputs to this call, which includes the target,
|
// receiver, context, etc.
|
// TODO(titzer): this should input the framestate input too.
|
size_t InputCount() const { return 1 + location_sig_->parameter_count(); }
|
|
size_t FrameStateCount() const { return NeedsFrameState() ? 1 : 0; }
|
|
Flags flags() const { return flags_; }
|
|
bool NeedsFrameState() const { return flags() & kNeedsFrameState; }
|
bool PushArgumentCount() const { return flags() & kPushArgumentCount; }
|
bool InitializeRootRegister() const {
|
return flags() & kInitializeRootRegister;
|
}
|
|
LinkageLocation GetReturnLocation(size_t index) const {
|
return location_sig_->GetReturn(index);
|
}
|
|
LinkageLocation GetInputLocation(size_t index) const {
|
if (index == 0) return target_loc_;
|
return location_sig_->GetParam(index - 1);
|
}
|
|
MachineSignature* GetMachineSignature(Zone* zone) const;
|
|
MachineType GetReturnType(size_t index) const {
|
return location_sig_->GetReturn(index).GetType();
|
}
|
|
MachineType GetInputType(size_t index) const {
|
if (index == 0) return target_type_;
|
return location_sig_->GetParam(index - 1).GetType();
|
}
|
|
MachineType GetParameterType(size_t index) const {
|
return location_sig_->GetParam(index).GetType();
|
}
|
|
// Operator properties describe how this call can be optimized, if at all.
|
Operator::Properties properties() const { return properties_; }
|
|
// Get the callee-saved registers, if any, across this call.
|
RegList CalleeSavedRegisters() const { return callee_saved_registers_; }
|
|
// Get the callee-saved FP registers, if any, across this call.
|
RegList CalleeSavedFPRegisters() const { return callee_saved_fp_registers_; }
|
|
const char* debug_name() const { return debug_name_; }
|
|
bool UsesOnlyRegisters() const;
|
|
bool HasSameReturnLocationsAs(const CallDescriptor* other) const;
|
|
// Returns the first stack slot that is not used by the stack parameters.
|
int GetFirstUnusedStackSlot() const;
|
|
int GetStackParameterDelta(const CallDescriptor* tail_caller) const;
|
|
bool CanTailCall(const Node* call) const;
|
|
int CalculateFixedFrameSize() const;
|
|
RegList AllocatableRegisters() const { return allocatable_registers_; }
|
|
bool HasRestrictedAllocatableRegisters() const {
|
return allocatable_registers_ != 0;
|
}
|
|
void set_save_fp_mode(SaveFPRegsMode mode) { save_fp_mode_ = mode; }
|
|
SaveFPRegsMode get_save_fp_mode() const { return save_fp_mode_; }
|
|
private:
|
friend class Linkage;
|
SaveFPRegsMode save_fp_mode_ = kSaveFPRegs;
|
|
const Kind kind_;
|
const MachineType target_type_;
|
const LinkageLocation target_loc_;
|
const LocationSignature* const location_sig_;
|
const size_t stack_param_count_;
|
const size_t stack_return_count_;
|
const Operator::Properties properties_;
|
const RegList callee_saved_registers_;
|
const RegList callee_saved_fp_registers_;
|
// Non-zero value means restricting the set of allocatable registers for
|
// register allocator to use.
|
const RegList allocatable_registers_;
|
const Flags flags_;
|
const char* const debug_name_;
|
|
DISALLOW_COPY_AND_ASSIGN(CallDescriptor);
|
};
|
|
DEFINE_OPERATORS_FOR_FLAGS(CallDescriptor::Flags)
|
|
std::ostream& operator<<(std::ostream& os, const CallDescriptor& d);
|
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
|
const CallDescriptor::Kind& k);
|
|
// Defines the linkage for a compilation, including the calling conventions
|
// for incoming parameters and return value(s) as well as the outgoing calling
|
// convention for any kind of call. Linkage is generally architecture-specific.
|
//
|
// Can be used to translate {arg_index} (i.e. index of the call node input) as
|
// well as {param_index} (i.e. as stored in parameter nodes) into an operator
|
// representing the architecture-specific location. The following call node
|
// layouts are supported (where {n} is the number of value inputs):
|
//
|
// #0 #1 #2 [...] #n
|
// Call[CodeStub] code, arg 1, arg 2, [...], context
|
// Call[JSFunction] function, rcvr, arg 1, [...], new, #arg, context
|
// Call[Runtime] CEntry, arg 1, arg 2, [...], fun, #arg, context
|
// Call[BytecodeDispatch] address, arg 1, arg 2, [...]
|
class V8_EXPORT_PRIVATE Linkage : public NON_EXPORTED_BASE(ZoneObject) {
|
public:
|
explicit Linkage(CallDescriptor* incoming) : incoming_(incoming) {}
|
|
static CallDescriptor* ComputeIncoming(Zone* zone,
|
OptimizedCompilationInfo* info);
|
|
// The call descriptor for this compilation unit describes the locations
|
// of incoming parameters and the outgoing return value(s).
|
CallDescriptor* GetIncomingDescriptor() const { return incoming_; }
|
static CallDescriptor* GetJSCallDescriptor(Zone* zone, bool is_osr,
|
int parameter_count,
|
CallDescriptor::Flags flags);
|
|
static CallDescriptor* GetRuntimeCallDescriptor(
|
Zone* zone, Runtime::FunctionId function, int js_parameter_count,
|
Operator::Properties properties, CallDescriptor::Flags flags);
|
|
static CallDescriptor* GetCEntryStubCallDescriptor(
|
Zone* zone, int return_count, int js_parameter_count,
|
const char* debug_name, Operator::Properties properties,
|
CallDescriptor::Flags flags);
|
|
static CallDescriptor* GetStubCallDescriptor(
|
Zone* zone, const CallInterfaceDescriptor& descriptor,
|
int stack_parameter_count, CallDescriptor::Flags flags,
|
Operator::Properties properties = Operator::kNoProperties,
|
StubCallMode stub_mode = StubCallMode::kCallOnHeapBuiltin);
|
|
static CallDescriptor* GetBytecodeDispatchCallDescriptor(
|
Zone* zone, const CallInterfaceDescriptor& descriptor,
|
int stack_parameter_count);
|
|
// Creates a call descriptor for simplified C calls that is appropriate
|
// for the host platform. This simplified calling convention only supports
|
// integers and pointers of one word size each, i.e. no floating point,
|
// structs, pointers to members, etc.
|
static CallDescriptor* GetSimplifiedCDescriptor(
|
Zone* zone, const MachineSignature* sig,
|
bool set_initialize_root_flag = false);
|
|
// Get the location of an (incoming) parameter to this function.
|
LinkageLocation GetParameterLocation(int index) const {
|
return incoming_->GetInputLocation(index + 1); // + 1 to skip target.
|
}
|
|
// Get the machine type of an (incoming) parameter to this function.
|
MachineType GetParameterType(int index) const {
|
return incoming_->GetInputType(index + 1); // + 1 to skip target.
|
}
|
|
// Get the location where this function should place its return value.
|
LinkageLocation GetReturnLocation(size_t index = 0) const {
|
return incoming_->GetReturnLocation(index);
|
}
|
|
// Get the machine type of this function's return value.
|
MachineType GetReturnType(size_t index = 0) const {
|
return incoming_->GetReturnType(index);
|
}
|
|
bool ParameterHasSecondaryLocation(int index) const;
|
LinkageLocation GetParameterSecondaryLocation(int index) const;
|
|
static bool NeedsFrameStateInput(Runtime::FunctionId function);
|
|
// Get the location where an incoming OSR value is stored.
|
LinkageLocation GetOsrValueLocation(int index) const;
|
|
// A special {Parameter} index for Stub Calls that represents context.
|
static int GetStubCallContextParamIndex(int parameter_count) {
|
return parameter_count + 0; // Parameter (arity + 0) is special.
|
}
|
|
// A special {Parameter} index for JSCalls that represents the new target.
|
static int GetJSCallNewTargetParamIndex(int parameter_count) {
|
return parameter_count + 0; // Parameter (arity + 0) is special.
|
}
|
|
// A special {Parameter} index for JSCalls that represents the argument count.
|
static int GetJSCallArgCountParamIndex(int parameter_count) {
|
return parameter_count + 1; // Parameter (arity + 1) is special.
|
}
|
|
// A special {Parameter} index for JSCalls that represents the context.
|
static int GetJSCallContextParamIndex(int parameter_count) {
|
return parameter_count + 2; // Parameter (arity + 2) is special.
|
}
|
|
// A special {Parameter} index for JSCalls that represents the closure.
|
static const int kJSCallClosureParamIndex = -1;
|
|
// A special {OsrValue} index to indicate the context spill slot.
|
static const int kOsrContextSpillSlotIndex = -1;
|
|
// A special {OsrValue} index to indicate the accumulator register.
|
static const int kOsrAccumulatorRegisterIndex = -1;
|
|
private:
|
CallDescriptor* const incoming_;
|
|
DISALLOW_COPY_AND_ASSIGN(Linkage);
|
};
|
|
} // namespace compiler
|
} // namespace internal
|
} // namespace v8
|
|
#endif // V8_COMPILER_LINKAGE_H_
|