Commit 860fcb1b authored by Paolo Severini's avatar Paolo Severini Committed by Commit Bot

Faster JS-to-Wasm calls

This replaces https://chromium-review.googlesource.com/c/v8/v8/+/2376165/.

Currently JS-to-Wasm calls go through a wrapper/trampoline, built on
the basis of the signature of a Wasm function to call, and whose task
is to:
- set "thread_in_wasm_flag" to true
- convert the arguments from tagged types into Wasm native types
- calculate the address of the Wasm function to call and call it
- convert back the result from Wasm native types into tagged types
- reset "thread_in_wasm_flag" to false.

This CL tries to improve the performance of JS-to-Wasm calls by
inlining the code of the JS-to-Wasm wrappers in the call site.

It introduces a new IR operand, JSWasmCall, which replaces JSCall for
this kind of calls. A 'JSWasmCall' node is associated to
WasmCallParameters, which contain information about the signature of
the Wasm function to call.

WasmWrapperGraphBuilder::BuildJSToWasmWrapper is modified to avoid generating code to convert the types for the arguments
of the Wasm function, when the conversion is not necessary.
The actual inlining of the graph generated for this wrapper happens in
the simplified-lowering phase.

A new builtin, JSToWasmLazyDeoptContinuation, is introduced to manage
lazy deoptimizations that can happen if the Wasm function callee calls
back some JS code that invalidates the compiled JS caller function.

Bug: v8:11092
Change-Id: I3174c1c1f59b39107b333d1929ecc0584486b8ad
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2557538Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Reviewed-by: 's avatarGeorg Neis (ooo until January 5) <neis@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Paolo Severini <paolosev@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#71824}
parent f475e990
......@@ -3424,6 +3424,7 @@ v8_source_set("v8_base_without_compiler") {
"src/wasm/streaming-decoder.h",
"src/wasm/struct-types.h",
"src/wasm/sync-streaming-decoder.cc",
"src/wasm/value-type.cc",
"src/wasm/value-type.h",
"src/wasm/wasm-arguments.h",
"src/wasm/wasm-code-manager.cc",
......
......@@ -202,7 +202,7 @@ namespace internal {
TFC(I32PairToBigInt, I32PairToBigInt) \
\
/* Type conversions continuations */ \
TFC(ToBooleanLazyDeoptContinuation, TypeConversionStackParameter) \
TFC(ToBooleanLazyDeoptContinuation, SingleParameterOnStack) \
\
/* Handlers */ \
TFH(KeyedLoadIC_PolymorphicName, LoadWithVector) \
......@@ -817,6 +817,7 @@ namespace internal {
TFC(WasmI32AtomicWait32, WasmI32AtomicWait32) \
TFC(WasmI64AtomicWait32, WasmI64AtomicWait32) \
TFS(WasmAllocatePair, kValue1, kValue2) \
TFC(JSToWasmLazyDeoptContinuation, SingleParameterOnStack) \
\
/* WeakMap */ \
TFJ(WeakMapConstructor, kDontAdaptArgumentsSentinel) \
......
......@@ -129,5 +129,20 @@ TF_BUILTIN(WasmAllocatePair, WasmBuiltinsAssembler) {
Return(result);
}
TF_BUILTIN(JSToWasmLazyDeoptContinuation, WasmBuiltinsAssembler) {
// Reset thread_in_wasm_flag.
TNode<ExternalReference> thread_in_wasm_flag_address_address =
ExternalConstant(
ExternalReference::thread_in_wasm_flag_address_address(isolate()));
auto thread_in_wasm_flag_address =
Load<RawPtrT>(thread_in_wasm_flag_address_address);
StoreNoWriteBarrier(MachineRepresentation::kWord32,
thread_in_wasm_flag_address, Int32Constant(0));
// Return the argument.
auto value = Parameter<Object>(Descriptor::kArgument);
Return(value);
}
} // namespace internal
} // namespace v8
......@@ -485,6 +485,11 @@ ExternalReference::address_of_enable_experimental_regexp_engine() {
return ExternalReference(&FLAG_enable_experimental_regexp_engine);
}
ExternalReference ExternalReference::thread_in_wasm_flag_address_address(
Isolate* isolate) {
return ExternalReference(isolate->thread_in_wasm_flag_address_address());
}
ExternalReference ExternalReference::is_profiling_address(Isolate* isolate) {
return ExternalReference(isolate->is_profiling_address());
}
......
......@@ -78,6 +78,8 @@ class StatsCounter;
V(address_of_regexp_stack_memory_top_address, \
"RegExpStack::memory_top_address_address()") \
V(address_of_static_offsets_vector, "OffsetsVector::static_offsets_vector") \
V(thread_in_wasm_flag_address_address, \
"Isolate::thread_in_wasm_flag_address_address") \
V(re_case_insensitive_compare_unicode, \
"NativeRegExpMacroAssembler::CaseInsensitiveCompareUnicode()") \
V(re_case_insensitive_compare_non_unicode, \
......
......@@ -293,7 +293,7 @@ void TypeConversionNoContextDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void TypeConversionStackParameterDescriptor::InitializePlatformSpecific(
void SingleParameterOnStackDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
data->InitializePlatformSpecific(0, nullptr);
}
......
......@@ -81,6 +81,7 @@ namespace internal {
V(LoadWithVector) \
V(LoadWithReceiverAndVector) \
V(NoContext) \
V(SingleParameterOnStack) \
V(RecordWrite) \
V(ResumeGenerator) \
V(RunMicrotasks) \
......@@ -95,7 +96,6 @@ namespace internal {
V(StringSubstring) \
V(TypeConversion) \
V(TypeConversionNoContext) \
V(TypeConversionStackParameter) \
V(Typeof) \
V(UnaryOp_WithFeedback) \
V(Void) \
......@@ -935,13 +935,11 @@ class TypeConversionNoContextDescriptor final : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(TypeConversionNoContextDescriptor, CallInterfaceDescriptor)
};
class TypeConversionStackParameterDescriptor final
: public CallInterfaceDescriptor {
class SingleParameterOnStackDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kArgument)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged())
DECLARE_DESCRIPTOR(TypeConversionStackParameterDescriptor,
CallInterfaceDescriptor)
DECLARE_DESCRIPTOR(SingleParameterOnStackDescriptor, CallInterfaceDescriptor)
};
class AsyncFunctionStackParameterDescriptor final
......
......@@ -1188,6 +1188,14 @@ void CodeGenerator::BuildTranslationForFrameStateDescriptor(
height);
break;
}
case FrameStateType::kJSToWasmBuiltinContinuation: {
const JSToWasmFrameStateDescriptor* js_to_wasm_descriptor =
static_cast<const JSToWasmFrameStateDescriptor*>(descriptor);
translation->BeginJSToWasmBuiltinContinuationFrame(
bailout_id, shared_info_id, height,
js_to_wasm_descriptor->return_type());
break;
}
case FrameStateType::kJavaScriptBuiltinContinuation: {
translation->BeginJavaScriptBuiltinContinuationFrame(
bailout_id, shared_info_id, height);
......
......@@ -3339,6 +3339,15 @@ FrameStateDescriptor* GetFrameStateDescriptorInternal(Zone* zone, Node* state) {
outer_state = GetFrameStateDescriptorInternal(zone, outer_node);
}
if (state_info.type() == FrameStateType::kJSToWasmBuiltinContinuation) {
auto function_info = static_cast<const JSToWasmFrameStateFunctionInfo*>(
state_info.function_info());
return zone->New<JSToWasmFrameStateDescriptor>(
zone, state_info.type(), state_info.bailout_id(),
state_info.state_combine(), parameters, locals, stack,
state_info.shared_info(), outer_state, function_info->signature());
}
return zone->New<FrameStateDescriptor>(
zone, state_info.type(), state_info.bailout_id(),
state_info.state_combine(), parameters, locals, stack,
......
......@@ -14,8 +14,10 @@
#include "src/compiler/graph.h"
#include "src/compiler/node.h"
#include "src/compiler/schedule.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/execution/frames.h"
#include "src/utils/ostreams.h"
#include "src/wasm/value-type.h"
namespace v8 {
namespace internal {
......@@ -1017,6 +1019,7 @@ size_t GetConservativeFrameSizeInBytes(FrameStateType type,
return info.frame_size_in_bytes();
}
case FrameStateType::kBuiltinContinuation:
case FrameStateType::kJSToWasmBuiltinContinuation:
case FrameStateType::kJavaScriptBuiltinContinuation:
case FrameStateType::kJavaScriptBuiltinContinuationWithCatch: {
const RegisterConfiguration* config = RegisterConfiguration::Default();
......@@ -1071,6 +1074,7 @@ size_t FrameStateDescriptor::GetHeight() const {
case FrameStateType::kInterpretedFunction:
return locals_count(); // The accumulator is *not* included.
case FrameStateType::kBuiltinContinuation:
case FrameStateType::kJSToWasmBuiltinContinuation:
// Custom, non-JS calling convention (that does not have a notion of
// a receiver or context).
return parameters_count();
......@@ -1122,6 +1126,17 @@ size_t FrameStateDescriptor::GetJSFrameCount() const {
return count;
}
JSToWasmFrameStateDescriptor::JSToWasmFrameStateDescriptor(
Zone* zone, FrameStateType type, BailoutId bailout_id,
OutputFrameStateCombine state_combine, size_t parameters_count,
size_t locals_count, size_t stack_count,
MaybeHandle<SharedFunctionInfo> shared_info,
FrameStateDescriptor* outer_state, const wasm::FunctionSig* wasm_signature)
: FrameStateDescriptor(zone, type, bailout_id, state_combine,
parameters_count, locals_count, stack_count,
shared_info, outer_state),
return_type_(wasm::WasmReturnTypeFromSignature(wasm_signature)) {}
std::ostream& operator<<(std::ostream& os, const RpoNumber& rpo) {
return os << rpo.ToSize();
}
......
......@@ -1318,6 +1318,7 @@ class FrameStateDescriptor : public ZoneObject {
bool HasContext() const {
return FrameStateFunctionInfo::IsJSFunctionType(type_) ||
type_ == FrameStateType::kBuiltinContinuation ||
type_ == FrameStateType::kJSToWasmBuiltinContinuation ||
type_ == FrameStateType::kConstructStub;
}
......@@ -1357,6 +1358,25 @@ class FrameStateDescriptor : public ZoneObject {
FrameStateDescriptor* const outer_state_;
};
class JSToWasmFrameStateDescriptor : public FrameStateDescriptor {
public:
JSToWasmFrameStateDescriptor(Zone* zone, FrameStateType type,
BailoutId bailout_id,
OutputFrameStateCombine state_combine,
size_t parameters_count, size_t locals_count,
size_t stack_count,
MaybeHandle<SharedFunctionInfo> shared_info,
FrameStateDescriptor* outer_state,
const wasm::FunctionSig* wasm_signature);
base::Optional<wasm::ValueType::Kind> return_type() const {
return return_type_;
}
private:
base::Optional<wasm::ValueType::Kind> return_type_;
};
// A deoptimization entry is a pair of the reason why we deoptimize and the
// frame state descriptor that we have to go back to.
class DeoptimizationEntry final {
......
......@@ -1620,9 +1620,15 @@ const Operator* CommonOperatorBuilder::ResizeMergeOrPhi(const Operator* op,
const FrameStateFunctionInfo*
CommonOperatorBuilder::CreateFrameStateFunctionInfo(
FrameStateType type, int parameter_count, int local_count,
Handle<SharedFunctionInfo> shared_info) {
return zone()->New<FrameStateFunctionInfo>(type, parameter_count, local_count,
shared_info);
Handle<SharedFunctionInfo> shared_info,
const wasm::FunctionSig* signature) {
DCHECK_EQ(type == FrameStateType::kJSToWasmBuiltinContinuation,
signature != nullptr);
return type == FrameStateType::kJSToWasmBuiltinContinuation
? zone()->New<JSToWasmFrameStateFunctionInfo>(
type, parameter_count, local_count, shared_info, signature)
: zone()->New<FrameStateFunctionInfo>(type, parameter_count,
local_count, shared_info);
}
const Operator* CommonOperatorBuilder::DeadValue(MachineRepresentation rep) {
......
......@@ -560,7 +560,8 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
// Constructs function info for frame state construction.
const FrameStateFunctionInfo* CreateFrameStateFunctionInfo(
FrameStateType type, int parameter_count, int local_count,
Handle<SharedFunctionInfo> shared_info);
Handle<SharedFunctionInfo> shared_info,
const wasm::FunctionSig* signature = nullptr);
const Operator* MarkAsSafetyCheck(const Operator* op,
IsSafetyCheck safety_check);
......
......@@ -11,6 +11,7 @@
#include "src/compiler/node.h"
#include "src/handles/handles-inl.h"
#include "src/objects/objects-inl.h"
#include "src/wasm/value-type.h"
namespace v8 {
namespace internal {
......@@ -65,6 +66,9 @@ std::ostream& operator<<(std::ostream& os, FrameStateType type) {
case FrameStateType::kBuiltinContinuation:
os << "BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kJSToWasmBuiltinContinuation:
os << "JS_TO_WASM_BUILTIN_CONTINUATION_FRAME";
break;
case FrameStateType::kJavaScriptBuiltinContinuation:
os << "JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME";
break;
......@@ -88,7 +92,7 @@ std::ostream& operator<<(std::ostream& os, FrameStateInfo const& info) {
namespace {
// Lazy deopt points where the frame state is assocated with a call get an
// Lazy deopt points where the frame state is associated with a call get an
// additional parameter for the return result from the call. The return result
// is added by the deoptimizer and not explicitly specified in the frame state.
// Lazy deopt points which can catch exceptions further get an additional
......@@ -110,7 +114,8 @@ FrameState CreateBuiltinContinuationFrameStateCommon(
JSGraph* jsgraph, FrameStateType frame_type, Builtins::Name name,
Node* closure, Node* context, Node** parameters, int parameter_count,
Node* outer_frame_state,
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>()) {
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(),
const wasm::FunctionSig* signature = nullptr) {
Graph* const graph = jsgraph->graph();
CommonOperatorBuilder* const common = jsgraph->common();
......@@ -121,7 +126,7 @@ FrameState CreateBuiltinContinuationFrameStateCommon(
BailoutId bailout_id = Builtins::GetContinuationBailoutId(name);
const FrameStateFunctionInfo* state_info =
common->CreateFrameStateFunctionInfo(frame_type, parameter_count, 0,
shared);
shared, signature);
const Operator* op = common->FrameState(
bailout_id, OutputFrameStateCombine::Ignore(), state_info);
return FrameState(graph->NewNode(op, params_node, jsgraph->EmptyStateValues(),
......@@ -134,7 +139,7 @@ FrameState CreateBuiltinContinuationFrameStateCommon(
FrameState CreateStubBuiltinContinuationFrameState(
JSGraph* jsgraph, Builtins::Name name, Node* context,
Node* const* parameters, int parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode) {
ContinuationFrameStateMode mode, const wasm::FunctionSig* signature) {
Callable callable = Builtins::CallableFor(jsgraph->isolate(), name);
CallInterfaceDescriptor descriptor = callable.descriptor();
......@@ -163,10 +168,29 @@ FrameState CreateStubBuiltinContinuationFrameState(
actual_parameters.push_back(parameters[i]);
}
FrameStateType frame_state_type = FrameStateType::kBuiltinContinuation;
if (name == Builtins::kJSToWasmLazyDeoptContinuation) {
CHECK_NOT_NULL(signature);
frame_state_type = FrameStateType::kJSToWasmBuiltinContinuation;
}
return CreateBuiltinContinuationFrameStateCommon(
jsgraph, FrameStateType::kBuiltinContinuation, name,
jsgraph->UndefinedConstant(), context, actual_parameters.data(),
static_cast<int>(actual_parameters.size()), outer_frame_state);
jsgraph, frame_state_type, name, jsgraph->UndefinedConstant(), context,
actual_parameters.data(), static_cast<int>(actual_parameters.size()),
outer_frame_state, Handle<SharedFunctionInfo>(), signature);
}
FrameState CreateJSWasmCallBuiltinContinuationFrameState(
JSGraph* jsgraph, Node* context, Node* outer_frame_state,
const wasm::FunctionSig* signature) {
base::Optional<wasm::ValueType::Kind> wasm_return_type =
wasm::WasmReturnTypeFromSignature(signature);
Node* node_return_type =
jsgraph->SmiConstant(wasm_return_type ? wasm_return_type.value() : -1);
Node* lazy_deopt_parameters[] = {node_return_type};
return CreateStubBuiltinContinuationFrameState(
jsgraph, Builtins::kJSToWasmLazyDeoptContinuation, context,
lazy_deopt_parameters, arraysize(lazy_deopt_parameters),
outer_frame_state, ContinuationFrameStateMode::LAZY, signature);
}
FrameState CreateJavaScriptBuiltinContinuationFrameState(
......
......@@ -66,6 +66,8 @@ enum class FrameStateType {
kArgumentsAdaptor, // Represents an ArgumentsAdaptorFrame.
kConstructStub, // Represents a ConstructStubFrame.
kBuiltinContinuation, // Represents a continuation to a stub.
kJSToWasmBuiltinContinuation, // Represents a lazy deopt continuation for a
// JS to Wasm call.
kJavaScriptBuiltinContinuation, // Represents a continuation to a JavaScipt
// builtin.
kJavaScriptBuiltinContinuationWithCatch // Represents a continuation to a
......@@ -101,6 +103,22 @@ class FrameStateFunctionInfo {
Handle<SharedFunctionInfo> const shared_info_;
};
class JSToWasmFrameStateFunctionInfo : public FrameStateFunctionInfo {
public:
JSToWasmFrameStateFunctionInfo(FrameStateType type, int parameter_count,
int local_count,
Handle<SharedFunctionInfo> shared_info,
const wasm::FunctionSig* signature)
: FrameStateFunctionInfo(type, parameter_count, local_count, shared_info),
signature_(signature) {
DCHECK_NOT_NULL(signature);
}
const wasm::FunctionSig* signature() const { return signature_; }
private:
const wasm::FunctionSig* const signature_;
};
class FrameStateInfo final {
public:
......@@ -154,7 +172,12 @@ enum class ContinuationFrameStateMode { EAGER, LAZY, LAZY_WITH_CATCH };
FrameState CreateStubBuiltinContinuationFrameState(
JSGraph* graph, Builtins::Name name, Node* context, Node* const* parameters,
int parameter_count, Node* outer_frame_state,
ContinuationFrameStateMode mode);
ContinuationFrameStateMode mode,
const wasm::FunctionSig* signature = nullptr);
FrameState CreateJSWasmCallBuiltinContinuationFrameState(
JSGraph* jsgraph, Node* context, Node* outer_frame_state,
const wasm::FunctionSig* signature);
FrameState CreateJavaScriptBuiltinContinuationFrameState(
JSGraph* graph, const SharedFunctionInfoRef& shared, Builtins::Name name,
......
......@@ -230,6 +230,8 @@ std::unique_ptr<char[]> GetVisualizerLogFileName(OptimizedCompilationInfo* info,
}
std::replace(filename.begin(), filename.begin() + filename.length(), ' ',
'_');
std::replace(filename.begin(), filename.begin() + filename.length(), ':',
'-');
EmbeddedVector<char, 256> base_dir;
if (optional_base_dir != nullptr) {
......
......@@ -31,6 +31,13 @@ class JSRegExp;
class JSTypedArray;
class NativeContext;
class ScriptContextTable;
template <typename>
class Signature;
namespace wasm {
class ValueType;
struct WasmModule;
} // namespace wasm
namespace compiler {
......@@ -787,20 +794,22 @@ class ScopeInfoRef : public HeapObjectRef {
void SerializeScopeInfoChain();
};
#define BROKER_SFI_FIELDS(V) \
V(int, internal_formal_parameter_count) \
V(bool, has_duplicate_parameters) \
V(int, function_map_index) \
V(FunctionKind, kind) \
V(LanguageMode, language_mode) \
V(bool, native) \
V(bool, HasBreakInfo) \
V(bool, HasBuiltinId) \
V(bool, construct_as_builtin) \
V(bool, HasBytecodeArray) \
V(int, StartPosition) \
V(bool, is_compiled) \
V(bool, IsUserJavaScript)
#define BROKER_SFI_FIELDS(V) \
V(int, internal_formal_parameter_count) \
V(bool, has_duplicate_parameters) \
V(int, function_map_index) \
V(FunctionKind, kind) \
V(LanguageMode, language_mode) \
V(bool, native) \
V(bool, HasBreakInfo) \
V(bool, HasBuiltinId) \
V(bool, construct_as_builtin) \
V(bool, HasBytecodeArray) \
V(int, StartPosition) \
V(bool, is_compiled) \
V(bool, IsUserJavaScript) \
V(const wasm::WasmModule*, wasm_module) \
V(const wasm::FunctionSig*, wasm_function_signature)
class V8_EXPORT_PRIVATE SharedFunctionInfoRef : public HeapObjectRef {
public:
......
......@@ -3429,6 +3429,82 @@ Reduction JSCallReducer::ReduceArraySome(Node* node,
return ReplaceWithSubgraph(&a, subgraph);
}
namespace {
bool CanInlineJSToWasmCall(const wasm::FunctionSig* wasm_signature) {
if (!FLAG_turbo_inline_js_wasm_calls || wasm_signature->return_count() > 1) {
return false;
}
for (auto type : wasm_signature->all()) {
#if defined(V8_TARGET_ARCH_32_BIT)
if (type == wasm::kWasmI64) return false;
#endif
if (type != wasm::kWasmI32 && type != wasm::kWasmI64 &&
type != wasm::kWasmF32 && type != wasm::kWasmF64) {
return false;
}
}
return true;
}
} // namespace
Reduction JSCallReducer::ReduceCallWasmFunction(
Node* node, const SharedFunctionInfoRef& shared) {
JSCallNode n(node);
const CallParameters& p = n.Parameters();
// Avoid deoptimization loops
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
return NoChange();
}
// Don't inline if we are in a try/catch.
// WasmWrapperGraphBuilder::BuildJSToWasmWrapper generates Call nodes that are
// not part of the control chain and this breaks the inlining of IfSuccess
// nodes.
if (NodeProperties::IsExceptionalCall(node)) {
return NoChange();
}
const wasm::FunctionSig* wasm_signature = shared.wasm_function_signature();
if (!CanInlineJSToWasmCall(wasm_signature)) {
return NoChange();
}
const wasm::WasmModule* wasm_module = shared.wasm_module();
const Operator* op =
javascript()->CallWasm(wasm_module, wasm_signature, p.feedback());
// Remove additional inputs
size_t actual_arity = n.ArgumentCount();
DCHECK(JSCallNode::kFeedbackVectorIsLastInput);
DCHECK_EQ(actual_arity + JSWasmCallNode::kExtraInputCount - 1,
n.FeedbackVectorIndex());
size_t expected_arity = wasm_signature->parameter_count();
while (actual_arity > expected_arity) {
int removal_index =
static_cast<int>(n.FirstArgumentIndex() + expected_arity);
DCHECK_LT(removal_index, static_cast<int>(node->InputCount()));
node->RemoveInput(removal_index);
actual_arity--;
}
// Add missing inputs
while (actual_arity < expected_arity) {
int insertion_index = n.ArgumentIndex(n.ArgumentCount());
node->InsertInput(graph()->zone(), insertion_index,
jsgraph()->UndefinedConstant());
actual_arity++;
}
NodeProperties::ChangeOp(node, op);
return Changed(node);
}
#ifndef V8_ENABLE_FP_PARAMS_IN_C_LINKAGE
namespace {
bool HasFPParamsInSignature(const CFunctionInfo* c_signature) {
......@@ -4533,6 +4609,11 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
if (shared.function_template_info().has_value()) {
return ReduceCallApiFunction(node, shared);
}
if (shared.wasm_function_signature()) {
return ReduceCallWasmFunction(node, shared);
}
return NoChange();
}
......
......@@ -73,6 +73,8 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
Reduction ReduceBooleanConstructor(Node* node);
Reduction ReduceCallApiFunction(Node* node,
const SharedFunctionInfoRef& shared);
Reduction ReduceCallWasmFunction(Node* node,
const SharedFunctionInfoRef& shared);
Reduction ReduceFunctionPrototypeApply(Node* node);
Reduction ReduceFunctionPrototypeBind(Node* node);
Reduction ReduceFunctionPrototypeCall(Node* node);
......
......@@ -1251,6 +1251,9 @@ void JSGenericLowering::LowerJSCallRuntime(Node* node) {
ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity()));
}
// Will be lowered in SimplifiedLowering.
void JSGenericLowering::LowerJSWasmCall(Node* node) {}
void JSGenericLowering::LowerJSForInPrepare(Node* node) {
JSForInPrepareNode n(node);
Effect effect(node); // {node} is kept in the effect chain.
......
......@@ -676,6 +676,50 @@ ForInParameters const& ForInParametersOf(const Operator* op) {
return OpParameter<ForInParameters>(op);
}
JSWasmCallParameters const& JSWasmCallParametersOf(const Operator* op) {
DCHECK_EQ(IrOpcode::kJSWasmCall, op->opcode());
return OpParameter<JSWasmCallParameters>(op);
}
std::ostream& operator<<(std::ostream& os, JSWasmCallParameters const& p) {
return os << p.module() << ", " << p.signature() << ", " << p.feedback();
}
size_t hash_value(JSWasmCallParameters const& p) {
return base::hash_combine(p.module(), p.signature(),
FeedbackSource::Hash()(p.feedback()));
}
bool operator==(JSWasmCallParameters const& lhs,
JSWasmCallParameters const& rhs) {
return lhs.module() == rhs.module() && lhs.signature() == rhs.signature() &&
lhs.feedback() == rhs.feedback();
}
int JSWasmCallParameters::arity_without_implicit_args() const {
return static_cast<int>(signature_->parameter_count());
}
int JSWasmCallParameters::input_count() const {
return static_cast<int>(signature_->parameter_count()) +
JSWasmCallNode::kExtraInputCount;
}
// static
Type JSWasmCallNode::TypeForWasmReturnType(const wasm::ValueType& type) {
switch (type.kind()) {
case wasm::ValueType::kI32:
return Type::Signed32();
case wasm::ValueType::kI64:
return Type::BigInt();
case wasm::ValueType::kF32:
case wasm::ValueType::kF64:
return Type::Number();
default:
UNREACHABLE();
}
}
#define CACHED_OP_LIST(V) \
V(ToLength, Operator::kNoProperties, 1, 1) \
V(ToName, Operator::kNoProperties, 1, 1) \
......@@ -874,6 +918,17 @@ const Operator* JSOperatorBuilder::CallRuntime(const Runtime::Function* f,
parameters); // parameter
}
const Operator* JSOperatorBuilder::CallWasm(
const wasm::WasmModule* wasm_module,
const wasm::FunctionSig* wasm_signature, FeedbackSource const& feedback) {
JSWasmCallParameters parameters(wasm_module, wasm_signature, feedback);
return zone()->New<Operator1<JSWasmCallParameters>>(
IrOpcode::kJSWasmCall, Operator::kNoProperties, // opcode
"JSWasmCall", // name
parameters.input_count(), 1, 1, 1, 1, 2, // inputs/outputs
parameters); // parameter
}
const Operator* JSOperatorBuilder::ConstructForwardVarargs(
size_t arity, uint32_t start_index) {
ConstructForwardVarargsParameters parameters(arity, start_index);
......
......@@ -25,6 +25,10 @@ class ArrayBoilerplateDescription;
class FeedbackCell;
class SharedFunctionInfo;
namespace wasm {
class ValueType;
}
namespace compiler {
// Forward declarations.
......@@ -816,6 +820,35 @@ size_t hash_value(ForInParameters const&);
std::ostream& operator<<(std::ostream&, ForInParameters const&);
const ForInParameters& ForInParametersOf(const Operator* op);
class JSWasmCallParameters {
public:
explicit JSWasmCallParameters(const wasm::WasmModule* module,
const wasm::FunctionSig* signature,
FeedbackSource const& feedback)
: module_(module), signature_(signature), feedback_(feedback) {
DCHECK_NOT_NULL(module);
DCHECK_NOT_NULL(signature);
}
const wasm::WasmModule* module() const { return module_; }
const wasm::FunctionSig* signature() const { return signature_; }
FeedbackSource const& feedback() const { return feedback_; }
int input_count() const;
int arity_without_implicit_args() const;
private:
const wasm::WasmModule* const module_;
const wasm::FunctionSig* const signature_;
const FeedbackSource feedback_;
};
JSWasmCallParameters const& JSWasmCallParametersOf(const Operator* op)
V8_WARN_UNUSED_RESULT;
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&,
JSWasmCallParameters const&);
size_t hash_value(JSWasmCallParameters const&);
bool operator==(JSWasmCallParameters const&, JSWasmCallParameters const&);
int RegisterCountOf(Operator const* op) V8_WARN_UNUSED_RESULT;
int GeneratorStoreValueCountOf(const Operator* op) V8_WARN_UNUSED_RESULT;
......@@ -925,6 +958,10 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
const Operator* CallRuntime(const Runtime::Function* function, size_t arity);
const Operator* CallWasm(const wasm::WasmModule* wasm_module,
const wasm::FunctionSig* wasm_signature,
FeedbackSource const& feedback);
const Operator* ConstructForwardVarargs(size_t arity, uint32_t start_index);
const Operator* Construct(uint32_t arity,
CallFrequency const& frequency = CallFrequency(),
......@@ -1247,7 +1284,8 @@ class JSCallOrConstructNode : public JSNodeWrapperBase {
node->opcode() == IrOpcode::kJSCallWithSpread ||
node->opcode() == IrOpcode::kJSConstruct ||
node->opcode() == IrOpcode::kJSConstructWithArrayLike ||
node->opcode() == IrOpcode::kJSConstructWithSpread);
node->opcode() == IrOpcode::kJSConstructWithSpread ||
node->opcode() == IrOpcode::kJSWasmCall);
}
#define INPUTS(V) \
......@@ -1259,8 +1297,8 @@ class JSCallOrConstructNode : public JSNodeWrapperBase {
// Besides actual arguments, JSCall nodes (and variants) also take the
// following. Note that we rely on the fact that all variants (JSCall,
// JSCallWithArrayLike, JSCallWithSpread, JSConstruct,
// JSConstructWithArrayLike, JSConstructWithSpread) have the same underlying
// node layout.
// JSConstructWithArrayLike, JSConstructWithSpread, JSWasmCall) have the same
// underlying node layout.
static constexpr int kTargetInputCount = 1;
static constexpr int kReceiverOrNewTargetInputCount = 1;
static constexpr int kFeedbackVectorInputCount = 1;
......@@ -1355,6 +1393,35 @@ using JSCallNode = JSCallNodeBase<IrOpcode::kJSCall>;
using JSCallWithSpreadNode = JSCallNodeBase<IrOpcode::kJSCallWithSpread>;
using JSCallWithArrayLikeNode = JSCallNodeBase<IrOpcode::kJSCallWithArrayLike>;
class JSWasmCallNode final : public JSCallOrConstructNode {
public:
explicit constexpr JSWasmCallNode(Node* node) : JSCallOrConstructNode(node) {
CONSTEXPR_DCHECK(node->opcode() == IrOpcode::kJSWasmCall);
}
const JSWasmCallParameters& Parameters() const {
return OpParameter<JSWasmCallParameters>(node()->op());
}
#define INPUTS(V) \
V(Target, target, 0, Object) \
V(Receiver, receiver, 1, Object)
INPUTS(DEFINE_INPUT_ACCESSORS)
#undef INPUTS
static constexpr int kReceiverInputCount = 1;
STATIC_ASSERT(kReceiverInputCount ==
JSCallOrConstructNode::kReceiverOrNewTargetInputCount);
int ArgumentCount() const override {
// Note: The count reported by this function depends only on the parameter
// count, thus adding/removing inputs will not affect it.
return Parameters().arity_without_implicit_args();
}
static Type TypeForWasmReturnType(const wasm::ValueType& type);
};
template <int kOpcode>
class JSConstructNodeBase final : public JSCallOrConstructNode {
public:
......
......@@ -196,7 +196,8 @@
V(JSCall) \
V(JSCallForwardVarargs) \
V(JSCallWithArrayLike) \
V(JSCallWithSpread)
V(JSCallWithSpread) \
V(JSWasmCall)
#define JS_CONSTRUCT_OP_LIST(V) \
V(JSConstructForwardVarargs) \
......
......@@ -223,6 +223,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSCall:
case IrOpcode::kJSCallWithArrayLike:
case IrOpcode::kJSCallWithSpread:
case IrOpcode::kJSWasmCall:
// Misc operations
case IrOpcode::kJSAsyncFunctionEnter:
......
This diff is collapsed.
......@@ -992,6 +992,15 @@ Type Typer::Visitor::TypeCall(Node* node) { return Type::Any(); }
Type Typer::Visitor::TypeFastApiCall(Node* node) { return Type::Any(); }
Type Typer::Visitor::TypeJSWasmCall(Node* node) {
const JSWasmCallParameters& op_params = JSWasmCallParametersOf(node->op());
const wasm::FunctionSig* wasm_signature = op_params.signature();
if (wasm_signature->return_count() > 0) {
return JSWasmCallNode::TypeForWasmReturnType(wasm_signature->GetReturn());
}
return Type::Any();
}
Type Typer::Visitor::TypeProjection(Node* node) {
Type const type = Operand(node, 0);
if (type.Is(Type::None())) return Type::None();
......
......@@ -1627,6 +1627,11 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckValueInputIs(node, 0, Type::ExternalPointer()); // callee
CheckValueInputIs(node, 1, Type::Any()); // receiver
break;
case IrOpcode::kJSWasmCall:
CHECK_GE(value_count, 3);
CheckTypeIs(node, Type::Any());
CheckValueInputIs(node, 0, Type::Any()); // callee
break;
// Machine operators
// -----------------------
......
This diff is collapsed.
......@@ -148,6 +148,28 @@ enum CWasmEntryParameters {
V8_EXPORT_PRIVATE Handle<Code> CompileCWasmEntry(
Isolate*, const wasm::FunctionSig*, const wasm::WasmModule* module);
class JSWasmCallData {
public:
void set_arg_needs_conversion(size_t index, bool value) {
if (index >= arg_needs_conversion_.size())
arg_needs_conversion_.resize(index + 1);
arg_needs_conversion_[index] = value;
}
bool arg_needs_conversion(size_t index) const {
DCHECK_LT(index, arg_needs_conversion_.size());
return arg_needs_conversion_[index];
}
void set_result_needs_conversion(bool value) {
result_needs_conversion_ = value;
}
bool result_needs_conversion() const { return result_needs_conversion_; }
private:
bool result_needs_conversion_ = false;
std::vector<bool> arg_needs_conversion_;
};
// Values from the instance object are cached between Wasm-level function calls.
// This struct allows the SSA environment handling this cache to be defined
// and manipulated in wasm-compiler.{h,cc} instead of inside the Wasm decoder.
......@@ -487,7 +509,7 @@ class WasmGraphBuilder {
Node* BuildCCall(MachineSignature* sig, Node* function, Args... args);
Node* BuildCallNode(const wasm::FunctionSig* sig, Vector<Node*> args,
wasm::WasmCodePosition position, Node* instance_node,
const Operator* op);
const Operator* op, Node* frame_state = nullptr);
// Helper function for {BuildIndirectCall}.
void LoadIndirectFunctionTable(uint32_t table_index, Node** ift_size,
Node** ift_sig_ids, Node** ift_targets,
......@@ -498,7 +520,8 @@ class WasmGraphBuilder {
IsReturnCall continuation);
Node* BuildWasmCall(const wasm::FunctionSig* sig, Vector<Node*> args,
Vector<Node*> rets, wasm::WasmCodePosition position,
Node* instance_node, UseRetpoline use_retpoline);
Node* instance_node, UseRetpoline use_retpoline,
Node* frame_state = nullptr);
Node* BuildWasmReturnCall(const wasm::FunctionSig* sig, Vector<Node*> args,
wasm::WasmCodePosition position,
Node* instance_node, UseRetpoline use_retpoline);
......@@ -663,11 +686,17 @@ class WasmGraphBuilder {
enum WasmCallKind { kWasmFunction, kWasmImportWrapper, kWasmCapiFunction };
V8_EXPORT_PRIVATE void BuildInlinedJSToWasmWrapper(
Zone* zone, MachineGraph* mcgraph, const wasm::FunctionSig* signature,
const wasm::WasmModule* module, compiler::SourcePositionTable* spt,
StubCallMode stub_mode, wasm::WasmFeatures features,
const JSWasmCallData* js_wasm_call_data, Node* frame_state);
V8_EXPORT_PRIVATE CallDescriptor* GetWasmCallDescriptor(
Zone* zone, const wasm::FunctionSig* signature,
WasmGraphBuilder::UseRetpoline use_retpoline =
WasmGraphBuilder::kNoRetpoline,
WasmCallKind kind = kWasmFunction);
WasmCallKind kind = kWasmFunction, bool need_frame_state = false);
V8_EXPORT_PRIVATE CallDescriptor* GetI32WasmCallDescriptor(
Zone* zone, const CallDescriptor* call_descriptor);
......
This diff is collapsed.
......@@ -63,12 +63,14 @@ class TranslatedValue {
private:
friend class TranslatedState;
friend class TranslatedFrame;
friend class Deoptimizer;
enum Kind : uint8_t {
kInvalid,
kTagged,
kInt32,
kInt64,
kInt64ToBigInt,
kUInt32,
kBoolBit,
kFloat,
......@@ -105,6 +107,8 @@ class TranslatedValue {
static TranslatedValue NewDouble(TranslatedState* container, Float64 value);
static TranslatedValue NewInt32(TranslatedState* container, int32_t value);
static TranslatedValue NewInt64(TranslatedState* container, int64_t value);
static TranslatedValue NewInt64ToBigInt(TranslatedState* container,
int64_t value);
static TranslatedValue NewUInt32(TranslatedState* container, uint32_t value);
static TranslatedValue NewBool(TranslatedState* container, uint32_t value);
static TranslatedValue NewTagged(TranslatedState* container, Object literal);
......@@ -172,6 +176,7 @@ class TranslatedFrame {
kArgumentsAdaptor,
kConstructStub,
kBuiltinContinuation,
kJSToWasmBuiltinContinuation,
kJavaScriptBuiltinContinuation,
kJavaScriptBuiltinContinuationWithCatch,
kInvalid
......@@ -246,8 +251,15 @@ class TranslatedFrame {
reference front() { return values_.front(); }
const_reference front() const { return values_.front(); }
// Only for Kind == kJSToWasmBuiltinContinuation
base::Optional<wasm::ValueType::Kind> wasm_call_return_type() const {
DCHECK_EQ(kind(), kJSToWasmBuiltinContinuation);
return return_type_;
}
private:
friend class TranslatedState;
friend class Deoptimizer;
// Constructor static methods.
static TranslatedFrame InterpretedFrame(BailoutId bytecode_offset,
......@@ -263,6 +275,9 @@ class TranslatedFrame {
int height);
static TranslatedFrame BuiltinContinuationFrame(
BailoutId bailout_id, SharedFunctionInfo shared_info, int height);
static TranslatedFrame JSToWasmBuiltinContinuationFrame(
BailoutId bailout_id, SharedFunctionInfo shared_info, int height,
base::Optional<wasm::ValueType::Kind> return_type);
static TranslatedFrame JavaScriptBuiltinContinuationFrame(
BailoutId bailout_id, SharedFunctionInfo shared_info, int height);
static TranslatedFrame JavaScriptBuiltinContinuationWithCatchFrame(
......@@ -299,6 +314,9 @@ class TranslatedFrame {
using ValuesContainer = std::deque<TranslatedValue>;
ValuesContainer values_;
// Only for Kind == kJSToWasmBuiltinContinuation
base::Optional<wasm::ValueType::Kind> return_type_;
};
// Auxiliary class for translating deoptimization values.
......@@ -578,6 +596,9 @@ class Deoptimizer : public Malloced {
static Builtins::Name TrampolineForBuiltinContinuation(
BuiltinContinuationMode mode, bool must_handle_result);
TranslatedValue TranslatedValueForWasmReturnType(
base::Optional<wasm::ValueType::Kind> wasm_call_return_type);
void DoComputeBuiltinContinuation(TranslatedFrame* translated_frame,
int frame_index,
BuiltinContinuationMode mode);
......@@ -876,6 +897,7 @@ class TranslationIterator {
V(BEGIN) \
V(INTERPRETED_FRAME) \
V(BUILTIN_CONTINUATION_FRAME) \
V(JS_TO_WASM_BUILTIN_CONTINUATION_FRAME) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_FRAME) \
V(JAVA_SCRIPT_BUILTIN_CONTINUATION_WITH_CATCH_FRAME) \
V(CONSTRUCT_STUB_FRAME) \
......@@ -929,6 +951,9 @@ class Translation {
unsigned height);
void BeginBuiltinContinuationFrame(BailoutId bailout_id, int literal_id,
unsigned height);
void BeginJSToWasmBuiltinContinuationFrame(
BailoutId bailout_id, int literal_id, unsigned height,
base::Optional<wasm::ValueType::Kind> return_type);
void BeginJavaScriptBuiltinContinuationFrame(BailoutId bailout_id,
int literal_id, unsigned height);
void BeginJavaScriptBuiltinContinuationWithCatchFrame(BailoutId bailout_id,
......
......@@ -1088,6 +1088,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory {
isolate_root_bias());
}
THREAD_LOCAL_TOP_ADDRESS(Address, thread_in_wasm_flag_address)
MaterializedObjectStore* materialized_object_store() {
return materialized_object_store_;
}
......
......@@ -424,6 +424,7 @@ DEFINE_BOOL(future, FUTURE_BOOL,
DEFINE_WEAK_IMPLICATION(future, write_protect_code_memory)
DEFINE_WEAK_IMPLICATION(future, finalize_streaming_on_background)
DEFINE_WEAK_IMPLICATION(future, super_ic)
DEFINE_WEAK_IMPLICATION(future, turbo_inline_js_wasm_calls)
// Flags for jitless
DEFINE_BOOL(jitless, V8_LITE_BOOL,
......@@ -735,6 +736,7 @@ DEFINE_INT(reuse_opt_code_count, 0,
DEFINE_BOOL(turbo_dynamic_map_checks, true,
"use dynamic map checks when generating code for property accesses "
"if all handlers in an IC are the same for turboprop and NCI")
DEFINE_BOOL(turbo_inline_js_wasm_calls, false, "inline JS->Wasm calls")
// Native context independent (NCI) code.
DEFINE_BOOL(turbo_nci, false,
......
......@@ -526,6 +526,19 @@ void DeoptimizationData::DeoptimizationDataPrint(std::ostream& os) { // NOLINT
break;
}
case Translation::JS_TO_WASM_BUILTIN_CONTINUATION_FRAME: {
int bailout_id = iterator.Next();
int shared_info_id = iterator.Next();
Object shared_info = LiteralArray().get(shared_info_id);
unsigned height = iterator.Next();
int wasm_return_type = iterator.Next();
os << "{bailout_id=" << bailout_id << ", function="
<< SharedFunctionInfo::cast(shared_info).DebugNameCStr().get()
<< ", height=" << height
<< ", wasm_return_type=" << wasm_return_type << "}";
break;
}
case Translation::ARGUMENTS_ADAPTOR_FRAME: {
int shared_info_id = iterator.Next();
Object shared_info = LiteralArray().get(shared_info_id);
......
......@@ -15,6 +15,7 @@
#include "src/objects/scope-info.h"
#include "src/objects/shared-function-info.h"
#include "src/objects/templates.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
// Has to be the last include (doesn't have include guards):
......@@ -656,6 +657,22 @@ bool SharedFunctionInfo::HasWasmJSFunctionData() const {
return function_data(kAcquireLoad).IsWasmJSFunctionData();
}
const wasm::WasmModule* SharedFunctionInfo::wasm_module() const {
if (!HasWasmExportedFunctionData()) return nullptr;
const WasmExportedFunctionData& function_data = wasm_exported_function_data();
const WasmInstanceObject& wasm_instance = function_data.instance();
const WasmModuleObject& wasm_module_object = wasm_instance.module_object();
return wasm_module_object.module();
}
const wasm::FunctionSig* SharedFunctionInfo::wasm_function_signature() const {
const wasm::WasmModule* module = wasm_module();
if (!module) return nullptr;
const WasmExportedFunctionData& function_data = wasm_exported_function_data();
DCHECK_LT(function_data.function_index(), module->functions.size());
return module->functions[function_data.function_index()].sig;
}
bool SharedFunctionInfo::HasWasmCapiFunctionData() const {
return function_data(kAcquireLoad).IsWasmCapiFunctionData();
}
......
......@@ -18,6 +18,7 @@
#include "src/objects/smi.h"
#include "src/objects/struct.h"
#include "src/roots/roots.h"
#include "src/wasm/value-type.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
#include "torque-generated/bit-fields.h"
#include "torque-generated/field-offsets.h"
......@@ -34,10 +35,16 @@ class BytecodeArray;
class CoverageInfo;
class DebugInfo;
class IsCompiledScope;
template <typename>
class Signature;
class WasmCapiFunctionData;
class WasmExportedFunctionData;
class WasmJSFunctionData;
namespace wasm {
struct WasmModule;
} // namespace wasm
#include "torque-generated/src/objects/shared-function-info-tq.inc"
// Data collected by the pre-parser storing information about scopes and inner
......@@ -317,6 +324,9 @@ class SharedFunctionInfo
inline bool HasWasmCapiFunctionData() const;
WasmCapiFunctionData wasm_capi_function_data() const;
inline const wasm::WasmModule* wasm_module() const;
inline const wasm::FunctionSig* wasm_function_signature() const;
// Clear out pre-parsed scope data from UncompiledDataWithPreparseData,
// turning it into UncompiledDataWithoutPreparseData.
inline void ClearPreparseData();
......
// 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.
#include "src/wasm/value-type.h"
#include "src/codegen/signature.h"
namespace v8 {
namespace internal {
namespace wasm {
base::Optional<wasm::ValueType::Kind> WasmReturnTypeFromSignature(
const FunctionSig* wasm_signature) {
if (wasm_signature->return_count() == 0) {
return {};
} else {
DCHECK_EQ(wasm_signature->return_count(), 1);
ValueType return_type = wasm_signature->GetReturn(0);
switch (return_type.kind()) {
case ValueType::kI32:
case ValueType::kI64:
case ValueType::kF32:
case ValueType::kF64:
return {return_type.kind()};
default:
UNREACHABLE();
}
}
}
} // namespace wasm
} // namespace internal
} // namespace v8
......@@ -6,6 +6,7 @@
#define V8_WASM_VALUE_TYPE_H_
#include "src/base/bit-field.h"
#include "src/base/optional.h"
#include "src/codegen/machine-type.h"
#include "src/wasm/wasm-constants.h"
......@@ -679,6 +680,9 @@ class StoreType {
};
};
base::Optional<wasm::ValueType::Kind> WasmReturnTypeFromSignature(
const FunctionSig* wasm_signature);
} // namespace wasm
} // namespace internal
} // namespace v8
......
......@@ -247,6 +247,7 @@ v8_source_set("cctest_sources") {
"test-inobject-slack-tracking.cc",
"test-inspector.cc",
"test-intl.cc",
"test-js-to-wasm.cc",
"test-js-weak-refs.cc",
"test-liveedit.cc",
"test-local-handles.cc",
......
......@@ -365,6 +365,10 @@ static inline v8::Local<v8::Integer> v8_int(int32_t x) {
return v8::Integer::New(v8::Isolate::GetCurrent(), x);
}
static inline v8::Local<v8::BigInt> v8_bigint(int64_t x) {
return v8::BigInt::New(v8::Isolate::GetCurrent(), x);
}
static inline v8::Local<v8::String> v8_str(const char* x) {
return v8::String::NewFromUtf8(v8::Isolate::GetCurrent(), x).ToLocalChecked();
}
......
......@@ -603,6 +603,7 @@
'test-api/TurboAsmDisablesDetach': [SKIP],
'test-cpu-profiler/TickLinesOptimized': [SKIP],
'test-heap/TestOptimizeAfterBytecodeFlushingCandidate': [SKIP],
'test-js-to-wasm/*': [SKIP],
'test-run-wasm-exceptions/RunWasmInterpreter_TryCatchCallDirect': [SKIP],
'test-run-wasm-exceptions/RunWasmInterpreter_TryCatchCallExternal': [SKIP],
'test-run-wasm-exceptions/RunWasmInterpreter_TryCatchCallIndirect': [SKIP],
......@@ -646,6 +647,7 @@
'test-cpu-profiler/DetailedSourcePositionAPI_Inlining': [SKIP],
'serializer-tester/BoundFunctionArguments': [SKIP],
'serializer-tester/BoundFunctionTarget': [SKIP],
'test-js-to-wasm/*': [SKIP],
}], # variant == turboprop
##############################################################################
......
......@@ -27492,126 +27492,6 @@ UNINITIALIZED_TEST(NestedIsolates) {
#ifndef V8_LITE_MODE
namespace {
template <typename T>
struct ConvertJSValue {
static Maybe<T> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context);
};
template <>
struct ConvertJSValue<int32_t> {
static Maybe<int32_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
return value->Int32Value(context);
}
};
template <>
struct ConvertJSValue<uint32_t> {
static Maybe<uint32_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
return value->Uint32Value(context);
}
};
// NaNs and +/-Infinity should be 0, otherwise (modulo 2^64) - 2^63.
// Step 8 - 12 of https://heycam.github.io/webidl/#abstract-opdef-converttoint
// The int64_t and uint64_t implementations below are copied from Blink:
// https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h;l=249?q=doubletointeger&sq=&ss=chromium%2Fchromium%2Fsrc
template <>
struct ConvertJSValue<int64_t> {
static Maybe<int64_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
Maybe<double> double_value = value->NumberValue(context);
if (!double_value.IsJust()) {
return v8::Nothing<int64_t>();
}
double result = double_value.ToChecked();
if (std::isinf(result) || std::isnan(result)) {
return v8::Just(int64_t(0));
}
result = trunc(result);
constexpr uint64_t kMaxULL = std::numeric_limits<uint64_t>::max();
// -2^{64} < fmod_value < 2^{64}.
double fmod_value = fmod(result, kMaxULL + 1.0);
if (fmod_value >= 0) {
if (fmod_value < pow(2, 63)) {
// 0 <= fmod_value < 2^{63}.
// 0 <= value < 2^{63}. This cast causes no loss.
return v8::Just(static_cast<int64_t>(fmod_value));
} else {
// 2^{63} <= fmod_value < 2^{64}.
// 2^{63} <= value < 2^{64}. This cast causes no loss.
return v8::Just(static_cast<int64_t>(fmod_value - pow(2, 64)));
}
}
// -2^{64} < fmod_value < 0.
// 0 < fmod_value_uint64 < 2^{64}. This cast causes no loss.
uint64_t fmod_value_uint64 = static_cast<uint64_t>(-fmod_value);
// -1 < (kMaxULL - fmod_value_uint64) < 2^{64} - 1.
// 0 < value < 2^{64}.
return v8::Just(static_cast<int64_t>(kMaxULL - fmod_value_uint64 + 1));
}
};
template <>
struct ConvertJSValue<uint64_t> {
static Maybe<uint64_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
Maybe<double> double_value = value->NumberValue(context);
if (!double_value.IsJust()) {
return v8::Nothing<uint64_t>();
}
double result = double_value.ToChecked();
if (std::isinf(result) || std::isnan(result)) {
return v8::Just(uint64_t(0));
}
result = trunc(result);
constexpr uint64_t kMaxULL = std::numeric_limits<uint64_t>::max();
// -2^{64} < fmod_value < 2^{64}.
double fmod_value = fmod(result, kMaxULL + 1.0);
if (fmod_value >= 0) {
return v8::Just(static_cast<uint64_t>(fmod_value));
}
// -2^{64} < fmod_value < 0.
// 0 < fmod_value_uint64 < 2^{64}. This cast causes no loss.
uint64_t fmod_value_uint64 = static_cast<uint64_t>(-fmod_value);
// -1 < (kMaxULL - fmod_value_uint64) < 2^{64} - 1.
// 0 < value < 2^{64}.
return v8::Just(static_cast<uint64_t>(kMaxULL - fmod_value_uint64 + 1));
}
};
template <>
struct ConvertJSValue<float> {
static Maybe<float> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
Maybe<double> val = value->NumberValue(context);
if (val.IsNothing()) return v8::Nothing<float>();
return v8::Just(static_cast<float>(val.ToChecked()));
}
};
template <>
struct ConvertJSValue<double> {
static Maybe<double> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
return value->NumberValue(context);
}
};
template <>
struct ConvertJSValue<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 Value, typename Impl>
struct BasicApiChecker {
static void FastCallback(v8::ApiObject receiver, Value argument,
......@@ -52,4 +52,135 @@ static void CheckInternalFieldsAreZero(v8::Local<T> value) {
}
}
template <typename T>
struct ConvertJSValue {
static v8::Maybe<T> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context);
};
template <>
struct ConvertJSValue<int32_t> {
static v8::Maybe<int32_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
return value->Int32Value(context);
}
};
template <>
struct ConvertJSValue<uint32_t> {
static v8::Maybe<uint32_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
return value->Uint32Value(context);
}
};
// NaNs and +/-Infinity should be 0, otherwise (modulo 2^64) - 2^63.
// Step 8 - 12 of https://heycam.github.io/webidl/#abstract-opdef-converttoint
// The int64_t and uint64_t implementations below are copied from Blink:
// https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h;l=249?q=doubletointeger&sq=&ss=chromium%2Fchromium%2Fsrc
template <>
struct ConvertJSValue<int64_t> {
static v8::Maybe<int64_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
v8::Maybe<double> double_value = value->NumberValue(context);
if (!double_value.IsJust()) {
return v8::Nothing<int64_t>();
}
double result = double_value.ToChecked();
if (std::isinf(result) || std::isnan(result)) {
return v8::Just(int64_t(0));
}
result = trunc(result);
constexpr uint64_t kMaxULL = std::numeric_limits<uint64_t>::max();
// -2^{64} < fmod_value < 2^{64}.
double fmod_value = fmod(result, kMaxULL + 1.0);
if (fmod_value >= 0) {
if (fmod_value < pow(2, 63)) {
// 0 <= fmod_value < 2^{63}.
// 0 <= value < 2^{63}. This cast causes no loss.
return v8::Just(static_cast<int64_t>(fmod_value));
} else {
// 2^{63} <= fmod_value < 2^{64}.
// 2^{63} <= value < 2^{64}. This cast causes no loss.
return v8::Just(static_cast<int64_t>(fmod_value - pow(2, 64)));
}
}
// -2^{64} < fmod_value < 0.
// 0 < fmod_value_uint64 < 2^{64}. This cast causes no loss.
uint64_t fmod_value_uint64 = static_cast<uint64_t>(-fmod_value);
// -1 < (kMaxULL - fmod_value_uint64) < 2^{64} - 1.
// 0 < value < 2^{64}.
return v8::Just(static_cast<int64_t>(kMaxULL - fmod_value_uint64 + 1));
}
};
template <>
struct ConvertJSValue<uint64_t> {
static v8::Maybe<uint64_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
v8::Maybe<double> double_value = value->NumberValue(context);
if (!double_value.IsJust()) {
return v8::Nothing<uint64_t>();
}
double result = double_value.ToChecked();
if (std::isinf(result) || std::isnan(result)) {
return v8::Just(uint64_t(0));
}
result = trunc(result);
constexpr uint64_t kMaxULL = std::numeric_limits<uint64_t>::max();
// -2^{64} < fmod_value < 2^{64}.
double fmod_value = fmod(result, kMaxULL + 1.0);
if (fmod_value >= 0) {
return v8::Just(static_cast<uint64_t>(fmod_value));
}
// -2^{64} < fmod_value < 0.
// 0 < fmod_value_uint64 < 2^{64}. This cast causes no loss.
uint64_t fmod_value_uint64 = static_cast<uint64_t>(-fmod_value);
// -1 < (kMaxULL - fmod_value_uint64) < 2^{64} - 1.
// 0 < value < 2^{64}.
return v8::Just(static_cast<uint64_t>(kMaxULL - fmod_value_uint64 + 1));
}
};
template <>
struct ConvertJSValue<v8::BigInt> {
static v8::Maybe<v8::Local<v8::BigInt>> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
if (value->IsBigInt()) {
return v8::Just(value.As<v8::BigInt>());
}
return v8::Nothing<v8::Local<v8::BigInt>>();
}
};
template <>
struct ConvertJSValue<float> {
static v8::Maybe<float> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
v8::Maybe<double> val = value->NumberValue(context);
if (val.IsNothing()) return v8::Nothing<float>();
return v8::Just(static_cast<float>(val.ToChecked()));
}
};
template <>
struct ConvertJSValue<double> {
static v8::Maybe<double> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
return value->NumberValue(context);
}
};
template <>
struct ConvertJSValue<bool> {
static v8::Maybe<bool> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
return v8::Just<bool>(value->BooleanValue(CcTest::isolate()));
}
};
#endif // V8_TEST_CCTEST_TEST_API_H_
This diff is collapsed.
......@@ -47,6 +47,7 @@ class TestSignatures {
sig_v_iii(0, 3, kIntTypes4),
sig_v_e(0, 1, kExternRefTypes4),
sig_v_c(0, 1, kFuncTypes4),
sig_v_d(0, 1, kDoubleTypes4),
sig_s_i(1, 1, kSimd128IntTypes4),
sig_s_s(1, 1, kSimd128Types4),
sig_s_ss(1, 2, kSimd128Types4),
......@@ -111,6 +112,7 @@ class TestSignatures {
FunctionSig* v_iii() { return &sig_v_iii; }
FunctionSig* v_e() { return &sig_v_e; }
FunctionSig* v_c() { return &sig_v_c; }
FunctionSig* v_d() { return &sig_v_d; }
FunctionSig* s_i() { return &sig_s_i; }
FunctionSig* s_s() { return &sig_s_s; }
FunctionSig* s_ss() { return &sig_s_ss; }
......@@ -178,6 +180,7 @@ class TestSignatures {
FunctionSig sig_v_iii;
FunctionSig sig_v_e;
FunctionSig sig_v_c;
FunctionSig sig_v_d;
FunctionSig sig_s_i;
FunctionSig sig_s_s;
FunctionSig sig_s_ss;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment