[wasm-gc] Allow Js -> Wasm wrapper inlining for (non-null) ref extern

This is a follow-up to https://crrev.com/c/4204032 which allowed
wrapper inlining for the nullable externref type.

Bug: v8:7748
Change-Id: I5a82c37b7cf0cfcbcacbe399f8b3119176c3bba4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4212394
Commit-Queue: Matthias Liedtke <mliedtke@chromium.org>
Reviewed-by: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: Darius Mercadier <dmercadier@chromium.org>
Auto-Submit: Matthias Liedtke <mliedtke@chromium.org>
Cr-Commit-Position: refs/heads/main@{#85598}
This commit is contained in:
Matthias Liedtke 2023-02-01 15:44:24 +01:00 committed by V8 LUCI CQ
parent 8082a8f51a
commit 811d50b9c1
6 changed files with 93 additions and 23 deletions

View File

@ -3554,13 +3554,14 @@ bool CanInlineJSToWasmCall(const wasm::FunctionSig* wasm_signature) {
return false;
}
wasm::ValueType externRefNonNull = wasm::kWasmExternRef.AsNonNull();
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 &&
type != wasm::kWasmExternRef) {
type != wasm::kWasmExternRef && type != externRefNonNull) {
return false;
}
}

View File

@ -730,6 +730,7 @@ Type JSWasmCallNode::TypeForWasmReturnType(const wasm::ValueType& type) {
case wasm::kF32:
case wasm::kF64:
return Type::Number();
case wasm::kRef:
case wasm::kRefNull:
CHECK_EQ(type.heap_type(), wasm::HeapType::kExtern);
return Type::Any();

View File

@ -2033,6 +2033,7 @@ class RepresentationSelector {
return MachineType::Float32();
case wasm::kF64:
return MachineType::Float64();
case wasm::kRef:
case wasm::kRefNull:
return MachineType::AnyTagged();
default:
@ -2057,6 +2058,7 @@ class RepresentationSelector {
// WasmWrapperGraphBuilder::BuildJSToWasmWrapper.
return UseInfo::CheckedNumberOrOddballAsFloat64(kDistinguishZeros,
feedback);
case wasm::kRef:
case wasm::kRefNull:
return UseInfo::AnyTagged();
default:

View File

@ -72,6 +72,14 @@ struct ConvertJSValue<uint32_t> {
}
};
template <>
struct ConvertJSValue<std::nullptr_t> {
static v8::Maybe<std::nullptr_t> Get(v8::Local<v8::Value> value,
v8::Local<v8::Context> context) {
return value->IsNull() ? v8::Just(nullptr) : v8::Nothing<std::nullptr_t>();
}
};
// 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:

View File

@ -47,11 +47,16 @@ bool CheckType<v8::Local<v8::String>>(v8::Local<v8::Value> result) {
return result->IsString();
}
template <>
bool CheckType<std::nullptr_t>(v8::Local<v8::Value> result) {
return result->IsNull();
}
static TestSignatures sigs;
struct ExportedFunction {
std::string name;
FunctionSig* signature;
const FunctionSig* signature;
std::vector<ValueType> locals;
std::vector<uint8_t> code;
@ -84,7 +89,14 @@ DECLARE_EXPORTED_FUNCTION(i64_square, sigs.l_l(),
WASM_CODE({WASM_LOCAL_GET(0), WASM_LOCAL_GET(0),
kExprI64Mul}))
DECLARE_EXPORTED_FUNCTION(externref_id, sigs.a_a(),
DECLARE_EXPORTED_FUNCTION(externref_null_id, sigs.a_a(),
WASM_CODE({WASM_LOCAL_GET(0)}))
static constexpr ValueType extern_extern_types[] = {kWasmExternRef.AsNonNull(),
kWasmExternRef.AsNonNull()};
static constexpr FunctionSig sig_extern_extern(1, 1, extern_extern_types);
DECLARE_EXPORTED_FUNCTION(externref_id, &sig_extern_extern,
WASM_CODE({WASM_LOCAL_GET(0)}))
DECLARE_EXPORTED_FUNCTION(f32_square, sigs.f_f(),
@ -273,6 +285,7 @@ class FastJSWasmCallTester {
: allocator_(),
zone_(&allocator_, ZONE_NAME),
builder_(zone_.New<WasmModuleBuilder>(&zone_)) {
i::v8_flags.experimental_wasm_typed_funcref = true;
i::v8_flags.allow_natives_syntax = true;
i::v8_flags.turbo_inline_js_wasm_calls = true;
i::v8_flags.stress_background_compile = false;
@ -817,6 +830,19 @@ TEST(TestFastJSWasmCall_I64NegativeResult) {
"i64_add", {v8_bigint(1ll), v8_bigint(-2ll)}, v8_bigint(-1ll));
}
TEST(TestFastJSWasmCall_ExternrefNullArg) {
v8::HandleScope scope(CcTest::isolate());
FastJSWasmCallTester tester;
tester.AddExportedFunction(k_externref_null_id);
Local<Primitive> v8_null = v8::Null(CcTest::isolate());
tester.CallAndCheckWasmFunction("externref_null_id", {v8_null}, nullptr);
tester.CallAndCheckWasmFunction("externref_null_id", {v8_num(42)}, 42);
tester.CallAndCheckWasmFunctionBigInt("externref_null_id", {v8_bigint(42)},
v8_bigint(42));
auto str = v8_str("test");
tester.CallAndCheckWasmFunction("externref_null_id", {str}, str);
}
TEST(TestFastJSWasmCall_ExternrefArg) {
v8::HandleScope scope(CcTest::isolate());
FastJSWasmCallTester tester;

View File

@ -6,12 +6,23 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
function testOptimized(run, fctToOptimize) {
%PrepareFunctionForOptimization(fctToOptimize);
for (let i = 0; i < 10; ++i) {
run();
}
%OptimizeFunctionOnNextCall(fctToOptimize);
for (let i = 0; i < 10; ++i) {
run();
}
}
(function TestInliningStructGet() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let struct = builder.addStruct([makeField(kWasmI32, true)]);
builder.addFunction('createStruct', makeSig([kWasmI32], [kWasmExternRef]))
builder.addFunction('createStructNull', makeSig([kWasmI32], [kWasmExternRef]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct,
@ -19,7 +30,25 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
])
.exportFunc();
builder.addFunction('getElement', makeSig([kWasmExternRef], [kWasmI32]))
builder.addFunction('getElementNull', makeSig([kWasmExternRef], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprExternInternalize,
kGCPrefix, kExprRefCast, struct,
kGCPrefix, kExprStructGet, struct, 0])
.exportFunc();
builder.addFunction('createStruct',
makeSig([kWasmI32], [wasmRefType(kWasmExternRef)]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprStructNew, struct,
kGCPrefix, kExprExternExternalize,
])
.exportFunc();
builder.addFunction('getElement',
makeSig([wasmRefType(kWasmExternRef)], [kWasmI32]))
.addBody([
kExprLocalGet, 0,
kGCPrefix, kExprExternInternalize,
@ -31,24 +60,27 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let wasm = instance.exports;
let n = 100;
const createStruct = wasm.createStruct;
const getElement = wasm.getElement;
for (let [create, get] of [
[wasm.createStruct, wasm.getElement],
[wasm.createStructNull, wasm.getElementNull]]) {
let fct = () => {
let res = 0;
for (let i = 1; i <= n; ++i) {
const struct = create(i);
res += get(struct);
}
return res;
};
testOptimized(() => assertEquals((n * n + n) / 2, fct()), fct);
let fct = () => {
let res = 0;
for (let i = 1; i <= n; ++i) {
const struct = createStruct(i);
res += getElement(struct);
}
return res;
}
%PrepareFunctionForOptimization(fct);
for (let i = 0; i < 10; ++i) {
assertEquals((n * n + n) / 2, fct());
}
%OptimizeFunctionOnNextCall(fct);
for (let i = 0; i < 10; ++i) {
assertEquals((n * n + n) / 2, fct());
let getNull = () => get(null);
let fctNullValue = () => {
for (let i = 1; i <= n; ++i) {
// Depending on the param type (ref / ref.null), either the wrapper or
// the cast inside the function will throw.
assertThrows(getNull);
}
};
testOptimized(fctNullValue, getNull);
}
})();