v8/test/cctest/test-api.h
Paolo Severini 860fcb1bd2 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/+/2557538
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Reviewed-by: Georg Neis (ooo until January 5) <neis@chromium.org>
Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Commit-Queue: Paolo Severini <paolosev@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#71824}
2020-12-17 10:57:53 +00:00

187 lines
6.5 KiB
C++

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_TEST_CCTEST_TEST_API_H_
#define V8_TEST_CCTEST_TEST_API_H_
#include "src/init/v8.h"
#include "src/api/api.h"
#include "src/execution/isolate.h"
#include "src/execution/vm-state.h"
#include "test/cctest/cctest.h"
template <typename T>
static void CheckReturnValue(const T& t, i::Address callback) {
v8::ReturnValue<v8::Value> rv = t.GetReturnValue();
i::FullObjectSlot o(*reinterpret_cast<i::Address*>(&rv));
CHECK_EQ(CcTest::isolate(), t.GetIsolate());
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(t.GetIsolate());
CHECK_EQ(t.GetIsolate(), rv.GetIsolate());
CHECK((*o).IsTheHole(isolate) || (*o).IsUndefined(isolate));
// Verify reset
bool is_runtime = (*o).IsTheHole(isolate);
if (is_runtime) {
CHECK(rv.Get()->IsUndefined());
} else {
i::Handle<i::Object> v = v8::Utils::OpenHandle(*rv.Get());
CHECK_EQ(*v, *o);
}
rv.Set(true);
CHECK(!(*o).IsTheHole(isolate) && !(*o).IsUndefined(isolate));
rv.Set(v8::Local<v8::Object>());
CHECK((*o).IsTheHole(isolate) || (*o).IsUndefined(isolate));
CHECK_EQ(is_runtime, (*o).IsTheHole(isolate));
// If CPU profiler is active check that when API callback is invoked
// VMState is set to EXTERNAL.
if (isolate->is_profiling()) {
CHECK_EQ(v8::EXTERNAL, isolate->current_vm_state());
CHECK(isolate->external_callback_scope());
CHECK_EQ(callback, isolate->external_callback_scope()->callback());
}
}
template <typename T>
static void CheckInternalFieldsAreZero(v8::Local<T> value) {
CHECK_EQ(T::kInternalFieldCount, value->InternalFieldCount());
for (int i = 0; i < value->InternalFieldCount(); i++) {
CHECK_EQ(0, value->GetInternalField(i)
->Int32Value(CcTest::isolate()->GetCurrentContext())
.FromJust());
}
}
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_