Commit b9ddcbc8 authored by Maya Lekova's avatar Maya Lekova Committed by V8 LUCI CQ

[fastcall] Enable float support on arm64 simulator

This CL adds support for handling calls to C functions with arbitrary
signatures on the arm64 simulator. It adds infrastructure for
encoding the signature data from CallDescriptor and FunctionInfo
classes into a compact representation, stored in the simulator and
called EncodedCSignature.

Design doc:
https://docs.google.com/document/d/1ZxOF3GSyNmtU0C0YJvrsydPJj35W_tTJZymeXwfDxoI/edit

This CL is a follow up on the native support added in
https://chromium-review.googlesource.com/c/v8/v8/+/3182232
and is partially based on the previous attempt:
https://chromium-review.googlesource.com/c/v8/v8/+/2343072

Bug: chromium:1052746
Change-Id: I0991b47bd644b2fc2244c5eb923b085261f04765
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3060486
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77744}
parent c2b48193
......@@ -2872,6 +2872,7 @@ v8_header_set("v8_internal_headers") {
"src/diagnostics/unwinder.h",
"src/execution/arguments-inl.h",
"src/execution/arguments.h",
"src/execution/encoded-c-signature.h",
"src/execution/execution.h",
"src/execution/frame-constants.h",
"src/execution/frames-inl.h",
......@@ -4086,6 +4087,7 @@ v8_source_set("v8_base_without_compiler") {
"src/diagnostics/perf-jit.cc",
"src/diagnostics/unwinder.cc",
"src/execution/arguments.cc",
"src/execution/encoded-c-signature.cc",
"src/execution/execution.cc",
"src/execution/frames.cc",
"src/execution/futex-emulation.cc",
......
......@@ -249,6 +249,15 @@ class CTypeInfo {
kV8Value,
kApiObject, // This will be deprecated once all users have
// migrated from v8::ApiObject to v8::Local<v8::Value>.
kAny, // This is added to enable untyped representation of fast
// call arguments for test purposes. It can represent any of
// the other types stored in the same memory as a union (see
// the AnyCType struct declared below). This allows for
// uniform passing of arguments w.r.t. their location
// (in a register or on the stack), independent of their
// actual type. It's currently used by the arm64 simulator
// and can be added to the other simulators as well when fast
// calls having both GP and FP params need to be supported.
};
// kCallbackOptionsType is not part of the Type enum
......@@ -404,6 +413,37 @@ class V8_EXPORT CFunctionInfo {
const CTypeInfo* arg_info_;
};
struct FastApiCallbackOptions;
// Provided for testing.
struct AnyCType {
AnyCType() : int64_value(0) {}
union {
bool bool_value;
int32_t int32_value;
uint32_t uint32_value;
int64_t int64_value;
uint64_t uint64_value;
float float_value;
double double_value;
Local<Object> object_value;
Local<Array> sequence_value;
const FastApiTypedArray<int32_t>* int32_ta_value;
const FastApiTypedArray<uint32_t>* uint32_ta_value;
const FastApiTypedArray<int64_t>* int64_ta_value;
const FastApiTypedArray<uint64_t>* uint64_ta_value;
const FastApiTypedArray<float>* float_ta_value;
const FastApiTypedArray<double>* double_ta_value;
FastApiCallbackOptions* options_value;
};
};
static_assert(
sizeof(AnyCType) == 8,
"The AnyCType struct should have size == 64 bits, as this is assumed "
"by EffectControlLinearizer.");
class V8_EXPORT CFunction {
public:
constexpr CFunction() : address_(nullptr), type_info_(nullptr) {}
......@@ -460,6 +500,19 @@ class V8_EXPORT CFunction {
return ArgUnwrap<F*>::Make(func);
}
// Provided for testing purposes.
template <typename R, typename... Args, typename R_Patch,
typename... Args_Patch>
static CFunction Make(R (*func)(Args...),
R_Patch (*patching_func)(Args_Patch...)) {
CFunction c_func = ArgUnwrap<R (*)(Args...)>::Make(func);
static_assert(
sizeof...(Args_Patch) == sizeof...(Args),
"The patching function must have the same number of arguments.");
c_func.address_ = reinterpret_cast<void*>(patching_func);
return c_func;
}
CFunction(const void* address, const CFunctionInfo* type_info);
private:
......@@ -555,7 +608,8 @@ class CFunctionInfoImpl : public CFunctionInfo {
kReturnType == CTypeInfo::Type::kInt32 ||
kReturnType == CTypeInfo::Type::kUint32 ||
kReturnType == CTypeInfo::Type::kFloat32 ||
kReturnType == CTypeInfo::Type::kFloat64,
kReturnType == CTypeInfo::Type::kFloat64 ||
kReturnType == CTypeInfo::Type::kAny,
"64-bit int and api object values are not currently "
"supported return types.");
}
......@@ -606,7 +660,8 @@ struct CTypeInfoTraits {};
V(void, kVoid) \
V(v8::Local<v8::Value>, kV8Value) \
V(v8::Local<v8::Object>, kV8Value) \
V(ApiObject, kApiObject)
V(ApiObject, kApiObject) \
V(AnyCType, kAny)
// ApiObject was a temporary solution to wrap the pointer to the v8::Value.
// Please use v8::Local<v8::Value> in new code for the arguments and
......
......@@ -4,6 +4,7 @@
#include "src/codegen/external-reference.h"
#include "include/v8-fast-api-calls.h"
#include "src/api/api.h"
#include "src/base/ieee754.h"
#include "src/codegen/cpu-features.h"
......@@ -11,10 +12,11 @@
#include "src/date/date.h"
#include "src/debug/debug.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/encoded-c-signature.h"
#include "src/execution/isolate-utils.h"
#include "src/execution/isolate.h"
#include "src/execution/microtask-queue.h"
#include "src/execution/simulator-base.h"
#include "src/execution/simulator.h"
#include "src/heap/heap-inl.h"
#include "src/heap/heap.h"
#include "src/ic/stub-cache.h"
......@@ -172,9 +174,21 @@ static ExternalReference::Type BuiltinCallTypeForResultSize(int result_size) {
UNREACHABLE();
}
// static
ExternalReference ExternalReference::Create(ApiFunction* fun, Type type) {
return ExternalReference(Redirect(fun->address(), type));
}
// static
ExternalReference ExternalReference::Create(
ApiFunction* fun, Type type = ExternalReference::BUILTIN_CALL) {
Isolate* isolate, ApiFunction* fun, Type type, Address* c_functions,
const CFunctionInfo* const* c_signatures, unsigned num_functions) {
#ifdef USE_SIMULATOR_WITH_GENERIC_C_CALLS
isolate->CurrentPerIsolateThreadData()
->simulator()
->RegisterFunctionsAndSignatures(c_functions, c_signatures,
num_functions);
#endif // USE_SIMULATOR_WITH_GENERIC_C_CALLS
return ExternalReference(Redirect(fun->address(), type));
}
......
......@@ -11,6 +11,7 @@
namespace v8 {
class ApiFunction;
class CFunctionInfo;
namespace internal {
......@@ -406,6 +407,15 @@ class ExternalReference {
static ExternalReference Create(StatsCounter* counter);
static V8_EXPORT_PRIVATE ExternalReference Create(ApiFunction* ptr,
Type type);
// The following version is used by JSCallReducer in the compiler
// to create a reference for a fast API call, with one or more
// overloads. In simulator builds, it additionally "registers"
// the overloads with the simulator to ensure it maintains a
// mapping of callable Address'es to a function signature, encoding
// GP and FP arguments.
static V8_EXPORT_PRIVATE ExternalReference
Create(Isolate* isolate, ApiFunction* ptr, Type type, Address* c_functions,
const CFunctionInfo* const* c_signatures, unsigned num_functions);
static ExternalReference Create(const Runtime::Function* f);
static ExternalReference Create(IsolateAddressId id, Isolate* isolate);
static ExternalReference Create(Runtime::FunctionId id);
......
......@@ -90,6 +90,13 @@ constexpr int64_t TB = static_cast<int64_t>(GB) * 1024;
#define V8_DEFAULT_STACK_SIZE_KB 984
#endif
#if defined(USE_SIMULATOR) && defined(V8_TARGET_ARCH_ARM64)
#define USE_SIMULATOR_WITH_GENERIC_C_CALLS
#define IF_USE_SIMULATOR(V) , V
#else
#define IF_USE_SIMULATOR(V)
#endif
// Minimum stack size in KB required by compilers.
constexpr int kStackSpaceRequiredForCompilation = 40;
......
......@@ -4985,6 +4985,10 @@ MachineType MachineTypeFor(CTypeInfo::Type type) {
return MachineType::Uint32();
case CTypeInfo::Type::kInt64:
return MachineType::Int64();
case CTypeInfo::Type::kAny:
static_assert(sizeof(AnyCType) == 8,
"CTypeInfo::Type::kAny is assumed to be of size 64 bits.");
return MachineType::Int64();
case CTypeInfo::Type::kUint64:
return MachineType::Uint64();
case CTypeInfo::Type::kFloat32:
......@@ -5329,7 +5333,7 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
StoreRepresentation(MachineRepresentation::kWord32, kNoWriteBarrier),
stack_slot,
static_cast<int>(offsetof(v8::FastApiCallbackOptions, fallback)),
__ ZeroConstant());
__ Int32Constant(0));
__ Store(StoreRepresentation(MachineType::PointerRepresentation(),
kNoWriteBarrier),
stack_slot,
......@@ -5466,6 +5470,11 @@ Node* EffectControlLinearizer::LowerFastApiCall(Node* node) {
case CTypeInfo::Type::kV8Value:
case CTypeInfo::Type::kApiObject:
UNREACHABLE();
case CTypeInfo::Type::kAny:
fast_call_result =
ChangeFloat64ToTagged(__ ChangeInt64ToFloat64(c_call_result),
CheckForMinusZeroMode::kCheckForMinusZero);
break;
}
auto merge = __ MakeLabel(MachineRepresentation::kTagged);
......
......@@ -27,6 +27,7 @@ ElementsKind GetTypedArrayElementsKind(CTypeInfo::Type type) {
case CTypeInfo::Type::kBool:
case CTypeInfo::Type::kV8Value:
case CTypeInfo::Type::kApiObject:
case CTypeInfo::Type::kAny:
UNREACHABLE();
}
}
......
......@@ -92,8 +92,8 @@ const int kMaxFastLiteralProperties = JSObject::kMaxInObjectProperties;
// to add support for IA32, because it has a totally different approach
// (using FP stack). As support is added to more platforms, please make sure
// to list them here in order to enable tests of this functionality.
#if defined(V8_TARGET_ARCH_X64) || \
(defined(V8_TARGET_ARCH_ARM64) && !defined(USE_SIMULATOR))
// Make sure to sync the following with src/d8/d8-test.cc.
#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_ARM64)
#define V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
#endif
......
......@@ -956,7 +956,10 @@ class FastApiCallReducerAssembler : public JSCallReducerAssembler {
CallDescriptor::kNeedsFrameState);
ApiFunction api_function(call_handler_info.callback());
ExternalReference function_reference = ExternalReference::Create(
&api_function, ExternalReference::DIRECT_API_CALL);
isolate(), &api_function, ExternalReference::DIRECT_API_CALL,
function_template_info_.c_functions().data(),
function_template_info_.c_signatures().data(),
static_cast<unsigned>(function_template_info_.c_functions().size()));
Node* continuation_frame_state =
CreateGenericLazyDeoptContinuationFrameState(
......
......@@ -208,6 +208,25 @@ int CallDescriptor::CalculateFixedFrameSize(CodeKind code_kind) const {
UNREACHABLE();
}
EncodedCSignature CallDescriptor::ToEncodedCSignature() const {
int parameter_count = static_cast<int>(ParameterCount());
EncodedCSignature sig(parameter_count);
CHECK_LT(parameter_count, EncodedCSignature::kInvalidParamCount);
for (int i = 0; i < parameter_count; ++i) {
if (IsFloatingPoint(GetParameterType(i).representation())) {
sig.SetFloat(i);
}
}
if (ReturnCount() > 0) {
DCHECK_EQ(1, ReturnCount());
if (IsFloatingPoint(GetReturnType(0).representation())) {
sig.SetFloat(EncodedCSignature::kReturnIndex);
}
}
return sig;
}
void CallDescriptor::ComputeParamCounts() const {
gp_param_count_ = 0;
fp_param_count_ = 0;
......
......@@ -15,6 +15,7 @@
#include "src/common/globals.h"
#include "src/compiler/frame.h"
#include "src/compiler/operator.h"
#include "src/execution/encoded-c-signature.h"
#include "src/runtime/runtime.h"
#include "src/zone/zone.h"
......@@ -434,6 +435,8 @@ class V8_EXPORT_PRIVATE CallDescriptor final
return allocatable_registers_ != 0;
}
EncodedCSignature ToEncodedCSignature() const;
private:
void ComputeParamCounts() const;
......
......@@ -1821,6 +1821,7 @@ class RepresentationSelector {
// path.
case CTypeInfo::Type::kInt64:
case CTypeInfo::Type::kUint64:
case CTypeInfo::Type::kAny:
return UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, feedback);
case CTypeInfo::Type::kFloat32:
case CTypeInfo::Type::kFloat64:
......
This diff is collapsed.
......@@ -14,7 +14,6 @@
#include <cstdarg>
#include <type_traits>
#include "src/base/lazy-instance.h"
#include "src/base/overflowing-math.h"
#include "src/base/platform/platform.h"
#include "src/base/platform/wrappers.h"
......@@ -503,6 +502,126 @@ void UnsafeDirectGetterCall(int64_t function, int64_t arg0, int64_t arg1) {
target(arg0, arg1);
}
using MixedRuntimeCall_0 = AnyCType (*)();
#define BRACKETS(ident, N) ident[N]
#define REP_0(expr, FMT)
#define REP_1(expr, FMT) FMT(expr, 0)
#define REP_2(expr, FMT) REP_1(expr, FMT), FMT(expr, 1)
#define REP_3(expr, FMT) REP_2(expr, FMT), FMT(expr, 2)
#define REP_4(expr, FMT) REP_3(expr, FMT), FMT(expr, 3)
#define REP_5(expr, FMT) REP_4(expr, FMT), FMT(expr, 4)
#define REP_6(expr, FMT) REP_5(expr, FMT), FMT(expr, 5)
#define REP_7(expr, FMT) REP_6(expr, FMT), FMT(expr, 6)
#define REP_8(expr, FMT) REP_7(expr, FMT), FMT(expr, 7)
#define REP_9(expr, FMT) REP_8(expr, FMT), FMT(expr, 8)
#define REP_10(expr, FMT) REP_9(expr, FMT), FMT(expr, 9)
#define REP_11(expr, FMT) REP_10(expr, FMT), FMT(expr, 10)
#define REP_12(expr, FMT) REP_11(expr, FMT), FMT(expr, 11)
#define REP_13(expr, FMT) REP_12(expr, FMT), FMT(expr, 12)
#define REP_14(expr, FMT) REP_13(expr, FMT), FMT(expr, 13)
#define REP_15(expr, FMT) REP_14(expr, FMT), FMT(expr, 14)
#define REP_16(expr, FMT) REP_15(expr, FMT), FMT(expr, 15)
#define REP_17(expr, FMT) REP_16(expr, FMT), FMT(expr, 16)
#define REP_18(expr, FMT) REP_17(expr, FMT), FMT(expr, 17)
#define REP_19(expr, FMT) REP_18(expr, FMT), FMT(expr, 18)
#define REP_20(expr, FMT) REP_19(expr, FMT), FMT(expr, 19)
#define GEN_MAX_PARAM_COUNT(V) \
V(0) \
V(1) \
V(2) \
V(3) \
V(4) \
V(5) \
V(6) \
V(7) \
V(8) \
V(9) \
V(10) \
V(11) \
V(12) \
V(13) \
V(14) \
V(15) \
V(16) \
V(17) \
V(18) \
V(19) \
V(20)
#define MIXED_RUNTIME_CALL(N) \
using MixedRuntimeCall_##N = AnyCType (*)(REP_##N(AnyCType arg, CONCAT));
GEN_MAX_PARAM_COUNT(MIXED_RUNTIME_CALL)
#undef MIXED_RUNTIME_CALL
#define CALL_ARGS(N) REP_##N(args, BRACKETS)
#define CALL_TARGET_VARARG(N) \
if (signature.ParameterCount() == N) { /* NOLINT */ \
MixedRuntimeCall_##N target = \
reinterpret_cast<MixedRuntimeCall_##N>(target_address); \
result = target(CALL_ARGS(N)); \
} else /* NOLINT */
void Simulator::CallAnyCTypeFunction(Address target_address,
const EncodedCSignature& signature) {
TraceSim("Type: mixed types BUILTIN_CALL\n");
const int64_t* stack_pointer = reinterpret_cast<int64_t*>(sp());
const double* double_stack_pointer = reinterpret_cast<double*>(sp());
int num_gp_params = 0, num_fp_params = 0, num_stack_params = 0;
CHECK_LE(signature.ParameterCount(), kMaxCParameters);
static_assert(sizeof(AnyCType) == 8, "AnyCType is assumed to be 64-bit.");
AnyCType args[kMaxCParameters];
// The first 8 parameters of each type (GP or FP) are placed in corresponding
// registers. The rest are expected to be on the stack, where each parameter
// type counts on its own. For example a function like:
// foo(int i1, ..., int i9, float f1, float f2) will use up all 8 GP
// registers, place i9 on the stack, and place f1 and f2 in FP registers.
// Source: https://developer.arm.com/documentation/ihi0055/d/, section
// "Parameter Passing".
for (int i = 0; i < signature.ParameterCount(); ++i) {
if (signature.IsFloat(i)) {
if (num_fp_params < 8) {
args[i].double_value = dreg(num_fp_params++);
} else {
args[i].double_value = double_stack_pointer[num_stack_params++];
}
} else {
if (num_gp_params < 8) {
args[i].int64_value = xreg(num_gp_params++);
} else {
args[i].int64_value = stack_pointer[num_stack_params++];
}
}
}
AnyCType result;
GEN_MAX_PARAM_COUNT(CALL_TARGET_VARARG)
/* else */ {
UNREACHABLE();
}
static_assert(20 == kMaxCParameters,
"If you've changed kMaxCParameters, please change the "
"GEN_MAX_PARAM_COUNT macro.");
#undef CALL_TARGET_VARARG
#undef CALL_ARGS
#undef GEN_MAX_PARAM_COUNT
#ifdef DEBUG
CorruptAllCallerSavedCPURegisters();
#endif
if (signature.IsReturnFloat()) {
set_dreg(0, result.double_value);
} else {
set_xreg(0, result.int64_value);
}
}
void Simulator::DoRuntimeCall(Instruction* instr) {
Redirection* redirection = Redirection::FromInstruction(instr);
......@@ -523,6 +642,17 @@ void Simulator::DoRuntimeCall(Instruction* instr) {
FATAL("ALIGNMENT EXCEPTION");
}
Address func_addr =
reinterpret_cast<Address>(redirection->external_function());
const EncodedCSignature& signature = GetSignatureForTarget(func_addr);
if (signature.IsValid()) {
CHECK(redirection->type() == ExternalReference::FAST_C_CALL);
CallAnyCTypeFunction(external, signature);
set_lr(return_address);
set_pc(return_address);
return;
}
int64_t* stack_pointer = reinterpret_cast<int64_t*>(sp());
const int64_t arg0 = xreg(0);
......@@ -552,17 +682,6 @@ void Simulator::DoRuntimeCall(Instruction* instr) {
TraceSim("Type: Unknown.\n");
UNREACHABLE();
// FAST_C_CALL is temporarily handled here as well, because we lack
// proper support for direct C calls with FP params in the simulator.
// The generic BUILTIN_CALL path assumes all parameters are passed in
// the GP registers, thus supporting calling the slow callback without
// crashing. The reason for that is that in the mjsunit tests we check
// the `fast_c_api.supports_fp_params` (which is false on non-simulator
// builds for arm/arm64), thus we expect that the slow path will be
// called. And since the slow path passes the arguments as a `const
// FunctionCallbackInfo<Value>&` (which is a GP argument), the call is
// made correctly.
case ExternalReference::FAST_C_CALL:
case ExternalReference::BUILTIN_CALL:
#if defined(V8_OS_WIN)
{
......@@ -6222,6 +6341,31 @@ void Simulator::GlobalMonitor::RemoveProcessor(Processor* processor) {
processor->next_ = nullptr;
}
void Simulator::RegisterFunctionsAndSignatures(
Address* c_functions, const CFunctionInfo* const* c_signatures,
unsigned num_functions) {
base::MutexGuard guard(&signature_map_mutex_);
for (unsigned i = 0; i < num_functions; ++i) {
EncodedCSignature sig(c_signatures[i]);
AddSignatureForTarget(c_functions[i], sig);
}
}
void Simulator::AddSignatureForTarget(Address target,
const EncodedCSignature& signature) {
target_to_signature_table_[target] = signature;
}
const EncodedCSignature& Simulator::GetSignatureForTarget(Address target) {
base::MutexGuard guard(&signature_map_mutex_);
auto entry = target_to_signature_table_.find(target);
if (entry != target_to_signature_table_.end()) {
const EncodedCSignature& sig = entry->second;
return sig;
}
return EncodedCSignature::Invalid();
}
#undef SScanF
#undef COLOUR
#undef COLOUR_BOLD
......@@ -6264,4 +6408,6 @@ V8_EXPORT_PRIVATE extern bool _v8_internal_Simulator_ExecDebugCommand(
return simulator->ExecDebugCommand(std::move(command_copy));
}
#undef BRACKETS
#endif // USE_SIMULATOR
......@@ -20,6 +20,7 @@
#include "src/codegen/arm64/decoder-arm64.h"
#include "src/codegen/assembler.h"
#include "src/diagnostics/arm64/disasm-arm64.h"
#include "src/execution/encoded-c-signature.h"
#include "src/execution/simulator-base.h"
#include "src/utils/allocation.h"
#include "src/utils/utils.h"
......@@ -1438,6 +1439,28 @@ class Simulator : public DecoderVisitor, public SimulatorBase {
PACKey key, PointerType type);
V8_EXPORT_PRIVATE static uint64_t StripPAC(uint64_t ptr, PointerType type);
// Calls AddSignatureForTarget for each function and signature, registering
// an encoded version of the signature within a mapping maintained by the
// simulator (from function address -> encoded signature). The function
// is supposed to be called whenever one compiles a fast API function with
// possibly multiple overloads.
// Note that this function is called from one or more compiler threads,
// while the main thread might be reading at the same time from the map, so
// both Register* and Get* are guarded with a single mutex.
void RegisterFunctionsAndSignatures(Address* c_functions,
const CFunctionInfo* const* c_signatures,
unsigned num_functions);
// The following method is used by the simulator itself to query
// whether a signature is registered for the call target and use this
// information to address arguments correctly (load them from either GP or
// FP registers, or from the stack).
const EncodedCSignature& GetSignatureForTarget(Address target);
// This method is exposed only for tests, which don't need synchronisation.
void AddSignatureForTargetForTesting(Address target,
const EncodedCSignature& signature) {
AddSignatureForTarget(target, signature);
}
protected:
// Simulation helpers ------------------------------------
bool ConditionPassed(Condition cond) {
......@@ -2449,6 +2472,9 @@ class Simulator : public DecoderVisitor, public SimulatorBase {
V8_EXPORT_PRIVATE void CallImpl(Address entry, CallArgument* args);
void CallAnyCTypeFunction(Address target_address,
const EncodedCSignature& signature);
// Read floating point return values.
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type
......@@ -2510,10 +2536,17 @@ class Simulator : public DecoderVisitor, public SimulatorBase {
}
}
void AddSignatureForTarget(Address target,
const EncodedCSignature& signature);
int log_parameters_;
// Instruction counter only valid if FLAG_stop_sim_at isn't 0.
int icount_for_stop_sim_at_;
Isolate* isolate_;
v8::base::Mutex signature_map_mutex_;
typedef std::unordered_map<Address, EncodedCSignature> TargetToSignatureTable;
TargetToSignatureTable target_to_signature_table_;
};
template <>
......
// Copyright 2021 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.
#include "src/execution/encoded-c-signature.h"
#include "include/v8-fast-api-calls.h"
#include "src/base/bits.h"
#include "src/base/logging.h"
namespace v8 {
namespace internal {
int EncodedCSignature::FPParameterCount() const {
CHECK(IsValid());
return base::bits::CountPopulation(bitfield_ & ~(1 << kReturnIndex));
}
EncodedCSignature::EncodedCSignature(const CFunctionInfo* signature) {
parameter_count_ = static_cast<int>(signature->ArgumentCount());
for (int i = 0; i < parameter_count_; ++i) {
if (signature->ArgumentInfo(i).GetSequenceType() ==
CTypeInfo::SequenceType::kScalar &&
CTypeInfo::IsFloatingPointType(signature->ArgumentInfo(i).GetType())) {
SetFloat(i);
}
}
// The struct holding the options of the CFunction (e.g. callback) is not
// included in the number of regular parameters, so we add it manually here.
if (signature->HasOptions()) {
parameter_count_++;
}
if (signature->ReturnInfo().GetSequenceType() ==
CTypeInfo::SequenceType::kScalar &&
CTypeInfo::IsFloatingPointType(signature->ReturnInfo().GetType())) {
SetFloat(EncodedCSignature::kReturnIndex);
}
}
} // namespace internal
} // namespace v8
// Copyright 2021 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_EXECUTION_ENCODED_C_SIGNATURE_H_
#define V8_EXECUTION_ENCODED_C_SIGNATURE_H_
#include <stdint.h>
namespace v8 {
class CFunctionInfo;
namespace internal {
namespace compiler {
class CallDescriptor;
} // namespace compiler
// This structure represents whether the parameters for a given function
// should be read from general purpose or FP registers. parameter_count =
// kInvalidParamCount represents "invalid" signature, a placeholder for
// non-existing elements in the mapping.
struct EncodedCSignature {
public:
EncodedCSignature() = default;
EncodedCSignature(uint32_t bitfield, int parameter_count)
: bitfield_(bitfield), parameter_count_(parameter_count) {}
explicit EncodedCSignature(int parameter_count)
: parameter_count_(parameter_count) {}
explicit EncodedCSignature(const CFunctionInfo* signature);
bool IsFloat(int index) const {
return (bitfield_ & (static_cast<uint32_t>(1) << index)) != 0;
}
bool IsReturnFloat() const { return IsFloat(kReturnIndex); }
void SetFloat(int index) { bitfield_ |= (static_cast<uint32_t>(1) << index); }
bool IsValid() const { return parameter_count_ < kInvalidParamCount; }
int ParameterCount() const { return parameter_count_; }
int FPParameterCount() const;
static const EncodedCSignature& Invalid() {
static EncodedCSignature kInvalid = {0, kInvalidParamCount};
return kInvalid;
}
static const int kReturnIndex = 31;
static const int kInvalidParamCount = kReturnIndex + 1;
private:
uint32_t bitfield_ = 0; // Bit i is set if floating point, unset if not.
int parameter_count_ = kInvalidParamCount;
};
} // namespace internal
} // namespace v8
#endif // V8_EXECUTION_ENCODED_C_SIGNATURE_H_
......@@ -7,6 +7,9 @@
#include <type_traits>
#ifdef V8_TARGET_ARCH_ARM64
#include "include/v8-fast-api-calls.h"
#endif // V8_TARGET_ARCH_ARM64
#include "src/base/hashmap.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
......@@ -68,6 +71,16 @@ class SimulatorBase {
return Object(ret);
}
#ifdef V8_TARGET_ARCH_ARM64
template <typename T>
static typename std::enable_if<std::is_same<T, v8::AnyCType>::value, T>::type
ConvertReturn(intptr_t ret) {
v8::AnyCType result;
result.int64_value = static_cast<int64_t>(ret);
return result;
}
#endif // V8_TARGET_ARCH_ARM64
// Convert back void return type (i.e. no return).
template <typename T>
static typename std::enable_if<std::is_void<T>::value, T>::type ConvertReturn(
......@@ -106,6 +119,13 @@ class SimulatorBase {
ConvertArg(T arg) {
return reinterpret_cast<intptr_t>(arg);
}
template <typename T>
static
typename std::enable_if<std::is_floating_point<T>::value, intptr_t>::type
ConvertArg(T arg) {
UNREACHABLE();
}
};
// When the generated code calls an external reference we need to catch that in
......
......@@ -122,9 +122,13 @@ class GeneratedCode {
// Starboard is a platform abstraction interface that also include Windows
// platforms like UWP.
#if defined(V8_TARGET_OS_WIN) && !defined(V8_OS_WIN) && \
!defined(V8_OS_STARBOARD)
FATAL("Generated code execution not possible during cross-compilation.");
#endif // defined(V8_TARGET_OS_WIN) && !defined(V8_OS_WIN)
!defined(V8_OS_STARBOARD) && !defined(V8_TARGET_ARCH_ARM)
FATAL(
"Generated code execution not possible during cross-compilation."
"Also, generic C function calls are not implemented on 32-bit arm "
"yet.");
#endif // defined(V8_TARGET_OS_WIN) && !defined(V8_OS_WIN) &&
// !defined(V8_OS_STARBOARD) && !defined(V8_TARGET_ARCH_ARM)
return Simulator::current(isolate_)->template Call<Return>(
reinterpret_cast<Address>(fn_ptr_), args...);
}
......
......@@ -5,6 +5,10 @@
#ifndef V8_COMPILER_C_SIGNATURE_H_
#define V8_COMPILER_C_SIGNATURE_H_
#ifdef USE_SIMULATOR_WITH_GENERIC_C_CALLS
#include "include/v8-fast-api-calls.h"
#endif // USE_SIMULATOR_WITH_GENERIC_C_CALLS
#include "src/codegen/machine-type.h"
namespace v8 {
......@@ -42,6 +46,12 @@ inline constexpr MachineType MachineTypeForC() {
FOREACH_CTYPE_MACHINE_TYPE_MAPPING(DECLARE_TEMPLATE_SPECIALIZATION)
#undef DECLARE_TEMPLATE_SPECIALIZATION
#ifdef USE_SIMULATOR_WITH_GENERIC_C_CALLS
template <>
inline MachineType constexpr MachineTypeForC<v8::AnyCType>() {
return MachineType::Int64();
}
#endif // USE_SIMULATOR_WITH_GENERIC_C_CALLS
// Helper for building machine signatures from C types.
class CSignature : public MachineSignature {
protected:
......
This diff is collapsed.
......@@ -27867,6 +27867,81 @@ UNINITIALIZED_TEST(NestedIsolates) {
#ifndef V8_LITE_MODE
namespace {
#ifdef USE_SIMULATOR_WITH_GENERIC_C_CALLS
template <typename Value>
Value PrimitiveFromMixedType(v8::AnyCType argument);
template <>
bool PrimitiveFromMixedType(v8::AnyCType argument) {
return argument.bool_value;
}
template <>
int32_t PrimitiveFromMixedType(v8::AnyCType argument) {
return argument.int32_value;
}
template <>
uint32_t PrimitiveFromMixedType(v8::AnyCType argument) {
return argument.uint32_value;
}
template <>
int64_t PrimitiveFromMixedType(v8::AnyCType argument) {
return argument.int64_value;
}
template <>
uint64_t PrimitiveFromMixedType(v8::AnyCType argument) {
return argument.uint64_value;
}
template <>
float PrimitiveFromMixedType(v8::AnyCType argument) {
return argument.float_value;
}
template <>
double PrimitiveFromMixedType(v8::AnyCType argument) {
return argument.double_value;
}
template <>
v8::Local<v8::Value> PrimitiveFromMixedType(v8::AnyCType argument) {
return argument.object_value;
}
template <typename T>
v8::AnyCType PrimitiveToMixedType(T value) {
return v8::AnyCType();
}
template <>
v8::AnyCType PrimitiveToMixedType(bool value) {
v8::AnyCType ret;
ret.bool_value = value;
return ret;
}
template <>
v8::AnyCType PrimitiveToMixedType(int32_t value) {
v8::AnyCType ret;
ret.int32_value = value;
return ret;
}
template <>
v8::AnyCType PrimitiveToMixedType(uint32_t value) {
v8::AnyCType ret;
ret.uint32_value = value;
return ret;
}
template <>
v8::AnyCType PrimitiveToMixedType(float value) {
v8::AnyCType ret;
ret.float_value = value;
return ret;
}
template <>
v8::AnyCType PrimitiveToMixedType(double value) {
v8::AnyCType ret;
ret.double_value = value;
return ret;
}
#endif // USE_SIMULATOR_WITH_GENERIC_C_CALLS
template <typename Value, typename Impl, typename Ret>
struct BasicApiChecker {
static Ret FastCallback(v8::Local<v8::Object> receiver, Value argument,
......@@ -27882,6 +27957,7 @@ struct BasicApiChecker {
v8::FastApiCallbackOptions options = {false, {0}};
return Impl::FastCallback(receiver, argument, options);
}
static void SlowCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
Impl::SlowCallback(info);
}
......@@ -27898,6 +27974,44 @@ struct BasicApiChecker {
ApiCheckerResultFlags result_ = ApiCheckerResult::kNotCalled;
};
#ifdef USE_SIMULATOR_WITH_GENERIC_C_CALLS
template <typename Value, typename Impl, typename Ret,
typename = std::enable_if_t<!std::is_void<Ret>::value>>
static v8::AnyCType FastCallbackPatch(v8::AnyCType receiver,
v8::AnyCType argument,
v8::AnyCType options) {
v8::AnyCType ret = PrimitiveToMixedType<Ret>(Impl::FastCallback(
receiver.object_value, PrimitiveFromMixedType<Value>(argument),
*(options.options_value)));
return ret;
}
template <typename Value, typename Impl, typename Ret,
typename = std::enable_if_t<!std::is_void<Ret>::value>>
static v8::AnyCType FastCallbackNoFallbackWrapper(v8::AnyCType receiver,
v8::AnyCType argument) {
v8::FastApiCallbackOptions options = {false, {0}};
v8::AnyCType ret = PrimitiveToMixedType<Ret>(Impl::FastCallback(
receiver.object_value, PrimitiveFromMixedType<Value>(argument), options));
return ret;
}
template <typename Value, typename Impl, typename Ret,
typename = std::enable_if_t<std::is_void<Ret>::value>>
static void FastCallbackPatch(v8::AnyCType receiver, v8::AnyCType argument,
v8::AnyCType options) {
return Impl::FastCallback(receiver.object_value,
PrimitiveFromMixedType<Value>(argument),
*(options.options_value));
}
template <typename Value, typename Impl, typename Ret,
typename = std::enable_if_t<std::is_void<Ret>::value>>
static void FastCallbackNoFallbackWrapper(v8::AnyCType receiver,
v8::AnyCType argument) {
v8::FastApiCallbackOptions options = {false, {0}};
return Impl::FastCallback(receiver.object_value,
PrimitiveFromMixedType<Value>(argument), options);
}
#endif // USE_SIMULATOR_WITH_GENERIC_C_CALLS
enum class Behavior {
kNoException,
kException, // An exception should be thrown by the callback function.
......@@ -28042,11 +28156,23 @@ bool SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
v8::CFunction c_func;
if (supports_fallback) {
#ifdef USE_SIMULATOR_WITH_GENERIC_C_CALLS
c_func =
v8::CFunction::Make(BasicApiChecker<Value, Impl, Ret>::FastCallback,
FastCallbackPatch<Value, Impl, Ret>);
#else // USE_SIMULATOR_WITH_GENERIC_C_CALLS
c_func =
v8::CFunction::Make(BasicApiChecker<Value, Impl, Ret>::FastCallback);
#endif // USE_SIMULATOR_WITH_GENERIC_C_CALLS
} else {
#ifdef USE_SIMULATOR_WITH_GENERIC_C_CALLS
c_func = v8::CFunction::Make(
BasicApiChecker<Value, Impl, Ret>::FastCallbackNoFallback,
FastCallbackNoFallbackWrapper<Value, Impl, Ret>);
#else // USE_SIMULATOR_WITH_GENERIC_C_CALLS
c_func = v8::CFunction::Make(
BasicApiChecker<Value, Impl, Ret>::FastCallbackNoFallback);
#endif // USE_SIMULATOR_WITH_GENERIC_C_CALLS
}
CHECK_EQ(c_func.ArgumentInfo(0).GetType(), v8::CTypeInfo::Type::kV8Value);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment