#ifndef V8_COMPILER_LINKAGE_H_
#define V8_COMPILER_LINKAGE_H_
#include <optional>
#include "src/base/compiler-specific.h"
#include "src/base/flags.h"
#include "src/codegen/interface-descriptors.h"
#include "src/codegen/linkage-location.h"
#include "src/codegen/machine-type.h"
#include "src/codegen/register.h"
#include "src/codegen/reglist.h"
#include "src/codegen/signature.h"
#include "src/common/globals.h"
#include "src/compiler/frame.h"
#include "src/compiler/globals.h"
#include "src/compiler/operator.h"
#include "src/execution/encoded-c-signature.h"
#include "src/runtime/runtime.h"
#include "src/zone/zone.h"
namespace v8 {
class CFunctionInfo;
namespace internal {
class CallInterfaceDescriptor;
class OptimizedCompilationInfo;
namespace compiler {
constexpr RegList kNoCalleeSaved;
constexpr DoubleRegList kNoCalleeSavedFp;
class OsrHelper;
class V8_EXPORT_PRIVATE CallDescriptor final
: public NON_EXPORTED_BASE(ZoneObject) {
public:
enum Kind {
kCallCodeObject,
kCallJSFunction,
kCallAddress,
#if V8_ENABLE_WEBASSEMBLY
kCallWasmCapiFunction,
kCallWasmFunction,
kCallWasmFunctionIndirect,
kCallWasmImportWrapper,
kResumeWasmContinuation,
#endif
kCallBuiltinPointer,
};
static constexpr int kFlagsBitsEncodedInInstructionCode = 10;
enum Flag {
kNoFlags = 0u,
kNeedsFrameState = 1u << 0,
kHasExceptionHandler = 1u << 1,
kCanUseRoots = 1u << 2,
kInitializeRootRegister = 1u << 3,
kNoAllocate = 1u << 4,
kHasEffectHandler = kNoAllocate,
kFixedTargetRegister = 1u << 5,
kCallerSavedRegisters = 1u << 6,
kCallerSavedFPRegisters = 1u << 7,
kIsTailCallForTierUp = 1u << 8,
kNoFunctionDescriptor = 1u << 9,
};
using Flags = base::Flags<Flag>;
CallDescriptor(Kind kind, CodeEntrypointTag tag, MachineType target_type,
LinkageLocation target_loc, LocationSignature* location_sig,
size_t param_slot_count, Operator::Properties properties,
RegList callee_saved_registers,
DoubleRegList callee_saved_fp_registers, Flags flags,
const char* debug_name = "",
StackArgumentOrder stack_order = StackArgumentOrder::kDefault,
const RegList allocatable_registers = {},
size_t return_slot_count = 0,
uint64_t signature_hash = kInvalidWasmSignatureHash)
: kind_(kind),
tag_(tag),
target_type_(target_type),
target_loc_(target_loc),
location_sig_(location_sig),
param_slot_count_(param_slot_count),
return_slot_count_(return_slot_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),
signature_hash_(signature_hash) {
#ifdef V8_ENABLE_WEBASSEMBLY
if (kind == Kind::kCallWasmFunctionIndirect) {
CHECK_NE(signature_hash, kInvalidWasmSignatureHash);
}
#endif
}
CallDescriptor(const CallDescriptor&) = delete;
CallDescriptor& operator=(const CallDescriptor&) = delete;
Kind kind() const { return kind_; }
CodeEntrypointTag tag() const { return tag_; }
uint64_t signature_hash() const;
uint32_t shifted_tag() const {
static_assert(kCodeEntrypointTagShift >= 32);
return tag_ >> kCodeEntrypointTagShift;
}
bool IsCodeObjectCall() const { return kind_ == kCallCodeObject; }
bool IsCFunctionCall() const { return kind_ == kCallAddress; }
bool IsJSFunctionCall() const { return kind_ == kCallJSFunction; }
#if V8_ENABLE_WEBASSEMBLY
bool IsDirectWasmFunctionCall() const { return kind_ == kCallWasmFunction; }
bool IsIndirectWasmFunctionCall() const {
return kind_ == kCallWasmFunctionIndirect;
}
bool IsAnyWasmFunctionCall() const {
return IsDirectWasmFunctionCall() || IsIndirectWasmFunctionCall();
}
bool IsWasmImportWrapper() const { return kind_ == kCallWasmImportWrapper; }
bool IsWasmCapiFunction() const { return kind_ == kCallWasmCapiFunction; }
#endif
bool IsBuiltinPointerCall() const { return kind_ == kCallBuiltinPointer; }
bool RequiresFrameAsIncoming() const {
if (IsCFunctionCall() || IsJSFunctionCall()) return true;
#if V8_ENABLE_WEBASSEMBLY
if (IsAnyWasmFunctionCall()) return true;
#endif
if (CalleeSavedRegisters() != kNoCalleeSaved) return true;
return false;
}
bool RequiresEntrypointTagForCall() const { return IsCodeObjectCall(); }
size_t ReturnCount() const { return location_sig_->return_count(); }
size_t ParameterCount() const { return location_sig_->parameter_count(); }
size_t GPParameterCount() const {
if (!gp_param_count_) {
ComputeParamCounts();
}
return gp_param_count_.value();
}
size_t FPParameterCount() const {
if (!fp_param_count_) {
ComputeParamCounts();
}
return fp_param_count_.value();
}
size_t ParameterSlotCount() const { return param_slot_count_; }
size_t ReturnSlotCount() const { return return_slot_count_; }
size_t JSParameterCount() const {
DCHECK(IsJSFunctionCall());
return param_slot_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>(ParameterSlotCount());
}
}
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 properties() const { return properties_; }
RegList CalleeSavedRegisters() const { return callee_saved_registers_; }
DoubleRegList CalleeSavedFPRegisters() const {
return callee_saved_fp_registers_;
}
const char* debug_name() const { return debug_name_; }
int GetStackParameterDelta(const CallDescriptor* tail_caller) const;
int GetOffsetToFirstUnusedStackSlot() const;
int GetOffsetToReturns() const;
uint32_t 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_.is_empty();
}
EncodedCSignature ToEncodedCSignature() const;
#ifdef V8_USE_LLVM_BACKEND
bool hasContext(size_t& context_idx) const;
void printDescInfo() const;
#endif
std::optional<Runtime::FunctionId> runtime_function_id() const {
return runtime_function_id_;
}
private:
void ComputeParamCounts() const;
friend class Linkage;
const Kind kind_;
const CodeEntrypointTag tag_;
const MachineType target_type_;
const LinkageLocation target_loc_;
const LocationSignature* const location_sig_;
const size_t param_slot_count_;
const size_t return_slot_count_;
const Operator::Properties properties_;
const RegList callee_saved_registers_;
const DoubleRegList callee_saved_fp_registers_;
const RegList allocatable_registers_;
const Flags flags_;
const StackArgumentOrder stack_order_;
const char* const debug_name_;
uint64_t signature_hash_;
std::optional<Runtime::FunctionId> runtime_function_id_;
mutable std::optional<size_t> gp_param_count_;
mutable std::optional<size_t> fp_param_count_;
};
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);
#if V8_ENABLE_WEBASSEMBLY
V8_EXPORT_PRIVATE CallDescriptor* GetI32WasmCallDescriptor(
Zone* zone, const CallDescriptor* call_descriptor);
#endif
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);
CallDescriptor* GetIncomingDescriptor() const { return incoming_; }
static CallDescriptor* GetJSCallDescriptor(
Zone* zone, bool is_osr, int parameter_count, CallDescriptor::Flags flags,
Operator::Properties properties =
Operator::kNoProperties );
static CallDescriptor* GetRuntimeCallDescriptor(
Zone* zone, Runtime::FunctionId function, int parameter_count,
Operator::Properties properties, CallDescriptor::Flags flags,
LazyDeoptOnThrow lazy_deopt_on_throw = LazyDeoptOnThrow::kNo);
static CallDescriptor* GetCPPBuiltinCallDescriptor(
Zone* zone, 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::kCallCodeObject);
static CallDescriptor* GetBytecodeDispatchCallDescriptor(
Zone* zone, const CallInterfaceDescriptor& descriptor,
int stack_parameter_count);
static CallDescriptor* GetSimplifiedCDescriptor(
Zone* zone, const MachineSignature* sig,
CallDescriptor::Flags flags = CallDescriptor::kNoFlags,
Operator::Properties properties = Operator::kNoThrow);
LinkageLocation GetParameterLocation(int index) const {
return incoming_->GetInputLocation(index + 1);
}
MachineType GetParameterType(int index) const {
return incoming_->GetInputType(index + 1);
}
LinkageLocation GetReturnLocation(size_t index = 0) const {
return incoming_->GetReturnLocation(index);
}
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);
LinkageLocation GetOsrValueLocation(int index) const;
static int GetStubCallContextParamIndex(int parameter_count) {
return parameter_count + 0;
}
static constexpr int GetJSCallNewTargetParamIndex(int parameter_count) {
return parameter_count + 0;
}
static constexpr int GetJSCallArgCountParamIndex(int parameter_count) {
return GetJSCallNewTargetParamIndex(parameter_count) + 1;
}
#ifdef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE
static constexpr int GetJSCallDispatchHandleParamIndex(int parameter_count) {
return GetJSCallArgCountParamIndex(parameter_count) + 1;
}
#endif
static constexpr int GetJSCallContextParamIndex(int parameter_count) {
#ifdef V8_JS_LINKAGE_INCLUDES_DISPATCH_HANDLE
return GetJSCallDispatchHandleParamIndex(parameter_count) + 1;
#else
return GetJSCallArgCountParamIndex(parameter_count) + 1;
#endif
}
static constexpr int kJSCallClosureParamIndex = kJSCallClosureParameterIndex;
static_assert(kJSCallClosureParamIndex == -1);
static const int kOsrContextSpillSlotIndex = -1;
static const int kOsrAccumulatorRegisterIndex = -1;
private:
static CallDescriptor* GetCEntryStubCallDescriptor(
Zone* zone, int return_count, int stack_parameter_count,
const char* debug_name, Operator::Properties properties,
CallDescriptor::Flags flags, StackArgumentOrder stack_order,
CodeEntrypointTag entrypoint_tag);
CallDescriptor* const incoming_;
};
}
}
}
#undef NO_INLINE_FOR_ARM64_MSVC
#endif