[turbofan] Fast API calls from TurboFan
Design doc: http://doc/1SAHn7d8M7CoazTd1laVF8gduFC_ikZWiYuytrR9c4Oc/ This CL implements basic API with integer and pointer types marshaling. What is not supported yet: - sequences - annotations - floating point arguments - 64-bit arguments - exception handling - InstanceOf checks for the pointer types - functions with non-void return type Bug: chromium:1052746 Change-Id: Idbbf6dd50f43dfc9f8d707fe3333e5da3da84a13 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2030740 Commit-Queue: Maya Lekova <mslekova@chromium.org> Reviewed-by: Toon Verwaest <verwaest@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Reviewed-by: Leszek Swirski <leszeks@chromium.org> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Reviewed-by: Michael Stanton <mvstanton@chromium.org> Cr-Commit-Position: refs/heads/master@{#66322}
This commit is contained in:
parent
e9036451af
commit
50790c0b0d
2
BUILD.gn
2
BUILD.gn
@ -1706,6 +1706,7 @@ v8_header_set("v8_headers") {
|
|||||||
public_configs = [ ":v8_header_features" ]
|
public_configs = [ ":v8_header_features" ]
|
||||||
|
|
||||||
sources = [
|
sources = [
|
||||||
|
"include/v8-fast-api-calls.h",
|
||||||
"include/v8-internal.h",
|
"include/v8-internal.h",
|
||||||
"include/v8.h",
|
"include/v8.h",
|
||||||
"include/v8config.h",
|
"include/v8config.h",
|
||||||
@ -2027,6 +2028,7 @@ v8_source_set("v8_base_without_compiler") {
|
|||||||
|
|
||||||
### gcmole(all) ###
|
### gcmole(all) ###
|
||||||
"$target_gen_dir/builtins-generated/bytecodes-builtins-list.h",
|
"$target_gen_dir/builtins-generated/bytecodes-builtins-list.h",
|
||||||
|
"include/v8-fast-api-calls.h",
|
||||||
"include/v8-inspector-protocol.h",
|
"include/v8-inspector-protocol.h",
|
||||||
"include/v8-inspector.h",
|
"include/v8-inspector.h",
|
||||||
"include/v8-internal.h",
|
"include/v8-internal.h",
|
||||||
|
404
include/v8-fast-api-calls.h
Normal file
404
include/v8-fast-api-calls.h
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
// Copyright 2020 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.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file provides additional API on top of the default one for making
|
||||||
|
* API calls, which come from embedder C++ functions. The functions are being
|
||||||
|
* called directly from optimized code, doing all the necessary typechecks
|
||||||
|
* in the compiler itself, instead of on the embedder side. Hence the "fast"
|
||||||
|
* in the name. Example usage might look like:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* void FastMethod(int param, bool another_param);
|
||||||
|
*
|
||||||
|
* v8::FunctionTemplate::New(isolate, SlowCallback, data,
|
||||||
|
* signature, length, constructor_behavior
|
||||||
|
* side_effect_type,
|
||||||
|
* &v8::CFunction::Make(FastMethod));
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* An example for custom embedder type support might employ a way to wrap/
|
||||||
|
* unwrap various C++ types in JSObject instances, e.g:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
*
|
||||||
|
* // Represents the way this type system maps C++ and JS values.
|
||||||
|
* struct WrapperTypeInfo {
|
||||||
|
* // Store e.g. a method to map from exposed C++ types to the already
|
||||||
|
* // created v8::FunctionTemplate's for instantiating them.
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // Helper method with a sanity check.
|
||||||
|
* template <typename T, int offset>
|
||||||
|
* inline T* GetInternalField(v8::Local<v8::Object> wrapper) {
|
||||||
|
* assert(offset < wrapper->InternalFieldCount());
|
||||||
|
* return reinterpret_cast<T*>(
|
||||||
|
* wrapper->GetAlignedPointerFromInternalField(offset));
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Returns the type info from a wrapper JS object.
|
||||||
|
* inline const WrapperTypeInfo* ToWrapperTypeInfo(
|
||||||
|
* v8::Local<v8::Object> wrapper) {
|
||||||
|
* return GetInternalField<WrapperTypeInfo,
|
||||||
|
* kV8EmbedderWrapperTypeIndex>(wrapper);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* class CustomEmbedderType {
|
||||||
|
* public:
|
||||||
|
* static constexpr const WrapperTypeInfo* GetWrapperTypeInfo() {
|
||||||
|
* return &custom_type_wrapper_type_info;
|
||||||
|
* }
|
||||||
|
* // Returns the raw C object from a wrapper JS object.
|
||||||
|
* static CustomEmbedderType* Unwrap(v8::Local<v8::Object> wrapper) {
|
||||||
|
* return GetInternalField<CustomEmbedderType,
|
||||||
|
* kV8EmbedderWrapperObjectIndex>(wrapper);
|
||||||
|
* }
|
||||||
|
* static void FastMethod(CustomEmbedderType* receiver, int param) {
|
||||||
|
* assert(receiver != nullptr);
|
||||||
|
* // Type checks are already done by the optimized code.
|
||||||
|
* // Then call some performance-critical method like:
|
||||||
|
* // receiver->Method(param);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* static void SlowMethod(
|
||||||
|
* const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||||
|
* v8::Local<v8::Object> instance =
|
||||||
|
* v8::Local<v8::Object>::Cast(info.Holder());
|
||||||
|
* CustomEmbedderType* receiver = Unwrap(instance);
|
||||||
|
* // TODO: Do type checks and extract {param}.
|
||||||
|
* FastMethod(receiver, param);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* private:
|
||||||
|
* static const WrapperTypeInfo custom_type_wrapper_type_info;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // Support for custom embedder types via specialization of WrapperTraits.
|
||||||
|
* namespace v8 {
|
||||||
|
* template <>
|
||||||
|
* class WrapperTraits<CustomEmbedderType> {
|
||||||
|
* public:
|
||||||
|
* static const void* GetTypeInfo() {
|
||||||
|
* // We use the already defined machinery for the custom type.
|
||||||
|
* return CustomEmbedderType::GetWrapperTypeInfo();
|
||||||
|
* }
|
||||||
|
* };
|
||||||
|
* } // namespace v8
|
||||||
|
*
|
||||||
|
* // The constants kV8EmbedderWrapperTypeIndex and
|
||||||
|
* // kV8EmbedderWrapperObjectIndex describe the offsets for the type info
|
||||||
|
* // struct (the one returned by WrapperTraits::GetTypeInfo) and the
|
||||||
|
* // native object, when expressed as internal field indices within a
|
||||||
|
* // JSObject. The existance of this helper function assumes that all
|
||||||
|
* // embedder objects have their JSObject-side type info at the same
|
||||||
|
* // offset, but this is not a limitation of the API itself. For a detailed
|
||||||
|
* // use case, see the third example.
|
||||||
|
* static constexpr int kV8EmbedderWrapperTypeIndex = 0;
|
||||||
|
* static constexpr int kV8EmbedderWrapperObjectIndex = 1;
|
||||||
|
*
|
||||||
|
* // The following setup function can be templatized based on
|
||||||
|
* // the {embedder_object} argument.
|
||||||
|
* void SetupCustomEmbedderObject(v8::Isolate* isolate,
|
||||||
|
* v8::Local<v8::Context> context,
|
||||||
|
* CustomEmbedderType* embedder_object) {
|
||||||
|
* isolate->set_embedder_wrapper_type_index(
|
||||||
|
* kV8EmbedderWrapperTypeIndex);
|
||||||
|
* isolate->set_embedder_wrapper_object_index(
|
||||||
|
* kV8EmbedderWrapperObjectIndex);
|
||||||
|
*
|
||||||
|
* v8::CFunction c_func =
|
||||||
|
* MakeV8CFunction(CustomEmbedderType::FastMethod);
|
||||||
|
*
|
||||||
|
* Local<v8::FunctionTemplate> method_template =
|
||||||
|
* v8::FunctionTemplate::New(
|
||||||
|
* isolate, CustomEmbedderType::SlowMethod, v8::Local<v8::Value>(),
|
||||||
|
* v8::Local<v8::Signature>(), 1, v8::ConstructorBehavior::kAllow,
|
||||||
|
* v8::SideEffectType::kHasSideEffect, &c_func);
|
||||||
|
*
|
||||||
|
* v8::Local<v8::ObjectTemplate> object_template =
|
||||||
|
* v8::ObjectTemplate::New(isolate);
|
||||||
|
* object_template->SetInternalFieldCount(
|
||||||
|
* kV8EmbedderWrapperObjectIndex + 1);
|
||||||
|
* object_template->Set(v8::String::NewFromUtf8(isolate, "method",
|
||||||
|
* v8::NewStringType::kNormal)
|
||||||
|
* .ToLocalChecked(), method_template);
|
||||||
|
*
|
||||||
|
* // Instantiate the wrapper JS object.
|
||||||
|
* v8::Local<v8::Object> object =
|
||||||
|
* object_template->NewInstance(context).ToLocalChecked();
|
||||||
|
* object->SetAlignedPointerInInternalField(
|
||||||
|
* kV8EmbedderWrapperObjectIndex,
|
||||||
|
* reinterpret_cast<void*>(embedder_object));
|
||||||
|
*
|
||||||
|
* // TODO: Expose {object} where it's necessary.
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* For instance if {object} is exposed via a global "obj" variable,
|
||||||
|
* one could write in JS:
|
||||||
|
* function hot_func() {
|
||||||
|
* obj.method(42);
|
||||||
|
* }
|
||||||
|
* and once {hot_func} gets optimized, CustomEmbedderType::FastMethod
|
||||||
|
* will be called instead of the slow version, with the following arguments:
|
||||||
|
* receiver := the {embedder_object} from above
|
||||||
|
* param := 42
|
||||||
|
*
|
||||||
|
* Currently only void return types are supported.
|
||||||
|
* Currently supported argument types:
|
||||||
|
* - pointer to an embedder type
|
||||||
|
* - bool
|
||||||
|
* - int32_t
|
||||||
|
* - uint32_t
|
||||||
|
* To be supported types:
|
||||||
|
* - int64_t
|
||||||
|
* - uint64_t
|
||||||
|
* - float32_t
|
||||||
|
* - float64_t
|
||||||
|
* - arrays of C types
|
||||||
|
* - arrays of embedder types
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef INCLUDE_V8_FAST_API_CALLS_H_
|
||||||
|
#define INCLUDE_V8_FAST_API_CALLS_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "v8config.h" // NOLINT(build/include)
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
|
||||||
|
class CTypeInfo {
|
||||||
|
public:
|
||||||
|
enum class Type : char {
|
||||||
|
kVoid,
|
||||||
|
kBool,
|
||||||
|
kInt32,
|
||||||
|
kUint32,
|
||||||
|
kInt64,
|
||||||
|
kUint64,
|
||||||
|
kFloat32,
|
||||||
|
kFloat64,
|
||||||
|
kUnwrappedApiObject,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ArgFlags : char {
|
||||||
|
None = 0,
|
||||||
|
IsArrayBit = 1 << 0, // This argument is first in an array of values.
|
||||||
|
};
|
||||||
|
|
||||||
|
static CTypeInfo FromWrapperType(const void* wrapper_type_info,
|
||||||
|
ArgFlags flags = ArgFlags::None) {
|
||||||
|
uintptr_t wrapper_type_info_ptr =
|
||||||
|
reinterpret_cast<uintptr_t>(wrapper_type_info);
|
||||||
|
// Check that the lower kIsWrapperTypeBit bits are 0's.
|
||||||
|
CHECK_EQ(
|
||||||
|
wrapper_type_info_ptr & ~(static_cast<uintptr_t>(~0)
|
||||||
|
<< static_cast<uintptr_t>(kIsWrapperTypeBit)),
|
||||||
|
0);
|
||||||
|
// TODO(mslekova): Refactor the manual bit manipulations to use
|
||||||
|
// PointerWithPayload instead.
|
||||||
|
return CTypeInfo(wrapper_type_info_ptr | flags | kIsWrapperTypeBit);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr CTypeInfo FromCType(Type ctype,
|
||||||
|
ArgFlags flags = ArgFlags::None) {
|
||||||
|
// ctype cannot be Type::kUnwrappedApiObject.
|
||||||
|
return CTypeInfo(
|
||||||
|
((static_cast<uintptr_t>(ctype) << kTypeOffset) & kTypeMask) | flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* GetWrapperInfo() const;
|
||||||
|
|
||||||
|
constexpr Type GetType() const {
|
||||||
|
if (payload_ & kIsWrapperTypeBit) {
|
||||||
|
return Type::kUnwrappedApiObject;
|
||||||
|
}
|
||||||
|
return static_cast<Type>((payload_ & kTypeMask) >> kTypeOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsArray() const { return payload_ & ArgFlags::IsArrayBit; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit constexpr CTypeInfo(uintptr_t payload) : payload_(payload) {}
|
||||||
|
|
||||||
|
// That must be the last bit after ArgFlags.
|
||||||
|
static constexpr uintptr_t kIsWrapperTypeBit = 1 << 1;
|
||||||
|
static constexpr uintptr_t kWrapperTypeInfoMask = static_cast<uintptr_t>(~0)
|
||||||
|
<< 2;
|
||||||
|
|
||||||
|
static constexpr unsigned int kTypeOffset = kIsWrapperTypeBit;
|
||||||
|
static constexpr unsigned int kTypeSize = 8 - kTypeOffset;
|
||||||
|
static constexpr uintptr_t kTypeMask =
|
||||||
|
(~(static_cast<uintptr_t>(~0) << kTypeSize)) << kTypeOffset;
|
||||||
|
|
||||||
|
const uintptr_t payload_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CFunctionInfo {
|
||||||
|
public:
|
||||||
|
virtual const CTypeInfo& ReturnInfo() const = 0;
|
||||||
|
virtual unsigned int ArgumentCount() const = 0;
|
||||||
|
virtual const CTypeInfo* ArgumentInfo() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class WrapperTraits {
|
||||||
|
public:
|
||||||
|
static const void* GetTypeInfo() {
|
||||||
|
static_assert(sizeof(T) != sizeof(T),
|
||||||
|
"WrapperTraits must be specialized for this type.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct GetCType {
|
||||||
|
static_assert(sizeof(T) != sizeof(T), "Unsupported CType");
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SPECIALIZE_GET_C_TYPE_FOR(ctype, ctypeinfo) \
|
||||||
|
template <> \
|
||||||
|
struct GetCType<ctype> { \
|
||||||
|
static constexpr CTypeInfo Get() { \
|
||||||
|
return CTypeInfo::FromCType(CTypeInfo::Type::ctypeinfo); \
|
||||||
|
} \
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SUPPORTED_C_TYPES(V) \
|
||||||
|
V(void, kVoid) \
|
||||||
|
V(bool, kBool) \
|
||||||
|
V(int32_t, kInt32) \
|
||||||
|
V(uint32_t, kUint32) \
|
||||||
|
V(int64_t, kInt64) \
|
||||||
|
V(uint64_t, kUint64) \
|
||||||
|
V(float, kFloat32) \
|
||||||
|
V(double, kFloat64)
|
||||||
|
|
||||||
|
SUPPORTED_C_TYPES(SPECIALIZE_GET_C_TYPE_FOR)
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct EnableIfHasWrapperTypeInfo {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct EnableIfHasWrapperTypeInfo<void> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct EnableIfHasWrapperTypeInfo<T, decltype(WrapperTraits<T>::GetTypeInfo(),
|
||||||
|
void())> {
|
||||||
|
typedef void type;
|
||||||
|
};
|
||||||
|
|
||||||
|
// T* where T is a primitive (array of primitives).
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct GetCTypePointerImpl {
|
||||||
|
static constexpr CTypeInfo Get() {
|
||||||
|
return CTypeInfo::FromCType(GetCType<T>::Get().GetType(),
|
||||||
|
CTypeInfo::IsArrayBit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// T* where T is an API object.
|
||||||
|
template <typename T>
|
||||||
|
struct GetCTypePointerImpl<T, typename EnableIfHasWrapperTypeInfo<T>::type> {
|
||||||
|
static constexpr CTypeInfo Get() {
|
||||||
|
return CTypeInfo::FromWrapperType(WrapperTraits<T>::GetTypeInfo());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// T** where T is a primitive. Not allowed.
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct GetCTypePointerPointerImpl {
|
||||||
|
static_assert(sizeof(T**) != sizeof(T**), "Unsupported type");
|
||||||
|
};
|
||||||
|
|
||||||
|
// T** where T is an API object (array of API objects).
|
||||||
|
template <typename T>
|
||||||
|
struct GetCTypePointerPointerImpl<
|
||||||
|
T, typename EnableIfHasWrapperTypeInfo<T>::type> {
|
||||||
|
static constexpr CTypeInfo Get() {
|
||||||
|
return CTypeInfo::FromWrapperType(WrapperTraits<T>::GetTypeInfo(),
|
||||||
|
CTypeInfo::IsArrayBit);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct GetCType<T**> : public GetCTypePointerPointerImpl<T> {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct GetCType<T*> : public GetCTypePointerImpl<T> {};
|
||||||
|
|
||||||
|
template <typename R, typename... Args>
|
||||||
|
class CFunctionInfoImpl : public CFunctionInfo {
|
||||||
|
public:
|
||||||
|
CFunctionInfoImpl()
|
||||||
|
: return_info_(i::GetCType<R>::Get()),
|
||||||
|
arg_count_(sizeof...(Args)),
|
||||||
|
arg_info_{i::GetCType<Args>::Get()...} {
|
||||||
|
static_assert(i::GetCType<R>::Get().GetType() == CTypeInfo::Type::kVoid,
|
||||||
|
"Only void return types are currently supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const CTypeInfo& ReturnInfo() const override { return return_info_; }
|
||||||
|
unsigned int ArgumentCount() const override { return arg_count_; }
|
||||||
|
const CTypeInfo* ArgumentInfo() const override { return arg_info_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
CTypeInfo return_info_;
|
||||||
|
const unsigned int arg_count_;
|
||||||
|
CTypeInfo arg_info_[sizeof...(Args)];
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
class V8_EXPORT CFunction {
|
||||||
|
public:
|
||||||
|
const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); }
|
||||||
|
|
||||||
|
const CTypeInfo* ArgumentInfo() const { return type_info_->ArgumentInfo(); }
|
||||||
|
|
||||||
|
unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); }
|
||||||
|
|
||||||
|
const void* GetAddress() const { return address_; }
|
||||||
|
const CFunctionInfo* GetTypeInfo() const { return type_info_; }
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
static CFunction Make(F* func) {
|
||||||
|
return ArgUnwrap<F*>::Make(func);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const void* address_;
|
||||||
|
const CFunctionInfo* type_info_;
|
||||||
|
|
||||||
|
CFunction(const void* address, const CFunctionInfo* type_info);
|
||||||
|
|
||||||
|
template <typename R, typename... Args>
|
||||||
|
static CFunctionInfo* GetCFunctionInfo() {
|
||||||
|
static internal::CFunctionInfoImpl<R, Args...> instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
class ArgUnwrap {
|
||||||
|
static_assert(sizeof(F) != sizeof(F),
|
||||||
|
"CFunction must be created from a function pointer.");
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename R, typename... Args>
|
||||||
|
class ArgUnwrap<R (*)(Args...)> {
|
||||||
|
public:
|
||||||
|
static CFunction Make(R (*func)(Args...)) {
|
||||||
|
return CFunction(reinterpret_cast<const void*>(func),
|
||||||
|
GetCFunctionInfo<R, Args...>());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace v8
|
||||||
|
|
||||||
|
#endif // INCLUDE_V8_FAST_API_CALLS_H_
|
16
include/v8.h
16
include/v8.h
@ -6284,6 +6284,7 @@ typedef bool (*AccessCheckCallback)(Local<Context> accessing_context,
|
|||||||
Local<Object> accessed_object,
|
Local<Object> accessed_object,
|
||||||
Local<Value> data);
|
Local<Value> data);
|
||||||
|
|
||||||
|
class CFunction;
|
||||||
/**
|
/**
|
||||||
* A FunctionTemplate is used to create functions at runtime. There
|
* A FunctionTemplate is used to create functions at runtime. There
|
||||||
* can only be one function created from a FunctionTemplate in a
|
* can only be one function created from a FunctionTemplate in a
|
||||||
@ -6383,6 +6384,12 @@ typedef bool (*AccessCheckCallback)(Local<Context> accessing_context,
|
|||||||
* child_instance.instance_accessor calls 'InstanceAccessorCallback'
|
* child_instance.instance_accessor calls 'InstanceAccessorCallback'
|
||||||
* child_instance.instance_property == 3;
|
* child_instance.instance_property == 3;
|
||||||
* \endcode
|
* \endcode
|
||||||
|
*
|
||||||
|
* The additional 'c_function' parameter refers to a fast API call, which
|
||||||
|
* must not trigger GC or JavaScript execution, or call into V8 in other
|
||||||
|
* ways. For more information how to define them, see
|
||||||
|
* include/v8-fast-api-calls.h. Please note that this feature is still
|
||||||
|
* experimental.
|
||||||
*/
|
*/
|
||||||
class V8_EXPORT FunctionTemplate : public Template {
|
class V8_EXPORT FunctionTemplate : public Template {
|
||||||
public:
|
public:
|
||||||
@ -6392,7 +6399,8 @@ class V8_EXPORT FunctionTemplate : public Template {
|
|||||||
Local<Value> data = Local<Value>(),
|
Local<Value> data = Local<Value>(),
|
||||||
Local<Signature> signature = Local<Signature>(), int length = 0,
|
Local<Signature> signature = Local<Signature>(), int length = 0,
|
||||||
ConstructorBehavior behavior = ConstructorBehavior::kAllow,
|
ConstructorBehavior behavior = ConstructorBehavior::kAllow,
|
||||||
SideEffectType side_effect_type = SideEffectType::kHasSideEffect);
|
SideEffectType side_effect_type = SideEffectType::kHasSideEffect,
|
||||||
|
const CFunction* c_function = nullptr);
|
||||||
|
|
||||||
/** Get a template included in the snapshot by index. */
|
/** Get a template included in the snapshot by index. */
|
||||||
V8_DEPRECATED("Use v8::Isolate::GetDataFromSnapshotOnce instead")
|
V8_DEPRECATED("Use v8::Isolate::GetDataFromSnapshotOnce instead")
|
||||||
@ -6424,11 +6432,13 @@ class V8_EXPORT FunctionTemplate : public Template {
|
|||||||
/**
|
/**
|
||||||
* Set the call-handler callback for a FunctionTemplate. This
|
* Set the call-handler callback for a FunctionTemplate. This
|
||||||
* callback is called whenever the function created from this
|
* callback is called whenever the function created from this
|
||||||
* FunctionTemplate is called.
|
* FunctionTemplate is called. The 'c_function' represents a fast
|
||||||
|
* API call, see the comment above the class declaration.
|
||||||
*/
|
*/
|
||||||
void SetCallHandler(
|
void SetCallHandler(
|
||||||
FunctionCallback callback, Local<Value> data = Local<Value>(),
|
FunctionCallback callback, Local<Value> data = Local<Value>(),
|
||||||
SideEffectType side_effect_type = SideEffectType::kHasSideEffect);
|
SideEffectType side_effect_type = SideEffectType::kHasSideEffect,
|
||||||
|
const CFunction* c_function = nullptr);
|
||||||
|
|
||||||
/** Set the predefined length property for the FunctionTemplate. */
|
/** Set the predefined length property for the FunctionTemplate. */
|
||||||
void SetLength(int length);
|
void SetLength(int length);
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include "src/api/api-inl.h"
|
#include "src/api/api-inl.h"
|
||||||
|
|
||||||
|
#include "include/v8-fast-api-calls.h"
|
||||||
#include "include/v8-profiler.h"
|
#include "include/v8-profiler.h"
|
||||||
#include "include/v8-util.h"
|
#include "include/v8-util.h"
|
||||||
#include "src/api/api-natives.h"
|
#include "src/api/api-natives.h"
|
||||||
@ -1461,7 +1462,8 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
|||||||
i::Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
|
i::Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
|
||||||
v8::Local<Signature> signature, int length, bool do_not_cache,
|
v8::Local<Signature> signature, int length, bool do_not_cache,
|
||||||
v8::Local<Private> cached_property_name = v8::Local<Private>(),
|
v8::Local<Private> cached_property_name = v8::Local<Private>(),
|
||||||
SideEffectType side_effect_type = SideEffectType::kHasSideEffect) {
|
SideEffectType side_effect_type = SideEffectType::kHasSideEffect,
|
||||||
|
const CFunction* c_function = nullptr) {
|
||||||
i::Handle<i::Struct> struct_obj = isolate->factory()->NewStruct(
|
i::Handle<i::Struct> struct_obj = isolate->factory()->NewStruct(
|
||||||
i::FUNCTION_TEMPLATE_INFO_TYPE, i::AllocationType::kOld);
|
i::FUNCTION_TEMPLATE_INFO_TYPE, i::AllocationType::kOld);
|
||||||
i::Handle<i::FunctionTemplateInfo> obj =
|
i::Handle<i::FunctionTemplateInfo> obj =
|
||||||
@ -1479,7 +1481,8 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
|||||||
obj->set_serial_number(i::Smi::FromInt(next_serial_number));
|
obj->set_serial_number(i::Smi::FromInt(next_serial_number));
|
||||||
}
|
}
|
||||||
if (callback != nullptr) {
|
if (callback != nullptr) {
|
||||||
Utils::ToLocal(obj)->SetCallHandler(callback, data, side_effect_type);
|
Utils::ToLocal(obj)->SetCallHandler(callback, data, side_effect_type,
|
||||||
|
c_function);
|
||||||
}
|
}
|
||||||
obj->set_undetectable(false);
|
obj->set_undetectable(false);
|
||||||
obj->set_needs_access_check(false);
|
obj->set_needs_access_check(false);
|
||||||
@ -1497,14 +1500,15 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
|||||||
Local<FunctionTemplate> FunctionTemplate::New(
|
Local<FunctionTemplate> FunctionTemplate::New(
|
||||||
Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
|
Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
|
||||||
v8::Local<Signature> signature, int length, ConstructorBehavior behavior,
|
v8::Local<Signature> signature, int length, ConstructorBehavior behavior,
|
||||||
SideEffectType side_effect_type) {
|
SideEffectType side_effect_type, const CFunction* c_function) {
|
||||||
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||||
// Changes to the environment cannot be captured in the snapshot. Expect no
|
// Changes to the environment cannot be captured in the snapshot. Expect no
|
||||||
// function templates when the isolate is created for serialization.
|
// function templates when the isolate is created for serialization.
|
||||||
LOG_API(i_isolate, FunctionTemplate, New);
|
LOG_API(i_isolate, FunctionTemplate, New);
|
||||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
|
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
|
||||||
auto templ = FunctionTemplateNew(i_isolate, callback, data, signature, length,
|
auto templ =
|
||||||
false, Local<Private>(), side_effect_type);
|
FunctionTemplateNew(i_isolate, callback, data, signature, length, false,
|
||||||
|
Local<Private>(), side_effect_type, c_function);
|
||||||
if (behavior == ConstructorBehavior::kThrow) templ->RemovePrototype();
|
if (behavior == ConstructorBehavior::kThrow) templ->RemovePrototype();
|
||||||
return templ;
|
return templ;
|
||||||
}
|
}
|
||||||
@ -1553,7 +1557,8 @@ Local<AccessorSignature> AccessorSignature::New(
|
|||||||
|
|
||||||
void FunctionTemplate::SetCallHandler(FunctionCallback callback,
|
void FunctionTemplate::SetCallHandler(FunctionCallback callback,
|
||||||
v8::Local<Value> data,
|
v8::Local<Value> data,
|
||||||
SideEffectType side_effect_type) {
|
SideEffectType side_effect_type,
|
||||||
|
const CFunction* c_function) {
|
||||||
auto info = Utils::OpenHandle(this);
|
auto info = Utils::OpenHandle(this);
|
||||||
EnsureNotInstantiated(info, "v8::FunctionTemplate::SetCallHandler");
|
EnsureNotInstantiated(info, "v8::FunctionTemplate::SetCallHandler");
|
||||||
i::Isolate* isolate = info->GetIsolate();
|
i::Isolate* isolate = info->GetIsolate();
|
||||||
@ -1567,6 +1572,15 @@ void FunctionTemplate::SetCallHandler(FunctionCallback callback,
|
|||||||
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
|
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
|
||||||
}
|
}
|
||||||
obj->set_data(*Utils::OpenHandle(*data));
|
obj->set_data(*Utils::OpenHandle(*data));
|
||||||
|
if (c_function != nullptr) {
|
||||||
|
DCHECK_NOT_NULL(c_function->GetAddress());
|
||||||
|
i::FunctionTemplateInfo::SetCFunction(
|
||||||
|
isolate, info,
|
||||||
|
i::handle(*FromCData(isolate, c_function->GetAddress()), isolate));
|
||||||
|
i::FunctionTemplateInfo::SetCSignature(
|
||||||
|
isolate, info,
|
||||||
|
i::handle(*FromCData(isolate, c_function->GetTypeInfo()), isolate));
|
||||||
|
}
|
||||||
info->set_call_code(*obj);
|
info->set_call_code(*obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10856,6 +10870,34 @@ void EmbedderHeapTracer::ResetHandleInNonTracingGC(
|
|||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const void* CTypeInfo::GetWrapperInfo() const {
|
||||||
|
DCHECK(payload_ & kWrapperTypeInfoMask);
|
||||||
|
return reinterpret_cast<const void*>(payload_ & kWrapperTypeInfoMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
CFunction::CFunction(const void* address, const CFunctionInfo* type_info)
|
||||||
|
: address_(address), type_info_(type_info) {
|
||||||
|
CHECK_NOT_NULL(address_);
|
||||||
|
CHECK_NOT_NULL(type_info_);
|
||||||
|
for (size_t i = 0; i < type_info_->ArgumentCount(); ++i) {
|
||||||
|
if (type_info_->ArgumentInfo()[i].IsArray()) {
|
||||||
|
// Array args require an integer passed for their length
|
||||||
|
// as the next argument.
|
||||||
|
DCHECK_LT(i + 1, type_info_->ArgumentCount());
|
||||||
|
switch (type_info_->ArgumentInfo()[i + 1].GetType()) {
|
||||||
|
case CTypeInfo::Type::kInt32:
|
||||||
|
case CTypeInfo::Type::kUint32:
|
||||||
|
case CTypeInfo::Type::kInt64:
|
||||||
|
case CTypeInfo::Type::kUint64:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
const size_t HandleScopeImplementer::kEnteredContextsOffset =
|
const size_t HandleScopeImplementer::kEnteredContextsOffset =
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
#include "src/objects/instance-type.h"
|
#include "src/objects/instance-type.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
|
class CFunctionInfo;
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
class BytecodeArray;
|
class BytecodeArray;
|
||||||
@ -659,6 +661,8 @@ class FunctionTemplateInfoRef : public HeapObjectRef {
|
|||||||
|
|
||||||
void SerializeCallCode();
|
void SerializeCallCode();
|
||||||
base::Optional<CallHandlerInfoRef> call_code() const;
|
base::Optional<CallHandlerInfoRef> call_code() const;
|
||||||
|
Address c_function() const;
|
||||||
|
const CFunctionInfo* c_signature() const;
|
||||||
|
|
||||||
HolderLookupResult LookupHolderOfExpectedType(
|
HolderLookupResult LookupHolderOfExpectedType(
|
||||||
MapRef receiver_map,
|
MapRef receiver_map,
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
#include "include/v8-fast-api-calls.h"
|
||||||
#include "src/api/api-inl.h"
|
#include "src/api/api-inl.h"
|
||||||
|
#include "src/base/small-vector.h"
|
||||||
#include "src/builtins/builtins-promise.h"
|
#include "src/builtins/builtins-promise.h"
|
||||||
#include "src/builtins/builtins-utils.h"
|
#include "src/builtins/builtins-utils.h"
|
||||||
#include "src/codegen/code-factory.h"
|
#include "src/codegen/code-factory.h"
|
||||||
@ -864,6 +866,88 @@ class PromiseBuiltinReducerAssembler : public JSCallReducerAssembler {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FastApiCallReducerAssembler : public JSCallReducerAssembler {
|
||||||
|
public:
|
||||||
|
FastApiCallReducerAssembler(JSGraph* jsgraph, Zone* zone, Node* node,
|
||||||
|
Address c_function,
|
||||||
|
const CFunctionInfo* c_signature)
|
||||||
|
: JSCallReducerAssembler(jsgraph, zone, node),
|
||||||
|
c_function_(c_function),
|
||||||
|
c_signature_(c_signature) {
|
||||||
|
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
|
||||||
|
DCHECK_NE(c_function_, kNullAddress);
|
||||||
|
CHECK_NOT_NULL(c_signature_);
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<Object> ReduceFastApiCall() {
|
||||||
|
int c_arg_count = c_signature_->ArgumentCount();
|
||||||
|
Node* function_node =
|
||||||
|
ExternalConstant(ExternalReference::Create(c_function_));
|
||||||
|
base::SmallVector<Node*, kInlineSize + kExtraInputsCount> inputs(0);
|
||||||
|
inputs.emplace_back(function_node);
|
||||||
|
int wrapper_object_index = isolate()->embedder_wrapper_object_index();
|
||||||
|
CHECK_GE(wrapper_object_index, 0);
|
||||||
|
for (int i = 0; i < c_arg_count; ++i) {
|
||||||
|
if (i + kFunctionArgCount < ValueInputCount()) {
|
||||||
|
inputs.emplace_back(ConvertArgumentIfJSWrapper(
|
||||||
|
c_signature_->ArgumentInfo()[i].GetType(),
|
||||||
|
ValueInput(i + kFunctionArgCount), wrapper_object_index));
|
||||||
|
} else {
|
||||||
|
inputs.emplace_back(UndefinedConstant());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inputs.emplace_back(effect());
|
||||||
|
inputs.emplace_back(control());
|
||||||
|
|
||||||
|
return FastApiCall(inputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr int kFunctionArgCount = 1;
|
||||||
|
static constexpr int kExtraInputsCount =
|
||||||
|
kFunctionArgCount + 2; // effect, control
|
||||||
|
static constexpr int kInlineSize = 10;
|
||||||
|
|
||||||
|
TNode<Object> FastApiCall(
|
||||||
|
base::SmallVector<Node*, kInlineSize + kExtraInputsCount> const& inputs) {
|
||||||
|
return AddNode<Object>(
|
||||||
|
graph()->NewNode(simplified()->FastApiCall(c_signature_, feedback()),
|
||||||
|
static_cast<int>(inputs.size()), inputs.begin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<RawPtrT> UnwrapApiObject(TNode<JSObject> node,
|
||||||
|
int wrapper_object_index) {
|
||||||
|
const int offset =
|
||||||
|
Internals::kJSObjectHeaderSize +
|
||||||
|
(Internals::kEmbedderDataSlotSize * wrapper_object_index);
|
||||||
|
|
||||||
|
FieldAccess access(kTaggedBase, offset, MaybeHandle<Name>(),
|
||||||
|
MaybeHandle<Map>(), Type::Any(), MachineType::Pointer(),
|
||||||
|
WriteBarrierKind::kNoWriteBarrier);
|
||||||
|
TNode<RawPtrT> load = AddNode<RawPtrT>(graph()->NewNode(
|
||||||
|
simplified()->LoadField(access), node, effect(), control()));
|
||||||
|
return load;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* ConvertArgumentIfJSWrapper(CTypeInfo::Type type, TNode<Object> node,
|
||||||
|
int wrapper_object_index) {
|
||||||
|
switch (type) {
|
||||||
|
case CTypeInfo::Type::kUnwrappedApiObject:
|
||||||
|
// This call assumes that {node} is a JSObject with an internal field
|
||||||
|
// set to a C pointer. It should fail in all other cases.
|
||||||
|
// TODO(mslekova): Implement instanceOf check for the C pointer type.
|
||||||
|
// TODO(mslekova): Introduce a GraphAssembler primitive for safe cast.
|
||||||
|
return UnwrapApiObject(TNode<JSObject>::UncheckedCast(node),
|
||||||
|
wrapper_object_index);
|
||||||
|
default:
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Address c_function_;
|
||||||
|
const CFunctionInfo* const c_signature_;
|
||||||
|
};
|
||||||
|
|
||||||
TNode<Number> JSCallReducerAssembler::SpeculativeToNumber(
|
TNode<Number> JSCallReducerAssembler::SpeculativeToNumber(
|
||||||
TNode<Object> value, NumberOperationHint hint) {
|
TNode<Object> value, NumberOperationHint hint) {
|
||||||
return AddNode<Number>(
|
return AddNode<Number>(
|
||||||
@ -3368,8 +3452,9 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
|||||||
// See if we can constant-fold the compatible receiver checks.
|
// See if we can constant-fold the compatible receiver checks.
|
||||||
HolderLookupResult api_holder =
|
HolderLookupResult api_holder =
|
||||||
function_template_info.LookupHolderOfExpectedType(first_receiver_map);
|
function_template_info.LookupHolderOfExpectedType(first_receiver_map);
|
||||||
if (api_holder.lookup == CallOptimization::kHolderNotFound)
|
if (api_holder.lookup == CallOptimization::kHolderNotFound) {
|
||||||
return inference.NoChange();
|
return inference.NoChange();
|
||||||
|
}
|
||||||
|
|
||||||
// Check that all {receiver_maps} are actually JSReceiver maps and
|
// Check that all {receiver_maps} are actually JSReceiver maps and
|
||||||
// that the {function_template_info} accepts them without access
|
// that the {function_template_info} accepts them without access
|
||||||
@ -3471,6 +3556,18 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
|||||||
<< function_template_info);
|
<< function_template_info);
|
||||||
return NoChange();
|
return NoChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Address c_function = function_template_info.c_function();
|
||||||
|
|
||||||
|
if (FLAG_turbo_fast_api_calls && c_function != kNullAddress) {
|
||||||
|
const CFunctionInfo* c_signature = function_template_info.c_signature();
|
||||||
|
FastApiCallReducerAssembler a(jsgraph(), graph()->zone(), node, c_function,
|
||||||
|
c_signature);
|
||||||
|
Node* c_call = a.ReduceFastApiCall();
|
||||||
|
ReplaceWithSubgraph(&a, c_call);
|
||||||
|
return Replace(c_call);
|
||||||
|
}
|
||||||
|
|
||||||
CallHandlerInfoRef call_handler_info = *function_template_info.call_code();
|
CallHandlerInfoRef call_handler_info = *function_template_info.call_code();
|
||||||
Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
|
Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
|
||||||
CallInterfaceDescriptor cid = call_api_callback.descriptor();
|
CallInterfaceDescriptor cid = call_api_callback.descriptor();
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "include/v8-fast-api-calls.h"
|
||||||
#include "src/api/api-inl.h"
|
#include "src/api/api-inl.h"
|
||||||
#include "src/ast/modules.h"
|
#include "src/ast/modules.h"
|
||||||
#include "src/codegen/code-factory.h"
|
#include "src/codegen/code-factory.h"
|
||||||
@ -226,6 +227,8 @@ class FunctionTemplateInfoData : public HeapObjectData {
|
|||||||
|
|
||||||
void SerializeCallCode(JSHeapBroker* broker);
|
void SerializeCallCode(JSHeapBroker* broker);
|
||||||
CallHandlerInfoData* call_code() const { return call_code_; }
|
CallHandlerInfoData* call_code() const { return call_code_; }
|
||||||
|
Address c_function() const { return c_function_; }
|
||||||
|
const CFunctionInfo* c_signature() const { return c_signature_; }
|
||||||
KnownReceiversMap& known_receivers() { return known_receivers_; }
|
KnownReceiversMap& known_receivers() { return known_receivers_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -234,6 +237,8 @@ class FunctionTemplateInfoData : public HeapObjectData {
|
|||||||
bool has_call_code_ = false;
|
bool has_call_code_ = false;
|
||||||
|
|
||||||
CallHandlerInfoData* call_code_ = nullptr;
|
CallHandlerInfoData* call_code_ = nullptr;
|
||||||
|
const Address c_function_;
|
||||||
|
const CFunctionInfo* const c_signature_;
|
||||||
KnownReceiversMap known_receivers_;
|
KnownReceiversMap known_receivers_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -257,6 +262,8 @@ FunctionTemplateInfoData::FunctionTemplateInfoData(
|
|||||||
JSHeapBroker* broker, ObjectData** storage,
|
JSHeapBroker* broker, ObjectData** storage,
|
||||||
Handle<FunctionTemplateInfo> object)
|
Handle<FunctionTemplateInfo> object)
|
||||||
: HeapObjectData(broker, storage, object),
|
: HeapObjectData(broker, storage, object),
|
||||||
|
c_function_(v8::ToCData<Address>(object->GetCFunction())),
|
||||||
|
c_signature_(v8::ToCData<CFunctionInfo*>(object->GetCSignature())),
|
||||||
known_receivers_(broker->zone()) {
|
known_receivers_(broker->zone()) {
|
||||||
auto function_template_info = Handle<FunctionTemplateInfo>::cast(object);
|
auto function_template_info = Handle<FunctionTemplateInfo>::cast(object);
|
||||||
is_signature_undefined_ =
|
is_signature_undefined_ =
|
||||||
@ -3676,6 +3683,20 @@ Address CallHandlerInfoRef::callback() const {
|
|||||||
return HeapObjectRef::data()->AsCallHandlerInfo()->callback();
|
return HeapObjectRef::data()->AsCallHandlerInfo()->callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Address FunctionTemplateInfoRef::c_function() const {
|
||||||
|
if (broker()->mode() == JSHeapBroker::kDisabled) {
|
||||||
|
return v8::ToCData<Address>(object()->GetCFunction());
|
||||||
|
}
|
||||||
|
return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_function();
|
||||||
|
}
|
||||||
|
|
||||||
|
const CFunctionInfo* FunctionTemplateInfoRef::c_signature() const {
|
||||||
|
if (broker()->mode() == JSHeapBroker::kDisabled) {
|
||||||
|
return v8::ToCData<CFunctionInfo*>(object()->GetCSignature());
|
||||||
|
}
|
||||||
|
return HeapObjectRef::data()->AsFunctionTemplateInfo()->c_signature();
|
||||||
|
}
|
||||||
|
|
||||||
bool StringRef::IsSeqString() const {
|
bool StringRef::IsSeqString() const {
|
||||||
IF_ACCESS_FROM_HEAP_C(String, IsSeqString);
|
IF_ACCESS_FROM_HEAP_C(String, IsSeqString);
|
||||||
return data()->AsString()->is_seq_string();
|
return data()->AsString()->is_seq_string();
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include "src/zone/zone.h"
|
#include "src/zone/zone.h"
|
||||||
|
|
||||||
namespace v8 {
|
namespace v8 {
|
||||||
|
class CFunctionInfo;
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
class CallInterfaceDescriptor;
|
class CallInterfaceDescriptor;
|
||||||
@ -357,6 +359,13 @@ class V8_EXPORT_PRIVATE CallDescriptor final
|
|||||||
return allocatable_registers_ != 0;
|
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:
|
private:
|
||||||
friend class Linkage;
|
friend class Linkage;
|
||||||
|
|
||||||
@ -374,6 +383,7 @@ class V8_EXPORT_PRIVATE CallDescriptor final
|
|||||||
const RegList allocatable_registers_;
|
const RegList allocatable_registers_;
|
||||||
const Flags flags_;
|
const Flags flags_;
|
||||||
const char* const debug_name_;
|
const char* const debug_name_;
|
||||||
|
const CFunctionInfo* c_function_info_ = nullptr;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CallDescriptor);
|
DISALLOW_COPY_AND_ASSIGN(CallDescriptor);
|
||||||
};
|
};
|
||||||
|
@ -475,7 +475,8 @@
|
|||||||
V(PoisonIndex) \
|
V(PoisonIndex) \
|
||||||
V(RuntimeAbort) \
|
V(RuntimeAbort) \
|
||||||
V(AssertType) \
|
V(AssertType) \
|
||||||
V(DateNow)
|
V(DateNow) \
|
||||||
|
V(FastApiCall)
|
||||||
|
|
||||||
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) \
|
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) \
|
||||||
V(SpeculativeBigIntAdd) \
|
V(SpeculativeBigIntAdd) \
|
||||||
|
@ -889,12 +889,12 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
|
|||||||
if (use_info.type_check() == TypeCheckKind::kSignedSmall ||
|
if (use_info.type_check() == TypeCheckKind::kSignedSmall ||
|
||||||
use_info.type_check() == TypeCheckKind::kSigned32 ||
|
use_info.type_check() == TypeCheckKind::kSigned32 ||
|
||||||
use_info.type_check() == TypeCheckKind::kArrayIndex) {
|
use_info.type_check() == TypeCheckKind::kArrayIndex) {
|
||||||
bool indentify_zeros = use_info.truncation().IdentifiesZeroAndMinusZero();
|
bool identify_zeros = use_info.truncation().IdentifiesZeroAndMinusZero();
|
||||||
if (output_type.Is(Type::Signed32()) ||
|
if (output_type.Is(Type::Signed32()) ||
|
||||||
(indentify_zeros && output_type.Is(Type::Signed32OrMinusZero()))) {
|
(identify_zeros && output_type.Is(Type::Signed32OrMinusZero()))) {
|
||||||
return node;
|
return node;
|
||||||
} else if (output_type.Is(Type::Unsigned32()) ||
|
} else if (output_type.Is(Type::Unsigned32()) ||
|
||||||
(indentify_zeros &&
|
(identify_zeros &&
|
||||||
output_type.Is(Type::Unsigned32OrMinusZero()))) {
|
output_type.Is(Type::Unsigned32OrMinusZero()))) {
|
||||||
op = simplified()->CheckedUint32ToInt32(use_info.feedback());
|
op = simplified()->CheckedUint32ToInt32(use_info.feedback());
|
||||||
} else {
|
} else {
|
||||||
|
@ -196,6 +196,9 @@ class UseInfo {
|
|||||||
static UseInfo Float32() {
|
static UseInfo Float32() {
|
||||||
return UseInfo(MachineRepresentation::kFloat32, Truncation::Any());
|
return UseInfo(MachineRepresentation::kFloat32, Truncation::Any());
|
||||||
}
|
}
|
||||||
|
static UseInfo Float64() {
|
||||||
|
return UseInfo(MachineRepresentation::kFloat64, Truncation::Any());
|
||||||
|
}
|
||||||
static UseInfo TruncatingFloat64(
|
static UseInfo TruncatingFloat64(
|
||||||
IdentifyZeros identify_zeros = kDistinguishZeros) {
|
IdentifyZeros identify_zeros = kDistinguishZeros) {
|
||||||
return UseInfo(MachineRepresentation::kFloat64,
|
return UseInfo(MachineRepresentation::kFloat64,
|
||||||
|
@ -6,8 +6,11 @@
|
|||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
|
#include "include/v8-fast-api-calls.h"
|
||||||
#include "src/base/bits.h"
|
#include "src/base/bits.h"
|
||||||
|
#include "src/base/small-vector.h"
|
||||||
#include "src/codegen/code-factory.h"
|
#include "src/codegen/code-factory.h"
|
||||||
|
#include "src/codegen/machine-type.h"
|
||||||
#include "src/codegen/tick-counter.h"
|
#include "src/codegen/tick-counter.h"
|
||||||
#include "src/compiler/access-builder.h"
|
#include "src/compiler/access-builder.h"
|
||||||
#include "src/compiler/common-operator.h"
|
#include "src/compiler/common-operator.h"
|
||||||
@ -1695,6 +1698,102 @@ class RepresentationSelector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MachineType MachineTypeFor(CTypeInfo::Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case CTypeInfo::Type::kVoid:
|
||||||
|
return MachineType::Int32();
|
||||||
|
case CTypeInfo::Type::kBool:
|
||||||
|
return MachineType::Bool();
|
||||||
|
case CTypeInfo::Type::kInt32:
|
||||||
|
return MachineType::Int32();
|
||||||
|
case CTypeInfo::Type::kUint32:
|
||||||
|
return MachineType::Uint32();
|
||||||
|
case CTypeInfo::Type::kInt64:
|
||||||
|
return MachineType::Int64();
|
||||||
|
case CTypeInfo::Type::kUint64:
|
||||||
|
return MachineType::Uint64();
|
||||||
|
case CTypeInfo::Type::kFloat32:
|
||||||
|
return MachineType::Float32();
|
||||||
|
case CTypeInfo::Type::kFloat64:
|
||||||
|
return MachineType::Float64();
|
||||||
|
case CTypeInfo::Type::kUnwrappedApiObject:
|
||||||
|
return MachineType::Pointer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UseInfo UseInfoForFastApiCallArgument(CTypeInfo::Type type,
|
||||||
|
FeedbackSource const& feedback) {
|
||||||
|
switch (type) {
|
||||||
|
case CTypeInfo::Type::kVoid:
|
||||||
|
UNREACHABLE();
|
||||||
|
case CTypeInfo::Type::kBool:
|
||||||
|
return UseInfo::Bool();
|
||||||
|
case CTypeInfo::Type::kInt32:
|
||||||
|
case CTypeInfo::Type::kUint32:
|
||||||
|
case CTypeInfo::Type::kFloat32:
|
||||||
|
return UseInfo::CheckedNumberAsWord32(feedback);
|
||||||
|
case CTypeInfo::Type::kInt64:
|
||||||
|
return UseInfo::CheckedSigned64AsWord64(kIdentifyZeros, feedback);
|
||||||
|
case CTypeInfo::Type::kFloat64:
|
||||||
|
return UseInfo::CheckedNumberAsFloat64(kIdentifyZeros, feedback);
|
||||||
|
// UseInfo::Word64 does not propagate any TypeCheckKind, so it relies
|
||||||
|
// on the implicit assumption that Word64 representation only holds
|
||||||
|
// Numbers, which is already no longer true with BigInts. By now,
|
||||||
|
// BigInts are handled in a very conservative way to make sure they don't
|
||||||
|
// fall into that pit, but future changes may break this here.
|
||||||
|
case CTypeInfo::Type::kUint64:
|
||||||
|
return UseInfo::Word64();
|
||||||
|
case CTypeInfo::Type::kUnwrappedApiObject:
|
||||||
|
return UseInfo::Word();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr int kInitialArgumentsCount = 10;
|
||||||
|
|
||||||
|
void VisitFastApiCall(Node* node) {
|
||||||
|
FastApiCallParameters const& params = FastApiCallParametersOf(node->op());
|
||||||
|
const CFunctionInfo* c_signature = params.signature();
|
||||||
|
int c_arg_count = c_signature->ArgumentCount();
|
||||||
|
int value_input_count = node->op()->ValueInputCount();
|
||||||
|
// function, ... C args
|
||||||
|
CHECK_EQ(c_arg_count + 1, value_input_count);
|
||||||
|
|
||||||
|
base::SmallVector<UseInfo, kInitialArgumentsCount> arg_use_info(
|
||||||
|
value_input_count);
|
||||||
|
arg_use_info[0] = UseInfo::Word();
|
||||||
|
// Propagate representation information from TypeInfo.
|
||||||
|
for (int i = 0; i < value_input_count; i++) {
|
||||||
|
arg_use_info[i] = UseInfoForFastApiCallArgument(
|
||||||
|
c_signature->ArgumentInfo()[i - 1].GetType(), params.feedback());
|
||||||
|
ProcessInput(node, i, arg_use_info[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
MachineType return_type =
|
||||||
|
MachineTypeFor(c_signature->ReturnInfo().GetType());
|
||||||
|
SetOutput(node, return_type.representation());
|
||||||
|
|
||||||
|
if (lower()) {
|
||||||
|
MachineSignature::Builder builder(graph()->zone(), 1, c_arg_count);
|
||||||
|
builder.AddReturn(return_type);
|
||||||
|
for (int i = 0; i < c_arg_count; ++i) {
|
||||||
|
MachineType machine_type =
|
||||||
|
MachineTypeFor(c_signature->ArgumentInfo()[i].GetType());
|
||||||
|
// Here the arg_use_info are indexed starting from 1 because of the
|
||||||
|
// function input, while this loop is only over the actual arguments.
|
||||||
|
DCHECK_EQ(arg_use_info[i + 1].representation(),
|
||||||
|
machine_type.representation());
|
||||||
|
builder.AddParam(machine_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
CallDescriptor* call_descriptor = Linkage::GetSimplifiedCDescriptor(
|
||||||
|
graph()->zone(), builder.Build(), CallDescriptor::kNoFlags);
|
||||||
|
|
||||||
|
call_descriptor->SetCFunctionInfo(c_signature);
|
||||||
|
|
||||||
|
NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Dispatching routine for visiting the node {node} with the usage {use}.
|
// Dispatching routine for visiting the node {node} with the usage {use}.
|
||||||
// Depending on the operator, propagate new usage info to the inputs.
|
// Depending on the operator, propagate new usage info to the inputs.
|
||||||
void VisitNode(Node* node, Truncation truncation,
|
void VisitNode(Node* node, Truncation truncation,
|
||||||
@ -3557,6 +3656,11 @@ class RepresentationSelector {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case IrOpcode::kFastApiCall: {
|
||||||
|
VisitFastApiCall(node);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Operators with all inputs tagged and no or tagged output have uniform
|
// Operators with all inputs tagged and no or tagged output have uniform
|
||||||
// handling.
|
// handling.
|
||||||
case IrOpcode::kEnd:
|
case IrOpcode::kEnd:
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "src/compiler/simplified-operator.h"
|
#include "src/compiler/simplified-operator.h"
|
||||||
|
|
||||||
|
#include "include/v8-fast-api-calls.h"
|
||||||
#include "src/base/lazy-instance.h"
|
#include "src/base/lazy-instance.h"
|
||||||
#include "src/compiler/opcodes.h"
|
#include "src/compiler/opcodes.h"
|
||||||
#include "src/compiler/operator.h"
|
#include "src/compiler/operator.h"
|
||||||
@ -1256,6 +1257,16 @@ const Operator* SimplifiedOperatorBuilder::AssertType(Type type) {
|
|||||||
"AssertType", 1, 0, 0, 1, 0, 0, type);
|
"AssertType", 1, 0, 0, 1, 0, 0, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Operator* SimplifiedOperatorBuilder::FastApiCall(
|
||||||
|
const CFunctionInfo* signature, FeedbackSource const& feedback) {
|
||||||
|
// function, c args
|
||||||
|
int value_input_count = signature->ArgumentCount() + 1;
|
||||||
|
return new (zone()) Operator1<FastApiCallParameters>(
|
||||||
|
IrOpcode::kFastApiCall, Operator::kNoThrow, "FastApiCall",
|
||||||
|
value_input_count, 1, 1, 1, 1, 0,
|
||||||
|
FastApiCallParameters(signature, feedback));
|
||||||
|
}
|
||||||
|
|
||||||
const Operator* SimplifiedOperatorBuilder::CheckIf(
|
const Operator* SimplifiedOperatorBuilder::CheckIf(
|
||||||
DeoptimizeReason reason, const FeedbackSource& feedback) {
|
DeoptimizeReason reason, const FeedbackSource& feedback) {
|
||||||
if (!feedback.IsValid()) {
|
if (!feedback.IsValid()) {
|
||||||
@ -1679,6 +1690,25 @@ int NewArgumentsElementsMappedCountOf(const Operator* op) {
|
|||||||
return OpParameter<int>(op);
|
return OpParameter<int>(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FastApiCallParameters const& FastApiCallParametersOf(const Operator* op) {
|
||||||
|
DCHECK_EQ(IrOpcode::kFastApiCall, op->opcode());
|
||||||
|
return OpParameter<FastApiCallParameters>(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, FastApiCallParameters const& p) {
|
||||||
|
return os << p.signature() << ", " << p.feedback();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t hash_value(FastApiCallParameters const& p) {
|
||||||
|
return base::hash_combine(p.signature(),
|
||||||
|
FeedbackSource::Hash()(p.feedback()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(FastApiCallParameters const& lhs,
|
||||||
|
FastApiCallParameters const& rhs) {
|
||||||
|
return lhs.signature() == rhs.signature() && lhs.feedback() == rhs.feedback();
|
||||||
|
}
|
||||||
|
|
||||||
const Operator* SimplifiedOperatorBuilder::Allocate(Type type,
|
const Operator* SimplifiedOperatorBuilder::Allocate(Type type,
|
||||||
AllocationType allocation) {
|
AllocationType allocation) {
|
||||||
return new (zone()) Operator1<AllocateParameters>(
|
return new (zone()) Operator1<AllocateParameters>(
|
||||||
|
@ -580,6 +580,30 @@ DeoptimizeReason DeoptimizeReasonOf(const Operator* op) V8_WARN_UNUSED_RESULT;
|
|||||||
|
|
||||||
int NewArgumentsElementsMappedCountOf(const Operator* op) V8_WARN_UNUSED_RESULT;
|
int NewArgumentsElementsMappedCountOf(const Operator* op) V8_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
class FastApiCallParameters {
|
||||||
|
public:
|
||||||
|
explicit FastApiCallParameters(const CFunctionInfo* signature,
|
||||||
|
FeedbackSource const& feedback)
|
||||||
|
: signature_(signature), feedback_(feedback) {}
|
||||||
|
|
||||||
|
const CFunctionInfo* signature() const { return signature_; }
|
||||||
|
FeedbackSource const& feedback() const { return feedback_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const CFunctionInfo* signature_;
|
||||||
|
const FeedbackSource feedback_;
|
||||||
|
};
|
||||||
|
|
||||||
|
FastApiCallParameters const& FastApiCallParametersOf(const Operator* op)
|
||||||
|
V8_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&,
|
||||||
|
FastApiCallParameters const&);
|
||||||
|
|
||||||
|
size_t hash_value(FastApiCallParameters const&);
|
||||||
|
|
||||||
|
bool operator==(FastApiCallParameters const&, FastApiCallParameters const&);
|
||||||
|
|
||||||
// Interface for building simplified operators, which represent the
|
// Interface for building simplified operators, which represent the
|
||||||
// medium-level operations of V8, including adding numbers, allocating objects,
|
// medium-level operations of V8, including adding numbers, allocating objects,
|
||||||
// indexing into objects and arrays, etc.
|
// indexing into objects and arrays, etc.
|
||||||
@ -920,6 +944,10 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
|||||||
|
|
||||||
const Operator* DateNow();
|
const Operator* DateNow();
|
||||||
|
|
||||||
|
// Stores the signature and feedback of a fast C call
|
||||||
|
const Operator* FastApiCall(const CFunctionInfo* signature,
|
||||||
|
FeedbackSource const& feedback);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Zone* zone() const { return zone_; }
|
Zone* zone() const { return zone_; }
|
||||||
|
|
||||||
|
@ -1010,6 +1010,8 @@ Type Typer::Visitor::TypeTypedObjectState(Node* node) {
|
|||||||
|
|
||||||
Type Typer::Visitor::TypeCall(Node* node) { return Type::Any(); }
|
Type Typer::Visitor::TypeCall(Node* node) { return Type::Any(); }
|
||||||
|
|
||||||
|
Type Typer::Visitor::TypeFastApiCall(Node* node) { return Type::Any(); }
|
||||||
|
|
||||||
Type Typer::Visitor::TypeProjection(Node* node) {
|
Type Typer::Visitor::TypeProjection(Node* node) {
|
||||||
Type const type = Operand(node, 0);
|
Type const type = Operand(node, 0);
|
||||||
if (type.Is(Type::None())) return Type::None();
|
if (type.Is(Type::None())) return Type::None();
|
||||||
|
@ -1529,7 +1529,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
|||||||
// Object -> fieldtype
|
// Object -> fieldtype
|
||||||
// TODO(rossberg): activate once machine ops are typed.
|
// TODO(rossberg): activate once machine ops are typed.
|
||||||
// CheckValueInputIs(node, 0, Type::Object());
|
// CheckValueInputIs(node, 0, Type::Object());
|
||||||
// CheckTypeIs(node, FieldAccessOf(node->op()).type));
|
// CheckTypeIs(node, FieldAccessOf(node->op()).type);
|
||||||
break;
|
break;
|
||||||
case IrOpcode::kLoadElement:
|
case IrOpcode::kLoadElement:
|
||||||
case IrOpcode::kLoadStackArgument:
|
case IrOpcode::kLoadStackArgument:
|
||||||
@ -1539,7 +1539,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
|||||||
// CheckTypeIs(node, ElementAccessOf(node->op()).type));
|
// CheckTypeIs(node, ElementAccessOf(node->op()).type));
|
||||||
break;
|
break;
|
||||||
case IrOpcode::kLoadFromObject:
|
case IrOpcode::kLoadFromObject:
|
||||||
// TODO(gsps): Can we check some types here?
|
CheckValueInputIs(node, 0, Type::Receiver());
|
||||||
break;
|
break;
|
||||||
case IrOpcode::kLoadTypedElement:
|
case IrOpcode::kLoadTypedElement:
|
||||||
break;
|
break;
|
||||||
@ -1599,6 +1599,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
|||||||
CheckValueInputIs(node, 0, Type::Any());
|
CheckValueInputIs(node, 0, Type::Any());
|
||||||
CheckTypeIs(node, Type::BigInt());
|
CheckTypeIs(node, Type::BigInt());
|
||||||
break;
|
break;
|
||||||
|
case IrOpcode::kFastApiCall:
|
||||||
|
CHECK_GE(value_count, 1);
|
||||||
|
CheckValueInputIs(node, 0, Type::ExternalPointer());
|
||||||
|
break;
|
||||||
|
|
||||||
// Machine operators
|
// Machine operators
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
@ -437,7 +437,9 @@ using DebugObjectCache = std::vector<Handle<HeapObject>>;
|
|||||||
V(v8_inspector::V8Inspector*, inspector, nullptr) \
|
V(v8_inspector::V8Inspector*, inspector, nullptr) \
|
||||||
V(bool, next_v8_call_is_safe_for_termination, false) \
|
V(bool, next_v8_call_is_safe_for_termination, false) \
|
||||||
V(bool, only_terminate_in_safe_scope, false) \
|
V(bool, only_terminate_in_safe_scope, false) \
|
||||||
V(bool, detailed_source_positions_for_profiling, FLAG_detailed_line_info)
|
V(bool, detailed_source_positions_for_profiling, FLAG_detailed_line_info) \
|
||||||
|
V(int, embedder_wrapper_type_index, -1) \
|
||||||
|
V(int, embedder_wrapper_object_index, -1)
|
||||||
|
|
||||||
#define THREAD_LOCAL_TOP_ACCESSOR(type, name) \
|
#define THREAD_LOCAL_TOP_ACCESSOR(type, name) \
|
||||||
inline void set_##name(type v) { thread_local_top()->name##_ = v; } \
|
inline void set_##name(type v) { thread_local_top()->name##_ = v; } \
|
||||||
|
@ -638,6 +638,7 @@ DEFINE_BOOL(turbo_rewrite_far_jumps, true,
|
|||||||
DEFINE_BOOL(
|
DEFINE_BOOL(
|
||||||
stress_gc_during_compilation, false,
|
stress_gc_during_compilation, false,
|
||||||
"simulate GC/compiler thread race related to https://crbug.com/v8/8520")
|
"simulate GC/compiler thread race related to https://crbug.com/v8/8520")
|
||||||
|
DEFINE_BOOL(turbo_fast_api_calls, false, "enable fast API calls from TurboFan")
|
||||||
|
|
||||||
// Favor memory over execution speed.
|
// Favor memory over execution speed.
|
||||||
DEFINE_BOOL(optimize_for_size, false,
|
DEFINE_BOOL(optimize_for_size, false,
|
||||||
|
@ -1319,6 +1319,8 @@ FunctionTemplateRareData FunctionTemplateInfo::AllocateFunctionTemplateRareData(
|
|||||||
FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld);
|
FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld);
|
||||||
Handle<FunctionTemplateRareData> rare_data =
|
Handle<FunctionTemplateRareData> rare_data =
|
||||||
i::Handle<FunctionTemplateRareData>::cast(struct_obj);
|
i::Handle<FunctionTemplateRareData>::cast(struct_obj);
|
||||||
|
rare_data->set_c_function(Smi(0));
|
||||||
|
rare_data->set_c_signature(Smi(0));
|
||||||
function_template_info->set_rare_data(*rare_data);
|
function_template_info->set_rare_data(*rare_data);
|
||||||
return *rare_data;
|
return *rare_data;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,8 @@ extern class FunctionTemplateRareData extends Struct {
|
|||||||
instance_template: Object;
|
instance_template: Object;
|
||||||
instance_call_handler: Object;
|
instance_call_handler: Object;
|
||||||
access_check_info: Object;
|
access_check_info: Object;
|
||||||
|
c_function: Foreign|Smi;
|
||||||
|
c_signature: Foreign|Smi;
|
||||||
}
|
}
|
||||||
|
|
||||||
@generateCppClass
|
@generateCppClass
|
||||||
|
@ -50,11 +50,11 @@ FunctionTemplateRareData FunctionTemplateInfo::EnsureFunctionTemplateRareData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define RARE_ACCESSORS(Name, CamelName, Type) \
|
#define RARE_ACCESSORS(Name, CamelName, Type, Default) \
|
||||||
DEF_GETTER(FunctionTemplateInfo, Get##CamelName, Type) { \
|
DEF_GETTER(FunctionTemplateInfo, Get##CamelName, Type) { \
|
||||||
HeapObject extra = rare_data(isolate); \
|
HeapObject extra = rare_data(isolate); \
|
||||||
HeapObject undefined = GetReadOnlyRoots(isolate).undefined_value(); \
|
HeapObject undefined = GetReadOnlyRoots(isolate).undefined_value(); \
|
||||||
return extra == undefined ? undefined \
|
return extra == undefined ? Default \
|
||||||
: FunctionTemplateRareData::cast(extra).Name(); \
|
: FunctionTemplateRareData::cast(extra).Name(); \
|
||||||
} \
|
} \
|
||||||
inline void FunctionTemplateInfo::Set##CamelName( \
|
inline void FunctionTemplateInfo::Set##CamelName( \
|
||||||
@ -65,14 +65,18 @@ FunctionTemplateRareData FunctionTemplateInfo::EnsureFunctionTemplateRareData(
|
|||||||
rare_data.set_##Name(*Name); \
|
rare_data.set_##Name(*Name); \
|
||||||
}
|
}
|
||||||
|
|
||||||
RARE_ACCESSORS(prototype_template, PrototypeTemplate, Object)
|
RARE_ACCESSORS(prototype_template, PrototypeTemplate, Object, undefined)
|
||||||
RARE_ACCESSORS(prototype_provider_template, PrototypeProviderTemplate, Object)
|
RARE_ACCESSORS(prototype_provider_template, PrototypeProviderTemplate, Object,
|
||||||
RARE_ACCESSORS(parent_template, ParentTemplate, Object)
|
undefined)
|
||||||
RARE_ACCESSORS(named_property_handler, NamedPropertyHandler, Object)
|
RARE_ACCESSORS(parent_template, ParentTemplate, Object, undefined)
|
||||||
RARE_ACCESSORS(indexed_property_handler, IndexedPropertyHandler, Object)
|
RARE_ACCESSORS(named_property_handler, NamedPropertyHandler, Object, undefined)
|
||||||
RARE_ACCESSORS(instance_template, InstanceTemplate, Object)
|
RARE_ACCESSORS(indexed_property_handler, IndexedPropertyHandler, Object,
|
||||||
RARE_ACCESSORS(instance_call_handler, InstanceCallHandler, Object)
|
undefined)
|
||||||
RARE_ACCESSORS(access_check_info, AccessCheckInfo, Object)
|
RARE_ACCESSORS(instance_template, InstanceTemplate, Object, undefined)
|
||||||
|
RARE_ACCESSORS(instance_call_handler, InstanceCallHandler, Object, undefined)
|
||||||
|
RARE_ACCESSORS(access_check_info, AccessCheckInfo, Object, undefined)
|
||||||
|
RARE_ACCESSORS(c_function, CFunction, Object, Smi(0))
|
||||||
|
RARE_ACCESSORS(c_signature, CSignature, Object, Smi(0))
|
||||||
#undef RARE_ACCESSORS
|
#undef RARE_ACCESSORS
|
||||||
|
|
||||||
bool FunctionTemplateInfo::instantiated() {
|
bool FunctionTemplateInfo::instantiated() {
|
||||||
|
@ -82,6 +82,9 @@ class FunctionTemplateInfo
|
|||||||
DECL_RARE_ACCESSORS(instance_call_handler, InstanceCallHandler, Object)
|
DECL_RARE_ACCESSORS(instance_call_handler, InstanceCallHandler, Object)
|
||||||
|
|
||||||
DECL_RARE_ACCESSORS(access_check_info, AccessCheckInfo, Object)
|
DECL_RARE_ACCESSORS(access_check_info, AccessCheckInfo, Object)
|
||||||
|
|
||||||
|
DECL_RARE_ACCESSORS(c_function, CFunction, Object)
|
||||||
|
DECL_RARE_ACCESSORS(c_signature, CSignature, Object)
|
||||||
#undef DECL_RARE_ACCESSORS
|
#undef DECL_RARE_ACCESSORS
|
||||||
|
|
||||||
// Internal field to store a flag bitfield.
|
// Internal field to store a flag bitfield.
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <unistd.h> // NOLINT
|
#include <unistd.h> // NOLINT
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "include/v8-fast-api-calls.h"
|
||||||
#include "include/v8-util.h"
|
#include "include/v8-util.h"
|
||||||
#include "src/api/api-inl.h"
|
#include "src/api/api-inl.h"
|
||||||
#include "src/base/overflowing-math.h"
|
#include "src/base/overflowing-math.h"
|
||||||
@ -27013,3 +27014,324 @@ UNINITIALIZED_TEST(NestedIsolates) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#undef THREADED_PROFILED_TEST
|
#undef THREADED_PROFILED_TEST
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// The following should correspond to Chromium's kV8DOMWrapperObjectIndex.
|
||||||
|
static const int kV8WrapperTypeIndex = 0;
|
||||||
|
static const int kV8WrapperObjectIndex = 1;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct GetDeoptValue {
|
||||||
|
static Maybe<T> Get(v8::Local<v8::Value> value,
|
||||||
|
v8::Local<v8::Context> context);
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct GetDeoptValue<int32_t> {
|
||||||
|
static Maybe<int32_t> Get(v8::Local<v8::Value> value,
|
||||||
|
v8::Local<v8::Context> context) {
|
||||||
|
return value->Int32Value(context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct GetDeoptValue<uint32_t> {
|
||||||
|
static Maybe<uint32_t> Get(v8::Local<v8::Value> value,
|
||||||
|
v8::Local<v8::Context> context) {
|
||||||
|
return value->Uint32Value(context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct GetDeoptValue<int64_t> {
|
||||||
|
static Maybe<int64_t> Get(v8::Local<v8::Value> value,
|
||||||
|
v8::Local<v8::Context> context) {
|
||||||
|
return value->IntegerValue(context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct GetDeoptValue<bool> {
|
||||||
|
static Maybe<bool> Get(v8::Local<v8::Value> value,
|
||||||
|
v8::Local<v8::Context> context) {
|
||||||
|
return v8::Just<bool>(value->BooleanValue(CcTest::isolate()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ApiNumberChecker {
|
||||||
|
enum Result {
|
||||||
|
kNotCalled,
|
||||||
|
kSlowCalled,
|
||||||
|
kFastCalled,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ApiNumberChecker(T value) {}
|
||||||
|
|
||||||
|
static void CheckArgFast(ApiNumberChecker<T>* receiver, T argument) {
|
||||||
|
CHECK_NE(receiver, nullptr);
|
||||||
|
receiver->result = kFastCalled;
|
||||||
|
receiver->fast_value = argument;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CheckArgSlow(const v8::FunctionCallbackInfo<v8::Value>& info) {
|
||||||
|
CHECK_EQ(info.Length(), 1);
|
||||||
|
|
||||||
|
v8::Object* receiver = v8::Object::Cast(*info.Holder());
|
||||||
|
ApiNumberChecker<T>* checker = static_cast<ApiNumberChecker<T>*>(
|
||||||
|
receiver->GetAlignedPointerFromInternalField(kV8WrapperObjectIndex));
|
||||||
|
|
||||||
|
CHECK_NOT_NULL(checker);
|
||||||
|
if (checker->result == kSlowCalled) return;
|
||||||
|
checker->result = kSlowCalled;
|
||||||
|
|
||||||
|
LocalContext env;
|
||||||
|
checker->slow_value = GetDeoptValue<T>::Get(info[0], env.local());
|
||||||
|
}
|
||||||
|
|
||||||
|
T fast_value = T();
|
||||||
|
Maybe<T> slow_value = v8::Nothing<T>();
|
||||||
|
Result result = kNotCalled;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Behavior {
|
||||||
|
kSuccess, // The callback function should be called with the expected value,
|
||||||
|
// which == initial.
|
||||||
|
kThrow, // An exception should be thrown by the callback function.
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PathTaken {
|
||||||
|
kFast, // The fast path is taken after optimization.
|
||||||
|
kSlow, // The slow path is taken always.
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void SetupTest(v8::Local<v8::Value> initial_value, LocalContext* env,
|
||||||
|
ApiNumberChecker<T>* checker) {
|
||||||
|
v8::Isolate* isolate = CcTest::isolate();
|
||||||
|
|
||||||
|
v8::CFunction c_func = v8::CFunction::Make(ApiNumberChecker<T>::CheckArgFast);
|
||||||
|
|
||||||
|
Local<v8::FunctionTemplate> checker_templ = v8::FunctionTemplate::New(
|
||||||
|
isolate, ApiNumberChecker<T>::CheckArgSlow, v8::Local<v8::Value>(),
|
||||||
|
v8::Local<v8::Signature>(), 1, v8::ConstructorBehavior::kAllow,
|
||||||
|
v8::SideEffectType::kHasSideEffect, &c_func);
|
||||||
|
|
||||||
|
v8::Local<v8::ObjectTemplate> object_template =
|
||||||
|
v8::ObjectTemplate::New(isolate);
|
||||||
|
object_template->SetInternalFieldCount(kV8WrapperObjectIndex + 1);
|
||||||
|
object_template->Set(v8_str("api_func"), checker_templ);
|
||||||
|
|
||||||
|
v8::Local<v8::Object> object =
|
||||||
|
object_template->NewInstance(env->local()).ToLocalChecked();
|
||||||
|
object->SetAlignedPointerInInternalField(kV8WrapperObjectIndex,
|
||||||
|
reinterpret_cast<void*>(checker));
|
||||||
|
|
||||||
|
CHECK((*env)
|
||||||
|
->Global()
|
||||||
|
->Set(env->local(), v8_str("receiver"), object)
|
||||||
|
.FromJust());
|
||||||
|
CHECK((*env)
|
||||||
|
->Global()
|
||||||
|
->Set(env->local(), v8_str("value"), initial_value)
|
||||||
|
.FromJust());
|
||||||
|
CompileRun(
|
||||||
|
"function func(arg) { receiver.api_func(arg); }"
|
||||||
|
"%PrepareFunctionForOptimization(func);"
|
||||||
|
"func(value);"
|
||||||
|
"%OptimizeFunctionOnNextCall(func);"
|
||||||
|
"func(value);");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void CallAndCheck(T expected_value, Behavior expected_behavior,
|
||||||
|
PathTaken expected_path, v8::Local<v8::Value> initial_value) {
|
||||||
|
LocalContext env;
|
||||||
|
v8::TryCatch try_catch(CcTest::isolate());
|
||||||
|
ApiNumberChecker<T> checker(expected_value);
|
||||||
|
|
||||||
|
SetupTest<T>(initial_value, &env, &checker);
|
||||||
|
|
||||||
|
if (expected_behavior == Behavior::kThrow) {
|
||||||
|
CHECK(try_catch.HasCaught());
|
||||||
|
CHECK_NE(checker.result, ApiNumberChecker<T>::kFastCalled);
|
||||||
|
} else {
|
||||||
|
CHECK_EQ(try_catch.HasCaught(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected_path == PathTaken::kSlow) {
|
||||||
|
// The slow version callback should have been called twice.
|
||||||
|
CHECK_EQ(checker.result, ApiNumberChecker<T>::kSlowCalled);
|
||||||
|
|
||||||
|
if (expected_behavior != Behavior::kThrow) {
|
||||||
|
T slow_value_typed = checker.slow_value.ToChecked();
|
||||||
|
CHECK_EQ(slow_value_typed, expected_value);
|
||||||
|
}
|
||||||
|
} else if (expected_path == PathTaken::kFast) {
|
||||||
|
CHECK_EQ(checker.result, ApiNumberChecker<T>::kFastCalled);
|
||||||
|
CHECK_EQ(checker.fast_value, expected_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallAndDeopt() {
|
||||||
|
LocalContext env;
|
||||||
|
v8::Local<v8::Value> initial_value(v8_num(42));
|
||||||
|
ApiNumberChecker<int32_t> checker(42);
|
||||||
|
SetupTest(initial_value, &env, &checker);
|
||||||
|
|
||||||
|
v8::Local<v8::Value> function = CompileRun(
|
||||||
|
"try { func(BigInt(42)); } catch(e) {}"
|
||||||
|
"%PrepareFunctionForOptimization(func);"
|
||||||
|
"%OptimizeFunctionOnNextCall(func);"
|
||||||
|
"func(value);"
|
||||||
|
"func;");
|
||||||
|
CHECK(function->IsFunction());
|
||||||
|
i::Handle<i::JSFunction> ifunction =
|
||||||
|
i::Handle<i::JSFunction>::cast(v8::Utils::OpenHandle(*function));
|
||||||
|
CHECK(ifunction->IsOptimized());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallWithLessArguments() {
|
||||||
|
LocalContext env;
|
||||||
|
v8::Local<v8::Value> initial_value(v8_num(42));
|
||||||
|
ApiNumberChecker<int32_t> checker(42);
|
||||||
|
SetupTest(initial_value, &env, &checker);
|
||||||
|
|
||||||
|
CompileRun("func();");
|
||||||
|
|
||||||
|
// Passing not enough arguments should go through the slow path.
|
||||||
|
CHECK_EQ(checker.result, ApiNumberChecker<int32_t>::kSlowCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallWithMoreArguments() {
|
||||||
|
LocalContext env;
|
||||||
|
v8::Local<v8::Value> initial_value(v8_num(42));
|
||||||
|
ApiNumberChecker<int32_t> checker(42);
|
||||||
|
SetupTest(initial_value, &env, &checker);
|
||||||
|
|
||||||
|
CompileRun(
|
||||||
|
"%PrepareFunctionForOptimization(func);"
|
||||||
|
"%OptimizeFunctionOnNextCall(func);"
|
||||||
|
"func(value, value);");
|
||||||
|
|
||||||
|
// Passing too many arguments should just ignore the extra ones.
|
||||||
|
CHECK_EQ(checker.result, ApiNumberChecker<int32_t>::kFastCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace v8 {
|
||||||
|
template <typename T>
|
||||||
|
class WrapperTraits<ApiNumberChecker<T>> {
|
||||||
|
public:
|
||||||
|
static const void* GetTypeInfo() {
|
||||||
|
static const int tag = 0;
|
||||||
|
return reinterpret_cast<const void*>(&tag);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace v8
|
||||||
|
|
||||||
|
TEST(FastApiCalls) {
|
||||||
|
#ifndef V8_LITE_MODE
|
||||||
|
if (i::FLAG_jitless) return;
|
||||||
|
|
||||||
|
i::FLAG_turbo_fast_api_calls = true;
|
||||||
|
i::FLAG_opt = true;
|
||||||
|
i::FLAG_allow_natives_syntax = true;
|
||||||
|
// Disable --always_opt, otherwise we haven't generated the necessary
|
||||||
|
// feedback to go down the "best optimization" path for the fast call.
|
||||||
|
i::FLAG_always_opt = false;
|
||||||
|
|
||||||
|
v8::Isolate* isolate = CcTest::isolate();
|
||||||
|
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||||
|
i_isolate->set_embedder_wrapper_type_index(kV8WrapperTypeIndex);
|
||||||
|
i_isolate->set_embedder_wrapper_object_index(kV8WrapperObjectIndex);
|
||||||
|
|
||||||
|
v8::HandleScope scope(isolate);
|
||||||
|
LocalContext env;
|
||||||
|
|
||||||
|
// Main cases (the value fits in the type)
|
||||||
|
CallAndCheck<int32_t>(-42, Behavior::kSuccess, PathTaken::kFast, v8_num(-42));
|
||||||
|
CallAndCheck<uint32_t>(i::Smi::kMaxValue, Behavior::kSuccess,
|
||||||
|
PathTaken::kFast, v8_num(i::Smi::kMaxValue));
|
||||||
|
#ifdef V8_TARGET_ARCH_X64
|
||||||
|
CallAndCheck<int64_t>(static_cast<int64_t>(i::Smi::kMaxValue) + 1,
|
||||||
|
Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8_num(static_cast<int64_t>(i::Smi::kMaxValue) + 1));
|
||||||
|
#endif // V8_TARGET_ARCH_X64
|
||||||
|
|
||||||
|
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8::Boolean::New(isolate, false));
|
||||||
|
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8::Boolean::New(isolate, true));
|
||||||
|
|
||||||
|
// Corner cases (the value is out of bounds or of different type) - int32_t
|
||||||
|
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kFast, v8_num(-0.0));
|
||||||
|
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8_num(std::numeric_limits<double>::quiet_NaN()));
|
||||||
|
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8_num(std::numeric_limits<double>::infinity()));
|
||||||
|
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||||
|
v8_str("some_string"));
|
||||||
|
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||||
|
v8::Object::New(isolate));
|
||||||
|
CallAndCheck<int32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||||
|
v8::Array::New(isolate));
|
||||||
|
CallAndCheck<int32_t>(0, Behavior::kThrow, PathTaken::kSlow,
|
||||||
|
v8::BigInt::New(isolate, 42));
|
||||||
|
CallAndCheck<int32_t>(std::numeric_limits<int32_t>::min(), Behavior::kSuccess,
|
||||||
|
PathTaken::kFast,
|
||||||
|
v8_num(std::numeric_limits<int32_t>::max() + 1));
|
||||||
|
CallAndCheck<int32_t>(3, Behavior::kSuccess, PathTaken::kFast, v8_num(3.14));
|
||||||
|
|
||||||
|
// Corner cases - uint32_t
|
||||||
|
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kFast, v8_num(-0.0));
|
||||||
|
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8_num(std::numeric_limits<double>::quiet_NaN()));
|
||||||
|
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8_num(std::numeric_limits<double>::infinity()));
|
||||||
|
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||||
|
v8_str("some_string"));
|
||||||
|
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||||
|
v8::Object::New(isolate));
|
||||||
|
CallAndCheck<uint32_t>(0, Behavior::kSuccess, PathTaken::kSlow,
|
||||||
|
v8::Array::New(isolate));
|
||||||
|
CallAndCheck<uint32_t>(0, Behavior::kThrow, PathTaken::kSlow,
|
||||||
|
v8::BigInt::New(isolate, 42));
|
||||||
|
CallAndCheck<uint32_t>(std::numeric_limits<uint32_t>::min(),
|
||||||
|
Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8_num(std::numeric_limits<uint32_t>::max() + 1));
|
||||||
|
CallAndCheck<uint32_t>(3, Behavior::kSuccess, PathTaken::kFast, v8_num(3.14));
|
||||||
|
|
||||||
|
// Corner cases - bool
|
||||||
|
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8::Undefined(isolate));
|
||||||
|
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8::Null(isolate));
|
||||||
|
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast, v8_num(0));
|
||||||
|
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast, v8_num(42));
|
||||||
|
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast, v8_str(""));
|
||||||
|
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8_str("some_string"));
|
||||||
|
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8::Symbol::New(isolate));
|
||||||
|
CallAndCheck<bool>(false, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8::BigInt::New(isolate, 0));
|
||||||
|
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8::BigInt::New(isolate, 42));
|
||||||
|
CallAndCheck<bool>(true, Behavior::kSuccess, PathTaken::kFast,
|
||||||
|
v8::Object::New(isolate));
|
||||||
|
|
||||||
|
// Check for the deopt loop protection
|
||||||
|
CallAndDeopt();
|
||||||
|
|
||||||
|
// Wrong number of arguments
|
||||||
|
CallWithLessArguments();
|
||||||
|
CallWithMoreArguments();
|
||||||
|
|
||||||
|
// TODO(mslekova): Add corner cases for 64-bit values.
|
||||||
|
// TODO(mslekova): Add main cases for float and double.
|
||||||
|
// TODO(mslekova): Restructure the tests so that the fast optimized calls
|
||||||
|
// are compared against the slow optimized calls.
|
||||||
|
#endif // V8_LITE_MODE
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user