Commit 5d587693 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm][anyref] Implement sub-typing of anyref, anyfunc, and nullref

For the reference types anyref, anyfunc, and nullref, there exist
sub-typing rules. The spec says

A reference type reftype1 matches a reference type reftype2 if and only
if:

* Either both reftype1 and reftype2 are the same.
* Or reftype1 is nullref.
* Or reftype2 is anyref.

This CL introduces the type nullref for ref-null, and implements the
sub-typing rules in the function-body-decoder.

Note that because of the sub-typing check validation performance may
regresses. In that case we can optimize the sub-typing check.

R=titzer@chromium.org, clemensh@chromium.org

Bug: v8:7581
Change-Id: I55bab72a109f3374da3770d141b0fc8067aad8b2
Reviewed-on: https://chromium-review.googlesource.com/c/1430061
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59043}
parent 304e74c8
......@@ -1902,7 +1902,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
case kExprRefNull: {
CHECK_PROTOTYPE_OPCODE(anyref);
auto* value = Push(kWasmAnyRef);
auto* value = Push(kWasmNullRef);
CALL_INTERFACE_IF_REACHABLE(RefNull, value);
len = 1;
break;
......@@ -2563,9 +2563,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return stack_.data() + old_size;
}
V8_INLINE bool IsSubType(ValueType expected, ValueType actual) {
return (expected == actual) ||
(expected == kWasmAnyRef && actual == kWasmNullRef) ||
(expected == kWasmAnyRef && actual == kWasmAnyFunc) ||
(expected == kWasmAnyFunc && actual == kWasmNullRef);
}
V8_INLINE Value Pop(int index, ValueType expected) {
auto val = Pop();
if (!VALIDATE(val.type == expected || val.type == kWasmVar ||
if (!VALIDATE(IsSubType(expected, val.type) || val.type == kWasmVar ||
expected == kWasmVar)) {
this->errorf(val.pc, "%s[%d] expected type %s, found %s of type %s",
SafeOpcodeNameAt(this->pc_), index,
......@@ -2612,7 +2619,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
for (uint32_t i = 0; i < merge->arity; ++i) {
Value& val = stack_values[i];
Value& old = (*merge)[i];
if (val.type == old.type) continue;
if (IsSubType(old.type, val.type)) continue;
// If {val.type} is polymorphic, which results from unreachable, make
// it more specific by using the merge value's expected type.
// If it is not polymorphic, this is a type error.
......@@ -2683,7 +2690,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
for (uint32_t i = 0; i < num_returns; ++i) {
auto& val = stack_values[i];
ValueType expected_type = this->sig_->GetReturn(i);
if (val.type == expected_type) continue;
if (IsSubType(expected_type, val.type)) continue;
// If {val.type} is polymorphic, which results from unreachable,
// make it more specific by using the return's expected type.
// If it is not polymorphic, this is a type error.
......
......@@ -25,6 +25,7 @@ enum ValueType : uint8_t {
kWasmS128,
kWasmAnyRef,
kWasmAnyFunc,
kWasmNullRef,
kWasmExceptRef,
kWasmVar,
};
......@@ -341,6 +342,8 @@ class V8_EXPORT_PRIVATE ValueTypes {
return "anyref";
case kWasmAnyFunc:
return "anyfunc";
case kWasmNullRef:
return "nullref";
case kWasmExceptRef:
return "exn";
case kWasmS128:
......
......@@ -136,3 +136,65 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testAssignNullRefToAnyFuncLocal() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_a);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kExprSetLocal, 0, kExprGetLocal, 0])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main(main));
})();
(function testImplicitReturnNullRefAsAnyFunc() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testExplicitReturnNullRefAsAnyFunc() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kExprReturn])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testImplicitReturnAnyFuncAsAnyRef() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_r_v);
builder.addFunction('main', sig_index)
.addLocals({anyfunc_count: 1})
.addBody([kExprGetLocal, 0])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testExplicitReturnAnyFuncAsAnyRef() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_r_v);
builder.addFunction('main', sig_index)
.addLocals({anyfunc_count: 1})
.addBody([kExprGetLocal, 0, kExprReturn])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
......@@ -181,3 +181,27 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
assertEquals(null, instance.exports.main());
})();
(function testImplicitReturnNullRefAsAnyRef() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_r_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
(function testExplicitReturnNullRefAsAnyRef() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_r_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kExprReturn])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
})();
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