[turbofan] Fast API calls from TurboFan
Relanding the Fast C API code with fix for arm sim lite build. 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 TBR=yangguo@chromium.org,mvstanton@chromium.org,neis@chromium.org,leszeks@chromium.org,verwaest@chromium.org,mslekova@chromium.org,nicohartmann@chromium.org Change-Id: I4421ce817e3b6159a38d2cb39fb97847f128e648 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2064223 Reviewed-by: Michael Stanton <mvstanton@chromium.org> Commit-Queue: Michael Stanton <mvstanton@chromium.org> Cr-Commit-Position: refs/heads/master@{#66344}
This commit is contained in:
parent
52b3cb99a2
commit
4e11ad92f3
2
BUILD.gn
2
BUILD.gn
@ -1706,6 +1706,7 @@ v8_header_set("v8_headers") {
|
||||
public_configs = [ ":v8_header_features" ]
|
||||
|
||||
sources = [
|
||||
"include/v8-fast-api-calls.h",
|
||||
"include/v8-internal.h",
|
||||
"include/v8.h",
|
||||
"include/v8config.h",
|
||||
@ -2027,6 +2028,7 @@ v8_source_set("v8_base_without_compiler") {
|
||||
|
||||
### gcmole(all) ###
|
||||
"$target_gen_dir/builtins-generated/bytecodes-builtins-list.h",
|
||||
"include/v8-fast-api-calls.h",
|
||||
"include/v8-inspector-protocol.h",
|
||||
"include/v8-inspector.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<Value> data);
|
||||
|
||||
class CFunction;
|
||||
/**
|
||||
* A FunctionTemplate is used to create functions at runtime. There
|
||||
* 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_property == 3;
|
||||
* \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 {
|
||||
public:
|
||||
@ -6392,7 +6399,8 @@ class V8_EXPORT FunctionTemplate : public Template {
|
||||
Local<Value> data = Local<Value>(),
|
||||
Local<Signature> signature = Local<Signature>(), int length = 0,
|
||||
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. */
|
||||
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
|
||||
* 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(
|
||||
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. */
|
||||
void SetLength(int length);
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "src/api/api-inl.h"
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "include/v8-profiler.h"
|
||||
#include "include/v8-util.h"
|
||||
#include "src/api/api-natives.h"
|
||||
@ -1471,7 +1472,8 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
||||
i::Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
|
||||
v8::Local<Signature> signature, int length, bool do_not_cache,
|
||||
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::FUNCTION_TEMPLATE_INFO_TYPE, i::AllocationType::kOld);
|
||||
i::Handle<i::FunctionTemplateInfo> obj =
|
||||
@ -1489,7 +1491,8 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
||||
obj->set_serial_number(i::Smi::FromInt(next_serial_number));
|
||||
}
|
||||
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_needs_access_check(false);
|
||||
@ -1507,14 +1510,15 @@ static Local<FunctionTemplate> FunctionTemplateNew(
|
||||
Local<FunctionTemplate> FunctionTemplate::New(
|
||||
Isolate* isolate, FunctionCallback callback, v8::Local<Value> data,
|
||||
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);
|
||||
// Changes to the environment cannot be captured in the snapshot. Expect no
|
||||
// function templates when the isolate is created for serialization.
|
||||
LOG_API(i_isolate, FunctionTemplate, New);
|
||||
ENTER_V8_NO_SCRIPT_NO_EXCEPTION(i_isolate);
|
||||
auto templ = FunctionTemplateNew(i_isolate, callback, data, signature, length,
|
||||
false, Local<Private>(), side_effect_type);
|
||||
auto templ =
|
||||
FunctionTemplateNew(i_isolate, callback, data, signature, length, false,
|
||||
Local<Private>(), side_effect_type, c_function);
|
||||
if (behavior == ConstructorBehavior::kThrow) templ->RemovePrototype();
|
||||
return templ;
|
||||
}
|
||||
@ -1563,7 +1567,8 @@ Local<AccessorSignature> AccessorSignature::New(
|
||||
|
||||
void FunctionTemplate::SetCallHandler(FunctionCallback callback,
|
||||
v8::Local<Value> data,
|
||||
SideEffectType side_effect_type) {
|
||||
SideEffectType side_effect_type,
|
||||
const CFunction* c_function) {
|
||||
auto info = Utils::OpenHandle(this);
|
||||
EnsureNotInstantiated(info, "v8::FunctionTemplate::SetCallHandler");
|
||||
i::Isolate* isolate = info->GetIsolate();
|
||||
@ -1577,6 +1582,15 @@ void FunctionTemplate::SetCallHandler(FunctionCallback callback,
|
||||
data = v8::Undefined(reinterpret_cast<v8::Isolate*>(isolate));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@ -10866,6 +10880,34 @@ void EmbedderHeapTracer::ResetHandleInNonTracingGC(
|
||||
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 {
|
||||
|
||||
const size_t HandleScopeImplementer::kEnteredContextsOffset =
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include "src/objects/instance-type.h"
|
||||
|
||||
namespace v8 {
|
||||
class CFunctionInfo;
|
||||
|
||||
namespace internal {
|
||||
|
||||
class BytecodeArray;
|
||||
@ -659,6 +661,8 @@ class FunctionTemplateInfoRef : public HeapObjectRef {
|
||||
|
||||
void SerializeCallCode();
|
||||
base::Optional<CallHandlerInfoRef> call_code() const;
|
||||
Address c_function() const;
|
||||
const CFunctionInfo* c_signature() const;
|
||||
|
||||
HolderLookupResult LookupHolderOfExpectedType(
|
||||
MapRef receiver_map,
|
||||
|
@ -6,7 +6,9 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/base/small-vector.h"
|
||||
#include "src/builtins/builtins-promise.h"
|
||||
#include "src/builtins/builtins-utils.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<Object> value, NumberOperationHint hint) {
|
||||
return AddNode<Number>(
|
||||
@ -3368,8 +3452,9 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
// See if we can constant-fold the compatible receiver checks.
|
||||
HolderLookupResult api_holder =
|
||||
function_template_info.LookupHolderOfExpectedType(first_receiver_map);
|
||||
if (api_holder.lookup == CallOptimization::kHolderNotFound)
|
||||
if (api_holder.lookup == CallOptimization::kHolderNotFound) {
|
||||
return inference.NoChange();
|
||||
}
|
||||
|
||||
// Check that all {receiver_maps} are actually JSReceiver maps and
|
||||
// that the {function_template_info} accepts them without access
|
||||
@ -3471,6 +3556,18 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
<< function_template_info);
|
||||
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();
|
||||
Callable call_api_callback = CodeFactory::CallApiCallback(isolate());
|
||||
CallInterfaceDescriptor cid = call_api_callback.descriptor();
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/ast/modules.h"
|
||||
#include "src/codegen/code-factory.h"
|
||||
@ -226,6 +227,8 @@ class FunctionTemplateInfoData : public HeapObjectData {
|
||||
|
||||
void SerializeCallCode(JSHeapBroker* broker);
|
||||
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_; }
|
||||
|
||||
private:
|
||||
@ -234,6 +237,8 @@ class FunctionTemplateInfoData : public HeapObjectData {
|
||||
bool has_call_code_ = false;
|
||||
|
||||
CallHandlerInfoData* call_code_ = nullptr;
|
||||
const Address c_function_;
|
||||
const CFunctionInfo* const c_signature_;
|
||||
KnownReceiversMap known_receivers_;
|
||||
};
|
||||
|
||||
@ -257,6 +262,8 @@ FunctionTemplateInfoData::FunctionTemplateInfoData(
|
||||
JSHeapBroker* broker, ObjectData** storage,
|
||||
Handle<FunctionTemplateInfo> object)
|
||||
: HeapObjectData(broker, storage, object),
|
||||
c_function_(v8::ToCData<Address>(object->GetCFunction())),
|
||||
c_signature_(v8::ToCData<CFunctionInfo*>(object->GetCSignature())),
|
||||
known_receivers_(broker->zone()) {
|
||||
auto function_template_info = Handle<FunctionTemplateInfo>::cast(object);
|
||||
is_signature_undefined_ =
|
||||
@ -3676,6 +3683,20 @@ Address CallHandlerInfoRef::callback() const {
|
||||
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 {
|
||||
IF_ACCESS_FROM_HEAP_C(String, IsSeqString);
|
||||
return data()->AsString()->is_seq_string();
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "src/zone/zone.h"
|
||||
|
||||
namespace v8 {
|
||||
class CFunctionInfo;
|
||||
|
||||
namespace internal {
|
||||
|
||||
class CallInterfaceDescriptor;
|
||||
@ -357,6 +359,13 @@ class V8_EXPORT_PRIVATE CallDescriptor final
|
||||
return allocatable_registers_ != 0;
|
||||
}
|
||||
|
||||
// Stores the signature information for a fast API call - C++ functions
|
||||
// that can be called directly from TurboFan.
|
||||
void SetCFunctionInfo(const CFunctionInfo* c_function_info) {
|
||||
c_function_info_ = c_function_info;
|
||||
}
|
||||
const CFunctionInfo* GetCFunctionInfo() const { return c_function_info_; }
|
||||
|
||||
private:
|
||||
friend class Linkage;
|
||||
|
||||
@ -374,6 +383,7 @@ class V8_EXPORT_PRIVATE CallDescriptor final
|
||||
const RegList allocatable_registers_;
|
||||
const Flags flags_;
|
||||
const char* const debug_name_;
|
||||
const CFunctionInfo* c_function_info_ = nullptr;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CallDescriptor);
|
||||
};
|
||||
|
@ -475,7 +475,8 @@
|
||||
V(PoisonIndex) \
|
||||
V(RuntimeAbort) \
|
||||
V(AssertType) \
|
||||
V(DateNow)
|
||||
V(DateNow) \
|
||||
V(FastApiCall)
|
||||
|
||||
#define SIMPLIFIED_SPECULATIVE_BIGINT_BINOP_LIST(V) \
|
||||
V(SpeculativeBigIntAdd) \
|
||||
|
@ -889,12 +889,12 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
|
||||
if (use_info.type_check() == TypeCheckKind::kSignedSmall ||
|
||||
use_info.type_check() == TypeCheckKind::kSigned32 ||
|
||||
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()) ||
|
||||
(indentify_zeros && output_type.Is(Type::Signed32OrMinusZero()))) {
|
||||
(identify_zeros && output_type.Is(Type::Signed32OrMinusZero()))) {
|
||||
return node;
|
||||
} else if (output_type.Is(Type::Unsigned32()) ||
|
||||
(indentify_zeros &&
|
||||
(identify_zeros &&
|
||||
output_type.Is(Type::Unsigned32OrMinusZero()))) {
|
||||
op = simplified()->CheckedUint32ToInt32(use_info.feedback());
|
||||
} else {
|
||||
|
@ -196,6 +196,9 @@ class UseInfo {
|
||||
static UseInfo Float32() {
|
||||
return UseInfo(MachineRepresentation::kFloat32, Truncation::Any());
|
||||
}
|
||||
static UseInfo Float64() {
|
||||
return UseInfo(MachineRepresentation::kFloat64, Truncation::Any());
|
||||
}
|
||||
static UseInfo TruncatingFloat64(
|
||||
IdentifyZeros identify_zeros = kDistinguishZeros) {
|
||||
return UseInfo(MachineRepresentation::kFloat64,
|
||||
|
@ -6,8 +6,11 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "src/base/bits.h"
|
||||
#include "src/base/small-vector.h"
|
||||
#include "src/codegen/code-factory.h"
|
||||
#include "src/codegen/machine-type.h"
|
||||
#include "src/codegen/tick-counter.h"
|
||||
#include "src/compiler/access-builder.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}.
|
||||
// Depending on the operator, propagate new usage info to the inputs.
|
||||
void VisitNode(Node* node, Truncation truncation,
|
||||
@ -3557,6 +3656,11 @@ class RepresentationSelector {
|
||||
return;
|
||||
}
|
||||
|
||||
case IrOpcode::kFastApiCall: {
|
||||
VisitFastApiCall(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// Operators with all inputs tagged and no or tagged output have uniform
|
||||
// handling.
|
||||
case IrOpcode::kEnd:
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "src/compiler/simplified-operator.h"
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "src/base/lazy-instance.h"
|
||||
#include "src/compiler/opcodes.h"
|
||||
#include "src/compiler/operator.h"
|
||||
@ -1256,6 +1257,16 @@ const Operator* SimplifiedOperatorBuilder::AssertType(Type 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(
|
||||
DeoptimizeReason reason, const FeedbackSource& feedback) {
|
||||
if (!feedback.IsValid()) {
|
||||
@ -1679,6 +1690,25 @@ int NewArgumentsElementsMappedCountOf(const Operator* 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,
|
||||
AllocationType allocation) {
|
||||
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;
|
||||
|
||||
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
|
||||
// medium-level operations of V8, including adding numbers, allocating objects,
|
||||
// indexing into objects and arrays, etc.
|
||||
@ -920,6 +944,10 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
||||
|
||||
const Operator* DateNow();
|
||||
|
||||
// Stores the signature and feedback of a fast C call
|
||||
const Operator* FastApiCall(const CFunctionInfo* signature,
|
||||
FeedbackSource const& feedback);
|
||||
|
||||
private:
|
||||
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::TypeFastApiCall(Node* node) { return Type::Any(); }
|
||||
|
||||
Type Typer::Visitor::TypeProjection(Node* node) {
|
||||
Type const type = Operand(node, 0);
|
||||
if (type.Is(Type::None())) return Type::None();
|
||||
|
@ -1529,7 +1529,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
// Object -> fieldtype
|
||||
// TODO(rossberg): activate once machine ops are typed.
|
||||
// CheckValueInputIs(node, 0, Type::Object());
|
||||
// CheckTypeIs(node, FieldAccessOf(node->op()).type));
|
||||
// CheckTypeIs(node, FieldAccessOf(node->op()).type);
|
||||
break;
|
||||
case IrOpcode::kLoadElement:
|
||||
case IrOpcode::kLoadStackArgument:
|
||||
@ -1539,7 +1539,7 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
// CheckTypeIs(node, ElementAccessOf(node->op()).type));
|
||||
break;
|
||||
case IrOpcode::kLoadFromObject:
|
||||
// TODO(gsps): Can we check some types here?
|
||||
CheckValueInputIs(node, 0, Type::Receiver());
|
||||
break;
|
||||
case IrOpcode::kLoadTypedElement:
|
||||
break;
|
||||
@ -1599,6 +1599,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
||||
CheckValueInputIs(node, 0, Type::Any());
|
||||
CheckTypeIs(node, Type::BigInt());
|
||||
break;
|
||||
case IrOpcode::kFastApiCall:
|
||||
CHECK_GE(value_count, 1);
|
||||
CheckValueInputIs(node, 0, Type::ExternalPointer());
|
||||
break;
|
||||
|
||||
// Machine operators
|
||||
// -----------------------
|
||||
|
@ -437,7 +437,9 @@ using DebugObjectCache = std::vector<Handle<HeapObject>>;
|
||||
V(v8_inspector::V8Inspector*, inspector, nullptr) \
|
||||
V(bool, next_v8_call_is_safe_for_termination, 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) \
|
||||
inline void set_##name(type v) { thread_local_top()->name##_ = v; } \
|
||||
|
@ -638,6 +638,7 @@ DEFINE_BOOL(turbo_rewrite_far_jumps, true,
|
||||
DEFINE_BOOL(
|
||||
stress_gc_during_compilation, false,
|
||||
"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.
|
||||
DEFINE_BOOL(optimize_for_size, false,
|
||||
|
@ -1319,6 +1319,8 @@ FunctionTemplateRareData FunctionTemplateInfo::AllocateFunctionTemplateRareData(
|
||||
FUNCTION_TEMPLATE_RARE_DATA_TYPE, AllocationType::kOld);
|
||||
Handle<FunctionTemplateRareData> rare_data =
|
||||
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);
|
||||
return *rare_data;
|
||||
}
|
||||
|
@ -23,6 +23,8 @@ extern class FunctionTemplateRareData extends Struct {
|
||||
instance_template: Object;
|
||||
instance_call_handler: Object;
|
||||
access_check_info: Object;
|
||||
c_function: Foreign|Smi;
|
||||
c_signature: Foreign|Smi;
|
||||
}
|
||||
|
||||
@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) { \
|
||||
HeapObject extra = rare_data(isolate); \
|
||||
HeapObject undefined = GetReadOnlyRoots(isolate).undefined_value(); \
|
||||
return extra == undefined ? undefined \
|
||||
return extra == undefined ? Default \
|
||||
: FunctionTemplateRareData::cast(extra).Name(); \
|
||||
} \
|
||||
inline void FunctionTemplateInfo::Set##CamelName( \
|
||||
@ -65,14 +65,18 @@ FunctionTemplateRareData FunctionTemplateInfo::EnsureFunctionTemplateRareData(
|
||||
rare_data.set_##Name(*Name); \
|
||||
}
|
||||
|
||||
RARE_ACCESSORS(prototype_template, PrototypeTemplate, Object)
|
||||
RARE_ACCESSORS(prototype_provider_template, PrototypeProviderTemplate, Object)
|
||||
RARE_ACCESSORS(parent_template, ParentTemplate, Object)
|
||||
RARE_ACCESSORS(named_property_handler, NamedPropertyHandler, Object)
|
||||
RARE_ACCESSORS(indexed_property_handler, IndexedPropertyHandler, Object)
|
||||
RARE_ACCESSORS(instance_template, InstanceTemplate, Object)
|
||||
RARE_ACCESSORS(instance_call_handler, InstanceCallHandler, Object)
|
||||
RARE_ACCESSORS(access_check_info, AccessCheckInfo, Object)
|
||||
RARE_ACCESSORS(prototype_template, PrototypeTemplate, Object, undefined)
|
||||
RARE_ACCESSORS(prototype_provider_template, PrototypeProviderTemplate, Object,
|
||||
undefined)
|
||||
RARE_ACCESSORS(parent_template, ParentTemplate, Object, undefined)
|
||||
RARE_ACCESSORS(named_property_handler, NamedPropertyHandler, Object, undefined)
|
||||
RARE_ACCESSORS(indexed_property_handler, IndexedPropertyHandler, Object,
|
||||
undefined)
|
||||
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
|
||||
|
||||
bool FunctionTemplateInfo::instantiated() {
|
||||
|
@ -82,6 +82,9 @@ class FunctionTemplateInfo
|
||||
DECL_RARE_ACCESSORS(instance_call_handler, InstanceCallHandler, 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
|
||||
|
||||
// Internal field to store a flag bitfield.
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <unistd.h> // NOLINT
|
||||
#endif
|
||||
|
||||
#include "include/v8-fast-api-calls.h"
|
||||
#include "include/v8-util.h"
|
||||
#include "src/api/api-inl.h"
|
||||
#include "src/base/overflowing-math.h"
|
||||
@ -27013,3 +27014,325 @@ UNINITIALIZED_TEST(NestedIsolates) {
|
||||
}
|
||||
|
||||
#undef THREADED_PROFILED_TEST
|
||||
|
||||
#ifndef V8_LITE_MODE
|
||||
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
|
||||
#endif // V8_LITE_MODE
|
||||
|
||||
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