Skip to content

Commit

Permalink
[Wasm-GC] Add nullexternref and nullfuncref types
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=254695

Reviewed by Yusuke Suzuki.

* JSTests/wasm/gc/any.js:
(testNullfuncref):
(testNullexternref):
* Source/JavaScriptCore/wasm/WasmBBQJIT.cpp:
(JSC::Wasm::BBQJIT::sizeOfType):
(JSC::Wasm::BBQJIT::toValueKind):
(JSC::Wasm::BBQJIT::addConstant):
(JSC::Wasm::BBQJIT::getGlobal):
(JSC::Wasm::BBQJIT::setGlobal):
(JSC::Wasm::BBQJIT::addTopLevel):
(JSC::Wasm::BBQJIT::toB3Type):
(JSC::Wasm::BBQJIT::emitCatchImpl):
(JSC::Wasm::BBQJIT::emitCCall):
(JSC::Wasm::BBQJIT::emitStoreConst):
(JSC::Wasm::BBQJIT::emitMoveConst):
(JSC::Wasm::BBQJIT::emitStore):
(JSC::Wasm::BBQJIT::emitMoveMemory):
(JSC::Wasm::BBQJIT::emitMoveRegister):
(JSC::Wasm::BBQJIT::emitLoad):
* Source/JavaScriptCore/wasm/WasmCallingConvention.h:
(JSC::Wasm::WasmCallingConvention::numberOfStackResults const):
(JSC::Wasm::WasmCallingConvention::numberOfStackArguments const):
* Source/JavaScriptCore/wasm/WasmFormat.h:
(JSC::Wasm::isNullfuncref):
(JSC::Wasm::isNullexternref):
(JSC::Wasm::isSubtype):
(JSC::Wasm::isValidHeapTypeKind):
* Source/JavaScriptCore/wasm/WasmLLIntGenerator.cpp:
(JSC::Wasm::LLIntGenerator::callInformationForCaller):
(JSC::Wasm::LLIntGenerator::callInformationForCallee):
(JSC::Wasm::LLIntGenerator::addArguments):
* Source/JavaScriptCore/wasm/WasmTypeDefinition.cpp:
(JSC::Wasm::TypeInformation::castReference):
* Source/JavaScriptCore/wasm/WasmTypeDefinition.h:
(JSC::Wasm::typeKindSizeInBytes):
* Source/JavaScriptCore/wasm/js/JSWebAssemblyStruct.cpp:
(JSC::JSWebAssemblyStruct::set):
* Source/JavaScriptCore/wasm/js/WasmToJS.cpp:
(JSC::Wasm::wasmToJS):
* Source/JavaScriptCore/wasm/wasm.json:

Canonical link: https://commits.webkit.org/266766@main
  • Loading branch information
