860fcb1bd2
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}
187 lines
6.5 KiB
C++
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_
|