blob: ad68a579575d3e9d2977f0e60ae39fac808ce68e [file] [log] [blame]
// 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/codegen/interface-descriptors.h"
#include "src/codegen/machine-type.h"
#include "src/codegen/register-arch.h"
#include "src/codegen/reglist.h"
#include "src/codegen/signature.h"
#include "src/common/globals.h"
#include "src/compiler/frame.h"
#include "src/compiler/operator.h"
#include "src/runtime/runtime.h"
#include "src/zone/zone.h"
#if !defined(__clang__) && defined(_M_ARM64)
// _M_ARM64 is an MSVC-specific macro that clang-cl emulates.
#define NO_INLINE_FOR_ARM64_MSVC __declspec(noinline)
#else
#define NO_INLINE_FOR_ARM64_MSVC
#endif
namespace v8 {
class CFunctionInfo;
namespace internal {
class CallInterfaceDescriptor;
class OptimizedCompilationInfo;
namespace compiler {
const RegList kNoCalleeSaved = 0;
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_ &&
machine_type_ == other.machine_type_;
}
bool operator!=(const LinkageLocation& other) const {
return !(*this == other);
}
static bool IsSameLocation(const LinkageLocation& a,
const LinkageLocation& b) {
// Different MachineTypes may end up at the same physical location. With the
// sub-type check we make sure that types like {AnyTagged} and
// {TaggedPointer} which would end up with the same physical location are
// considered equal here.
return (a.bit_field_ == b.bit_field_) &&
(IsSubtype(a.machine_type_.representation(),
b.machine_type_.representation()) ||
IsSubtype(b.machine_type_.representation(),
a.machine_type_.representation()));
}
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) /
kSystemPointerSize,
MachineType::Pointer());
}
static LinkageLocation ForSavedCallerFramePtr() {
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
StandardFrameConstants::kCallerFPOffset) /
kSystemPointerSize,
MachineType::Pointer());
}
static LinkageLocation ForSavedCallerConstantPool() {
DCHECK(V8_EMBEDDED_CONSTANT_POOL);
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
StandardFrameConstants::kConstantPoolOffset) /
kSystemPointerSize,
MachineType::AnyTagged());
}
static LinkageLocation ForSavedCallerFunction() {
return ForCalleeFrameSlot((StandardFrameConstants::kCallerPCOffset -
StandardFrameConstants::kFunctionOffset) /
kSystemPointerSize,
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() + kSystemPointerSize - 1) / kSystemPointerSize;
}
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;
}
NO_INLINE_FOR_ARM64_MSVC 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 };
using TypeField = base::BitField<LocationType, 0, 1>;
using LocationField = TypeField::Next<int32_t, 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} can be -1 (ANY_REGISTER).
((static_cast<uint32_t>(location) << LocationField::kShift) &
LocationField::kMask);
machine_type_ = machine_type;
}
int32_t bit_field_;
MachineType machine_type_;
};
using LocationSignature = Signature<LinkageLocation>;
// 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
kCallWasmCapiFunction, // target is a Wasm C API function
kCallWasmFunction, // target is a wasm function
kCallWasmImportWrapper, // target is a wasm import wrapper
kCallBuiltinPointer, // target is a builtin pointer
};
// NOTE: The lowest 10 bits of the Flags field are encoded in InstructionCode
// (for use in the code generator). All higher bits are lost.
static constexpr int kFlagsBitsEncodedInInstructionCode = 10;
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,
// Use retpoline for this call if indirect.
kRetpoline = 1u << 5,
// Use the kJavaScriptCallCodeStartRegister (fixed) register for the
// indirect target address when calling.
kFixedTargetRegister = 1u << 6,
kCallerSavedRegisters = 1u << 7,
// The kCallerSavedFPRegisters only matters (and set) when the more general
// flag for kCallerSavedRegisters above is also set.
kCallerSavedFPRegisters = 1u << 8,
// Tail calls for tier up are special (in fact they are different enough
// from normal tail calls to warrant a dedicated opcode; but they also have
// enough similar aspects that reusing the TailCall opcode is pragmatic).
// Specifically:
//
// 1. Caller and callee are both JS-linkage Code objects.
// 2. JS runtime arguments are passed unchanged from caller to callee.
// 3. JS runtime arguments are not attached as inputs to the TailCall node.
// 4. Prior to the tail call, frame and register state is torn down to just
// before the caller frame was constructed.
// 5. Unlike normal tail calls, arguments adaptor frames (if present) are
// *not* torn down.
//
// In other words, behavior is identical to a jmp instruction prior caller
// frame construction.
kIsTailCallForTierUp = 1u << 9,
// Flags past here are *not* encoded in InstructionCode and are thus not
// accessible from the code generator. See also
// kFlagsBitsEncodedInInstructionCode.
// AIX has a function descriptor by default but it can be disabled for a
// certain CFunction call (only used for Kind::kCallAddress).
kNoFunctionDescriptor = 1u << 10,
};
using Flags = base::Flags<Flag>;
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 = "",
StackArgumentOrder stack_order = StackArgumentOrder::kDefault,
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),
stack_order_(stack_order),
debug_name_(debug_name) {}
CallDescriptor(const CallDescriptor&) = delete;
CallDescriptor& operator=(const CallDescriptor&) = delete;
// 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; }
// Returns {true} if this descriptor is a call to a WebAssembly function.
bool IsWasmImportWrapper() const { return kind_ == kCallWasmImportWrapper; }
// Returns {true} if this descriptor is a call to a Wasm C API function.
bool IsWasmCapiFunction() const { return kind_ == kCallWasmCapiFunction; }
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_;
}
int GetStackIndexFromSlot(int slot_index) const {
switch (GetStackArgumentOrder()) {
case StackArgumentOrder::kDefault:
return -slot_index - 1;
case StackArgumentOrder::kJS:
return slot_index + static_cast<int>(StackParameterCount());
}
}
// 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 InitializeRootRegister() const {
return flags() & kInitializeRootRegister;
}
bool NeedsCallerSavedRegisters() const {
return flags() & kCallerSavedRegisters;
}
bool NeedsCallerSavedFPRegisters() const {
return flags() & kCallerSavedFPRegisters;
}
bool IsTailCallForTierUp() const { return flags() & kIsTailCallForTierUp; }
bool NoFunctionDescriptor() const { return flags() & kNoFunctionDescriptor; }
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();
}
StackArgumentOrder GetStackArgumentOrder() const { return stack_order_; }
// 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;
// Returns the first stack slot that is not used by the stack parameters.
int GetFirstUnusedStackSlot() const;
int GetStackParameterDelta(const CallDescriptor* tail_caller) const;
int GetTaggedParameterSlots() const;
bool CanTailCall(const CallDescriptor* callee) const;
int CalculateFixedFrameSize(CodeKind code_kind) const;
RegList AllocatableRegisters() const { return allocatable_registers_; }
bool HasRestrictedAllocatableRegisters() const {
return allocatable_registers_ != 0;
}
// Stores the signature information for a fast API call - C++ functions
// that can be called directly from TurboFan.
void SetCFunctionInfo(const CFunctionInfo* c_function_info) {
c_function_info_ = c_function_info;
}
const CFunctionInfo* GetCFunctionInfo() const { return c_function_info_; }
private:
friend class Linkage;
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 StackArgumentOrder stack_order_;
const char* const debug_name_;
const CFunctionInfo* c_function_info_ = nullptr;
};
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) {}
Linkage(const Linkage&) = delete;
Linkage& operator=(const Linkage&) = delete;
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,
StackArgumentOrder stack_order = StackArgumentOrder::kDefault);
static CallDescriptor* GetStubCallDescriptor(
Zone* zone, const CallInterfaceDescriptor& descriptor,
int stack_parameter_count, CallDescriptor::Flags flags,
Operator::Properties properties = Operator::kNoProperties,
StubCallMode stub_mode = StubCallMode::kCallCodeObject);
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,
CallDescriptor::Flags flags = CallDescriptor::kNoFlags);
// 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 constexpr 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 constexpr 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 constexpr 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 constexpr 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_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#undef NO_INLINE_FOR_ARM64_MSVC
#endif // V8_COMPILER_LINKAGE_H_