takikawa committed Aug 10, 2023
1 parent f722291 commit 697f8d5
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 22 deletions.
80 changes: 80 additions & 0 deletions JSTests/wasm/gc/any.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,86 @@ function testNullref() {
`).exports.f();
}

function testNullfuncref() {
instantiate(`
(module
(func (param nullfuncref) (result))
(func (export "f") (call 0 (ref.null nofunc))))
`).exports.f();

instantiate(`
(module
(func (export "f") (result funcref) (ref.null nofunc)))
`).exports.f();

assert.throws(
() => compile(`
(module
(func (export "f") (result nullfuncref) (ref.null func)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Funcref is not a Nullfuncref"
)

assert.throws(
() => compile(`
(module
(func (export "f") (result nullref) (ref.null nofunc)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Nullfuncref is not a Nullref"
)

instantiate(`
(module
(func (result nullfuncref) (local nullfuncref)
(local.set 0 (ref.null nofunc))
(local.get 0))
(func (export "f") (call 0) (drop)))
`).exports.f();
}

function testNullexternref() {
instantiate(`
(module
(func (param nullexternref) (result))
(func (export "f") (call 0 (ref.null noextern))))
`).exports.f();

instantiate(`
(module
(func (export "f") (result externref) (ref.null noextern)))
`).exports.f();

assert.throws(
() => compile(`
(module
(func (export "f") (result nullexternref) (ref.null extern)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Externref is not a Nullexternref"
)

assert.throws(
() => compile(`
(module
(func (export "f") (result nullref) (ref.null noextern)))
`),
WebAssembly.CompileError,
"WebAssembly.Module doesn't validate: control flow returns with unexpected type. Nullexternref is not a Nullref"
)

instantiate(`
(module
(func (result nullexternref) (local nullexternref)
(local.set 0 (ref.null noextern))
(local.get 0))
(func (export "f") (call 0) (drop)))
`).exports.f();
}

testValidation();
testAnyref();
testNullref();
testNullfuncref();
testNullexternref();
50 changes: 50 additions & 0 deletions JSTests/wasm/gc/casts.js
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,56 @@ function testNullCasts() {
0
);

assert.throws(
() => instantiate(`
(module
(start 1)
(func (param funcref) (result nullfuncref)
(ref.cast (ref nofunc) (local.get 0)))
(func
(call 0 (ref.null func))
drop))
`),
WebAssembly.RuntimeError,
"ref.cast failed to cast reference to target heap type"
)

assert.eq(
instantiate(`
(module
(func (param funcref) (result i32)
(ref.test (ref nofunc) (local.get 0)))
(func (export "f") (result i32)
(call 0 (ref.null func))))
`).exports.f(),
0
);

assert.throws(
() => instantiate(`
(module
(start 1)
(func (param externref) (result nullexternref)
(ref.cast (ref noextern) (local.get 0)))
(func
(call 0 (ref.null extern))
drop))
`),
WebAssembly.RuntimeError,
"ref.cast failed to cast reference to target heap type"
)

assert.eq(
instantiate(`
(module
(func (param externref) (result i32)
(ref.test (ref noextern) (local.get 0)))
(func (export "f") (result i32)
(call 0 (ref.null extern))))
`).exports.f(),
0
);

assert.throws(
() => instantiate(`
(module
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/wasm/WasmAirIRGeneratorBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -2871,6 +2871,8 @@ void AirIRGeneratorBase<Derived, ExpressionType>::emitRefTestOrCast(CastKind cas
// Casts to these types cannot fail as they are the top types of their respective hierarchies, and static type-checking does not allow cross-hierarchy casts.
break;
case Wasm::TypeKind::Nullref:
case Wasm::TypeKind::Nullfuncref:
case Wasm::TypeKind::Nullexternref:
// Casts to any bottom type should always fail.
if (castKind == CastKind::Cast) {
B3::PatchpointValue* throwException = addPatchpoint(B3::Void);
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3389,6 +3389,8 @@ void B3IRGenerator::emitRefTestOrCast(CastKind castKind, ExpressionType referenc
// Casts to these types cannot fail as they are the top types of their respective hierarchies, and static type-checking does not allow cross-hierarchy casts.
break;
case Wasm::TypeKind::Nullref:
case Wasm::TypeKind::Nullfuncref:
case Wasm::TypeKind::Nullexternref:
// Casts to any bottom type should always fail.
if (castKind == CastKind::Cast) {
B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, origin());
Expand Down
30 changes: 30 additions & 0 deletions Source/JavaScriptCore/wasm/WasmBBQJIT.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
return sizeof(EncodedJSValue);
case TypeKind::Void:
return 0;
Expand Down Expand Up @@ -428,6 +430,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
return TypeKind::I64;
case TypeKind::Void:
RELEASE_ASSERT_NOT_REACHED();
Expand Down Expand Up @@ -1428,6 +1432,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
result = Value::fromRef(type.kind, static_cast<EncodedJSValue>(value));
LOG_INSTRUCTION("RefConst", makeString(type.kind), RESULT(result));
break;
Expand Down Expand Up @@ -1718,6 +1724,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
m_jit.load64(Address(wasmScratchGPR), resultLocation.asGPR());
break;
case TypeKind::Void:
Expand Down Expand Up @@ -1833,6 +1841,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
m_jit.store64(valueLocation.asGPR(), Address(wasmScratchGPR));
break;
case TypeKind::Void:
Expand Down Expand Up @@ -6825,6 +6835,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
clear(ClearMode::JSNull, type, m_locals[i]);
break;
default:
Expand Down Expand Up @@ -6924,6 +6936,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
return B3::Type(B3::Int64);
case TypeKind::F32:
return B3::Type(B3::Float);
Expand Down Expand Up @@ -7173,6 +7187,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
case TypeKind::Rec:
case TypeKind::Sub:
case TypeKind::Array:
Expand Down Expand Up @@ -7841,6 +7857,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
case TypeKind::Rec:
case TypeKind::Sub:
case TypeKind::Array:
Expand Down Expand Up @@ -9349,6 +9367,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
m_jit.store64(TrustedImm64(constant.asRef()), loc.asAddress());
break;
case TypeKind::I64:
Expand Down Expand Up @@ -9390,6 +9410,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
m_jit.move(TrustedImm64(constant.asRef()), loc.asGPR());
break;
case TypeKind::F32:
Expand Down Expand Up @@ -9432,6 +9454,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
m_jit.store64(src.asGPR(), dst.asAddress());
break;
case TypeKind::V128:
Expand Down Expand Up @@ -9481,6 +9505,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
m_jit.transfer64(src.asAddress(), dst.asAddress());
break;
case TypeKind::V128: {
Expand Down Expand Up @@ -9522,6 +9548,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
m_jit.move(src.asGPR(), dst.asGPR());
break;
case TypeKind::F32:
Expand Down Expand Up @@ -9572,6 +9600,8 @@ class BBQJIT {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
m_jit.load64(src.asAddress(), dst.asGPR());
break;
case TypeKind::V128:
Expand Down
4 changes: 4 additions & 0 deletions Source/JavaScriptCore/wasm/WasmCallingConvention.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ class WasmCallingConvention {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
case TypeKind::I31ref:
case TypeKind::Sub:
case TypeKind::Rec:
Expand Down Expand Up @@ -231,6 +233,8 @@ class WasmCallingConvention {
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
case TypeKind::I31ref:
case TypeKind::Sub:
case TypeKind::Rec:
Expand Down
22 changes: 22 additions & 0 deletions Source/JavaScriptCore/wasm/WasmFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,20 @@ inline bool isNullref(Type type)
return isRefType(type) && type.index == static_cast<TypeIndex>(TypeKind::Nullref);
}

inline bool isNullfuncref(Type type)
{
if (!Options::useWebAssemblyGC())
return false;
return isRefType(type) && type.index == static_cast<TypeIndex>(TypeKind::Nullfuncref);
}

inline bool isNullexternref(Type type)
{
if (!Options::useWebAssemblyGC())
return false;
return isRefType(type) && type.index == static_cast<TypeIndex>(TypeKind::Nullexternref);
}

inline bool isInternalref(Type type)
{
if (!Options::useWebAssemblyGC() || !isRefType(type) || !typeIndexIsType(type.index))
Expand Down Expand Up @@ -307,6 +321,12 @@ inline bool isSubtype(Type sub, Type parent)
if (isNullref(sub))
return isInternalref(parent);

if (isNullfuncref(sub))
return isSubtype(parent, funcrefType());

if (isNullexternref(sub))
return sub == parent || isExternref(parent);

if (sub.isRef() && parent.isRefNull())
return sub.index == parent.index;

Expand Down Expand Up @@ -334,6 +354,8 @@ inline bool isValidHeapTypeKind(TypeKind kind)
case TypeKind::Eqref:
case TypeKind::Anyref:
case TypeKind::Nullref:
case TypeKind::Nullfuncref:
case TypeKind::Nullexternref:
return Options::useWebAssemblyGC();
default:
break;
Expand Down
4 changes: 3 additions & 1 deletion Source/JavaScriptCore/wasm/WasmFunctionParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -2190,10 +2190,12 @@ FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE)
TypeIndex resultTypeIndex = static_cast<TypeIndex>(heapType);
switch (static_cast<TypeKind>(heapType)) {
case TypeKind::Funcref:
case TypeKind::Nullfuncref:
WASM_VALIDATOR_FAIL_IF(!isSubtype(ref.type(), funcrefType()), opName, " to type ", ref.type(), " expected a funcref");
break;
case TypeKind::Externref:
WASM_VALIDATOR_FAIL_IF(!isExternref(ref.type()), opName, " to type ", ref.type(), " expected an externref");
case TypeKind::Nullexternref:
WASM_VALIDATOR_FAIL_IF(!isSubtype(ref.type(), externrefType()), opName, " to type ", ref.type(), " expected an externref");
break;
case TypeKind::Eqref:
case TypeKind::Anyref:
Expand Down
Loading

0 comments on commit 697f8d5

Please sign in to comment.