Commit dbc8aa87 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Add type immediate to RefNull and RefIsNull instructions

With recent changes to the anyref proposal, null refs now have a type
immediate which declares the type of a null ref constant. Likewise,
the RefIsNull instruction is type aware now. This CL addresses these
proposal changes now.

R=jkummerow@chromium.org

Bug: v8:10556
Change-Id: I810dfa3a4ab4389afc9639f897cee5d43e9b62cb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2215172
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68141}
parent 490f3580
......@@ -128,81 +128,6 @@ struct WasmException;
V(I64AtomicStore16U, Uint16) \
V(I64AtomicStore32U, Uint32)
// Helpers for decoding different kinds of immediates which follow bytecodes.
template <Decoder::ValidateFlag validate>
struct LocalIndexImmediate {
uint32_t index;
ValueType type = kWasmStmt;
uint32_t length;
inline LocalIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc + 1, &length, "local index");
}
};
template <Decoder::ValidateFlag validate>
struct ExceptionIndexImmediate {
uint32_t index;
const WasmException* exception = nullptr;
uint32_t length;
inline ExceptionIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc + 1, &length, "exception index");
}
};
template <Decoder::ValidateFlag validate>
struct ImmI32Immediate {
int32_t value;
uint32_t length;
inline ImmI32Immediate(Decoder* decoder, const byte* pc) {
value = decoder->read_i32v<validate>(pc + 1, &length, "immi32");
}
};
template <Decoder::ValidateFlag validate>
struct ImmI64Immediate {
int64_t value;
uint32_t length;
inline ImmI64Immediate(Decoder* decoder, const byte* pc) {
value = decoder->read_i64v<validate>(pc + 1, &length, "immi64");
}
};
template <Decoder::ValidateFlag validate>
struct ImmF32Immediate {
float value;
uint32_t length = 4;
inline ImmF32Immediate(Decoder* decoder, const byte* pc) {
// Avoid bit_cast because it might not preserve the signalling bit of a NaN.
uint32_t tmp = decoder->read_u32<validate>(pc + 1, "immf32");
memcpy(&value, &tmp, sizeof(value));
}
};
template <Decoder::ValidateFlag validate>
struct ImmF64Immediate {
double value;
uint32_t length = 8;
inline ImmF64Immediate(Decoder* decoder, const byte* pc) {
// Avoid bit_cast because it might not preserve the signalling bit of a NaN.
uint64_t tmp = decoder->read_u64<validate>(pc + 1, "immf64");
memcpy(&value, &tmp, sizeof(value));
}
};
template <Decoder::ValidateFlag validate>
struct GlobalIndexImmediate {
uint32_t index;
ValueType type = kWasmStmt;
const WasmGlobal* global = nullptr;
uint32_t length;
inline GlobalIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc + 1, &length, "global index");
}
};
namespace value_type_reader {
// Read a value type starting at address 'pc' in 'decoder'.
......@@ -345,6 +270,94 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
}
} // namespace value_type_reader
// Helpers for decoding different kinds of immediates which follow bytecodes.
template <Decoder::ValidateFlag validate>
struct LocalIndexImmediate {
uint32_t index;
ValueType type = kWasmStmt;
uint32_t length;
inline LocalIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc + 1, &length, "local index");
}
};
template <Decoder::ValidateFlag validate>
struct ExceptionIndexImmediate {
uint32_t index;
const WasmException* exception = nullptr;
uint32_t length;
inline ExceptionIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc + 1, &length, "exception index");
}
};
template <Decoder::ValidateFlag validate>
struct ImmI32Immediate {
int32_t value;
uint32_t length;
inline ImmI32Immediate(Decoder* decoder, const byte* pc) {
value = decoder->read_i32v<validate>(pc + 1, &length, "immi32");
}
};
template <Decoder::ValidateFlag validate>
struct ImmI64Immediate {
int64_t value;
uint32_t length;
inline ImmI64Immediate(Decoder* decoder, const byte* pc) {
value = decoder->read_i64v<validate>(pc + 1, &length, "immi64");
}
};
template <Decoder::ValidateFlag validate>
struct ImmF32Immediate {
float value;
uint32_t length = 4;
inline ImmF32Immediate(Decoder* decoder, const byte* pc) {
// We can't use bit_cast here because calling any helper function that
// returns a float would potentially flip NaN bits per C++ semantics, so we
// have to inline the memcpy call directly.
uint32_t tmp = decoder->read_u32<validate>(pc + 1, "immf32");
memcpy(&value, &tmp, sizeof(value));
}
};
template <Decoder::ValidateFlag validate>
struct ImmF64Immediate {
double value;
uint32_t length = 8;
inline ImmF64Immediate(Decoder* decoder, const byte* pc) {
// Avoid bit_cast because it might not preserve the signalling bit of a NaN.
uint64_t tmp = decoder->read_u64<validate>(pc + 1, "immf64");
memcpy(&value, &tmp, sizeof(value));
}
};
template <Decoder::ValidateFlag validate>
struct RefNullImmediate {
ValueType type;
uint32_t length = 1;
inline RefNullImmediate(const WasmFeatures& enabled, Decoder* decoder,
const byte* pc) {
type = value_type_reader::read_value_type<validate>(decoder, pc + 1,
&length, enabled);
}
};
template <Decoder::ValidateFlag validate>
struct GlobalIndexImmediate {
uint32_t index;
ValueType type = kWasmStmt;
const WasmGlobal* global = nullptr;
uint32_t length;
inline GlobalIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc + 1, &length, "global index");
}
};
template <Decoder::ValidateFlag validate>
struct SelectTypeImmediate {
uint32_t length;
......@@ -1109,6 +1122,14 @@ class WasmDecoder : public Decoder {
return true;
}
inline bool Validate(const byte* pc, RefNullImmediate<validate>& imm) {
if (!VALIDATE(imm.type.IsNullable())) {
errorf(pc + 1, "ref.null does not exist for %s", imm.type.type_name());
return false;
}
return true;
}
inline bool Complete(const byte* pc, ExceptionIndexImmediate<validate>& imm) {
if (!VALIDATE(imm.index < module_->exceptions.size())) return false;
imm.exception = &module_->exceptions[imm.index];
......@@ -1483,7 +1504,12 @@ class WasmDecoder : public Decoder {
return 1 + imm.length;
}
case kExprRefNull: {
return 1;
RefNullImmediate<validate> imm(WasmFeatures::All(), decoder, pc);
return 1 + imm.length;
}
case kExprRefIsNull: {
RefNullImmediate<validate> imm(WasmFeatures::All(), decoder, pc);
return 1 + imm.length;
}
case kExprRefFunc: {
FunctionIndexImmediate<validate> imm(decoder, pc);
......@@ -1682,6 +1708,7 @@ class WasmDecoder : public Decoder {
case kExprMemoryGrow:
case kExprRefAsNonNull:
case kExprBrOnNull:
case kExprRefIsNull:
return {1, 1};
case kExprLocalSet:
case kExprGlobalSet:
......@@ -2076,6 +2103,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
for (size_t i = 0; i < value_count; ++i) Push(sig->GetParam(i));
Vector<Value> values(stack_.data() + c->stack_depth, value_count);
TypeCheckBranchResult check_result = TypeCheckBranch(c, true);
if (this->failed()) break;
if (V8_LIKELY(check_result == kReachableBranch)) {
CALL_INTERFACE(BrOnException, exception, imm.index, imm.depth.depth,
values);
......@@ -2415,9 +2443,21 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
case kExprRefNull: {
CHECK_PROTOTYPE_OPCODE(anyref);
Value* value = Push(kWasmNullRef);
RefNullImmediate<validate> imm(this->enabled_, this, this->pc_);
if (!this->Validate(this->pc_, imm)) break;
Value* value = Push(imm.type);
CALL_INTERFACE_IF_REACHABLE(RefNull, value);
len = 1;
len = 1 + imm.length;
break;
}
case kExprRefIsNull: {
CHECK_PROTOTYPE_OPCODE(anyref);
RefNullImmediate<validate> imm(this->enabled_, this, this->pc_);
if (!this->Validate(this->pc_, imm)) break;
Value value = Pop(0, imm.type);
Value* result = Push(kWasmI32);
CALL_INTERFACE_IF_REACHABLE(UnOp, opcode, value, result);
len = 1 + imm.length;
break;
}
case kExprRefFunc: {
......@@ -3697,9 +3737,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
void BuildSimplePrototypeOperator(WasmOpcode opcode) {
if (opcode == kExprRefIsNull) {
RET_ON_PROTOTYPE_OPCODE(anyref);
} else if (opcode == kExprRefEq) {
if (opcode == kExprRefEq) {
RET_ON_PROTOTYPE_OPCODE(gc);
}
const FunctionSig* sig = WasmOpcodes::Signature(opcode);
......
......@@ -122,12 +122,15 @@ ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
return kWasmF32;
case WasmInitExpr::kF64Const:
return kWasmF64;
case WasmInitExpr::kRefNullConst:
return kWasmNullRef;
case WasmInitExpr::kRefFuncConst:
return kWasmFuncRef;
case WasmInitExpr::kRefNullConst:
// It is not possible to retrieve the full {ValueType} of a {WasmInitExpr}
// of kind {kRefNullConst}. As WasmInitExpr of kind {krefNullConst} is
// only valid in globals, the {ValueType} has to be retrieved from the
// global definition itself.
UNREACHABLE();
}
UNREACHABLE();
}
// Reads a length-prefixed string, checking that it is within bounds. Returns
......@@ -1423,7 +1426,7 @@ class ModuleDecoderImpl : public Decoder {
global->type = consume_value_type();
global->mutability = consume_mutability();
const byte* pos = pc();
global->init = consume_init_expr(module, kWasmStmt);
global->init = consume_init_expr(module, global->type);
if (global->init.kind == WasmInitExpr::kGlobalIndex) {
uint32_t other_index = global->init.val.global_index;
if (other_index >= index) {
......@@ -1438,12 +1441,6 @@ class ModuleDecoderImpl : public Decoder {
other_index, global->type.type_name(),
module->globals[other_index].type.type_name());
}
} else {
if (!TypeOf(module, global->init).IsSubTypeOf(global->type)) {
errorf(pos, "type error in global initialization, expected %s, got %s",
global->type.type_name(),
TypeOf(module, global->init).type_name());
}
}
}
......@@ -1698,8 +1695,19 @@ class ModuleDecoderImpl : public Decoder {
}
case kExprRefNull: {
if (enabled_features_.has_anyref() || enabled_features_.has_eh()) {
RefNullImmediate<Decoder::kValidate> imm(WasmFeatures::All(), this,
pc() - 1);
if (!imm.type.IsReferenceType()) {
errorf(pc() - 1, "ref.null is not supported for %s",
imm.type.type_name());
break;
}
expr.kind = WasmInitExpr::kRefNullConst;
len = 0;
len = imm.length;
if (expected != kWasmStmt && !imm.type.IsSubTypeOf(expected)) {
errorf(pos, "type error in init expression, expected %s, got %s",
expected.type_name(), imm.type.type_name());
}
break;
}
V8_FALLTHROUGH;
......@@ -1730,7 +1738,10 @@ class ModuleDecoderImpl : public Decoder {
if (!expect_u8("end opcode", kExprEnd)) {
expr.kind = WasmInitExpr::kNone;
}
if (expected != kWasmStmt && TypeOf(module, expr) != kWasmI32) {
// The type check of ref.null is special, and already done above.
if (expected != kWasmStmt && opcode != kExprRefNull &&
TypeOf(module, expr) != expected) {
errorf(pos, "type error in init expression, expected %s, got %s",
expected.type_name(), TypeOf(module, expr).type_name());
}
......@@ -2049,9 +2060,13 @@ class ModuleDecoderImpl : public Decoder {
uint8_t opcode = consume_u8("element opcode");
if (failed()) return index;
switch (opcode) {
case kExprRefNull:
case kExprRefNull: {
RefNullImmediate<kValidate> imm(WasmFeatures::All(), this,
this->pc() - 1);
consume_bytes(imm.length, "ref.null immediate");
index = WasmElemSegment::kNullIndex;
break;
}
case kExprRefFunc:
index = consume_element_func_index();
if (failed()) return index;
......
......@@ -134,6 +134,11 @@ class ValueType {
return kAnyRef <= kind() && kind() <= kEqRef;
}
constexpr bool IsNullable() const {
return kind() == kAnyRef || kind() == kFuncRef || kind() == kExnRef ||
kind() == kOptRef;
}
// TODO(7748): Extend this with struct and function subtyping.
// Keep up to date with funcref vs. anyref subtyping.
static ValueType CommonSubType(ValueType a, ValueType b) {
......
......@@ -3183,6 +3183,9 @@ class ThreadImpl {
break;
}
case kExprRefNull: {
RefNullImmediate<Decoder::kNoValidate> imm(WasmFeatures::All(),
&decoder, code->at(pc));
len = 1 + imm.length;
Push(WasmValue(isolate_->factory()->null_value()));
break;
}
......@@ -3519,6 +3522,9 @@ class ThreadImpl {
SIGN_EXTENSION_CASE(I64SExtendI32, int64_t, int32_t);
#undef SIGN_EXTENSION_CASE
case kExprRefIsNull: {
RefNullImmediate<Decoder::kNoValidate> imm(WasmFeatures::All(),
&decoder, code->at(pc));
len = 1 + imm.length;
HandleScope handle_scope(isolate_); // Avoid leaking handles.
uint32_t result = Pop().to_anyref()->IsNull() ? 1 : 0;
Push(WasmValue(result));
......
......@@ -566,6 +566,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
break;
case WasmInitExpr::kRefNullConst:
buffer->write_u8(kExprRefNull);
WriteValueType(buffer, global.type);
break;
case WasmInitExpr::kRefFuncConst:
UNIMPLEMENTED();
......
......@@ -62,6 +62,7 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&);
V(F32Const, 0x43, _) \
V(F64Const, 0x44, _) \
V(RefNull, 0xd0, _) \
V(RefIsNull, 0xd1, _) \
V(RefFunc, 0xd2, _) \
V(RefAsNonNull, 0xd3, _)
......@@ -231,7 +232,6 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&);
V(I64SExtendI32, 0xc4, l_l)
#define FOREACH_SIMPLE_PROTOTYPE_OPCODE(V) \
V(RefIsNull, 0xd1, i_r) \
V(RefEq, 0xd5, i_rr) // made-up opcode, guessing future spec (GC)
// For compatibility with Asm.js.
......@@ -765,13 +765,16 @@ struct WasmInitExpr {
explicit WasmInitExpr(int64_t v) : kind(kI64Const) { val.i64_const = v; }
explicit WasmInitExpr(float v) : kind(kF32Const) { val.f32_const = v; }
explicit WasmInitExpr(double v) : kind(kF64Const) { val.f64_const = v; }
explicit WasmInitExpr(WasmInitKind kind) : kind(kind) {
DCHECK_EQ(kind, kRefNullConst);
}
WasmInitExpr(WasmInitKind kind, uint32_t index) : kind(kind) {
if (kind == kGlobalIndex) {
val.global_index = index;
} else if (kind == kRefFuncConst) {
val.function_index = index;
} else if (kind == kRefNullConst) {
// Nothing to do.
} else {
// For the other types, the other initializers should be used.
UNREACHABLE();
......
......@@ -85,17 +85,19 @@ WASM_EXEC_TEST(BasicStruct) {
// Test struct.set, ref.as_non_null,
// struct refs types in globals and if-results.
uint32_t k_global_index = builder->AddGlobal(kOptRefType, true);
uint32_t k_global_index = builder->AddGlobal(
kOptRefType, true, WasmInitExpr(WasmInitExpr::kRefNullConst));
WasmFunctionBuilder* k = builder->AddFunction(sigs.i_v());
uint32_t k_field_index = 0;
k->builder()->AddExport(CStrVector("k"), k);
byte k_code[] = {
WASM_SET_GLOBAL(k_global_index, WASM_STRUCT_NEW(type_index, WASM_I32V(55),
WASM_I32V(66))),
WASM_STRUCT_GET(type_index, k_field_index,
WASM_REF_AS_NON_NULL(WASM_IF_ELSE_R(
kOptRefType, WASM_I32V(1),
WASM_GET_GLOBAL(k_global_index), WASM_REF_NULL))),
WASM_STRUCT_GET(
type_index, k_field_index,
WASM_REF_AS_NON_NULL(WASM_IF_ELSE_R(kOptRefType, WASM_I32V(1),
WASM_GET_GLOBAL(k_global_index),
WASM_REF_NULL_GC(type_index)))),
kExprEnd};
k->EmitCode(k_code, sizeof(k_code));
......@@ -147,13 +149,14 @@ WASM_EXEC_TEST(BasicStruct) {
WASM_STRUCT_NEW(type_index, WASM_I32V(55),
WASM_I32V(66))),
WASM_I32V(1)),
WASM_I32_ADD(
WASM_I32_SHL( // false
WASM_REF_EQ(WASM_GET_LOCAL(n_local_index), WASM_REF_NULL),
WASM_I32V(2)),
WASM_I32_SHL(WASM_REF_EQ( // true
WASM_REF_NULL, WASM_REF_NULL),
WASM_I32V(3))))),
WASM_I32_ADD(WASM_I32_SHL( // false
WASM_REF_EQ(WASM_GET_LOCAL(n_local_index),
WASM_REF_NULL_GC(type_index)),
WASM_I32V(2)),
WASM_I32_SHL(WASM_REF_EQ( // true
WASM_REF_NULL_GC(type_index),
WASM_REF_NULL_GC(type_index)),
WASM_I32V(3))))),
kExprEnd};
n->EmitCode(n_code, sizeof(n_code));
// Result: 0b1001
......
......@@ -348,19 +348,20 @@ TEST(MemoryGrowInvalidSize) {
TEST(ReferenceTypeLocals) {
{
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
BUILD(r, WASM_REF_IS_NULL(WASM_REF_NULL));
BUILD(r, WASM_REF_IS_NULL(kLocalAnyRef, WASM_REF_NULL(kLocalAnyRef)));
CHECK_EQ(1, r.Call());
}
{
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
r.AllocateLocal(kWasmAnyRef);
BUILD(r, WASM_REF_IS_NULL(WASM_GET_LOCAL(0)));
BUILD(r, WASM_REF_IS_NULL(kLocalAnyRef, WASM_GET_LOCAL(0)));
CHECK_EQ(1, r.Call());
}
{
WasmRunner<int32_t> r(ExecutionTier::kInterpreter);
r.AllocateLocal(kWasmAnyRef);
BUILD(r, WASM_REF_IS_NULL(WASM_TEE_LOCAL(0, WASM_REF_NULL)));
BUILD(r, WASM_REF_IS_NULL(kLocalAnyRef,
WASM_TEE_LOCAL(0, WASM_REF_NULL(kLocalAnyRef))));
CHECK_EQ(1, r.Call());
}
}
......
......@@ -432,9 +432,11 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_STRUCT_SET(typeidx, fieldidx, struct_obj, value) \
struct_obj, value, WASM_GC_OP(kExprStructSet), static_cast<byte>(typeidx), \
static_cast<byte>(fieldidx)
#define WASM_REF_NULL kExprRefNull
#define WASM_REF_NULL(type) kExprRefNull, static_cast<byte>(type)
#define WASM_REF_NULL_GC(type) \
kExprRefNull, kLocalOptRef, static_cast<byte>(type)
#define WASM_REF_FUNC(val) kExprRefFunc, val
#define WASM_REF_IS_NULL(val) val, kExprRefIsNull
#define WASM_REF_IS_NULL(type, val) val, kExprRefIsNull, static_cast<byte>(type)
#define WASM_REF_AS_NON_NULL(val) val, kExprRefAsNonNull
#define WASM_REF_EQ(lhs, rhs) lhs, rhs, kExprRefEq
......
......@@ -13,11 +13,11 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
function create_builder(i) {
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_i_r)
.addBody([
kExprLocalGet, 0, kExprRefIsNull,
...wasmI32Const(i),
kExprI32Add])
.exportFunc();
.addBody([
kExprLocalGet, 0, kExprRefIsNull, kWasmAnyRef, ...wasmI32Const(i),
kExprI32Add
])
.exportFunc();
return builder;
}
......
......@@ -36,7 +36,7 @@ let kSig_r_i = makeSig([kWasmI32], [kWasmAnyRef]);
kExprLocalGet, 0,
kExprI32Eqz,
kExprIf, kWasmAnyRef,
kExprRefNull,
kExprRefNull, kWasmAnyRef,
kExprElse,
kExprLocalGet, 1,
kExprEnd,
......
......@@ -9,7 +9,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function testAnyRefNull() {
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_r_v)
.addBody([kExprRefNull])
.addBody([kExprRefNull, kWasmAnyRef])
.exportFunc();
var wire_bytes = builder.toBuffer();
......@@ -24,7 +24,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
(function testAnyRefIsNull() {
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_i_r)
.addBody([kExprLocalGet, 0, kExprRefIsNull])
.addBody([kExprLocalGet, 0, kExprRefIsNull, kWasmAnyRef])
.exportFunc();
var wire_bytes = builder.toBuffer();
......
......@@ -152,7 +152,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_a);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kExprLocalSet, 0, kExprLocalGet, 0])
.addBody([kExprRefNull, kWasmAnyFunc, kExprLocalSet, 0, kExprLocalGet, 0])
.exportFunc();
const main = builder.instantiate().exports.main;
......@@ -163,7 +163,9 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_v);
builder.addFunction('main', sig_index).addBody([kExprRefNull]).exportFunc();
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kWasmAnyFunc])
.exportFunc();
const main = builder.instantiate().exports.main;
assertEquals(null, main());
......@@ -174,7 +176,7 @@ load('test/mjsunit/wasm/wasm-module-builder.js');
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_a_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kExprReturn])
.addBody([kExprRefNull, kWasmAnyFunc, kExprReturn])
.exportFunc();
const main = builder.instantiate().exports.main;
......
......@@ -560,21 +560,14 @@ function dummy_func() {
(function TestRefFuncGlobalInit() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
const g_ref = builder.addGlobal(kWasmAnyRef, true);
const g_func = builder.addGlobal(kWasmAnyFunc, true);
const f_ref = builder.addFunction('get_anyref_global', kSig_r_v)
.addBody([kExprGlobalGet, g_ref.index])
.exportAs('get_anyref_global');
const f_func = builder.addFunction('get_anyfunc_global', kSig_a_v)
.addBody([kExprGlobalGet, g_func.index])
.exportAs('get_anyfunc_global');
builder.addDeclarativeElementSegment([f_ref.index, f_func.index]);
g_ref.function_index = f_ref.index;
builder.addDeclarativeElementSegment([f_func.index]);
g_func.function_index = f_func.index;
const instance = builder.instantiate();
assertEquals(
instance.exports.get_anyref_global, instance.exports.get_anyref_global());
assertEquals(
instance.exports.get_anyfunc_global,
instance.exports.get_anyfunc_global());
......
......@@ -165,7 +165,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_r_v)
.addBody([kExprRefNull])
.addBody([kExprRefNull, kWasmAnyRef])
.exportFunc();
const instance = builder.instantiate();
......@@ -177,7 +177,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_i_r)
.addBody([kExprLocalGet, 0, kExprRefIsNull])
.addBody([kExprLocalGet, 0, kExprRefIsNull, kWasmAnyRef])
.exportFunc();
const instance = builder.instantiate();
......@@ -196,7 +196,10 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
builder.addFunction('main', kSig_i_v)
.addBody([kExprRefNull, kExprRefIsNull])
.addBody([
kExprRefNull, kWasmAnyRef, // --
kExprRefIsNull, kWasmAnyRef // --
])
.exportFunc();
const instance = builder.instantiate();
......@@ -222,7 +225,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_r_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull])
.addBody([kExprRefNull, kWasmAnyRef])
.exportFunc();
const main = builder.instantiate().exports.main;
......@@ -234,7 +237,7 @@ load("test/mjsunit/wasm/wasm-module-builder.js");
const builder = new WasmModuleBuilder();
const sig_index = builder.addType(kSig_r_v);
builder.addFunction('main', sig_index)
.addBody([kExprRefNull, kExprReturn])
.addBody([kExprRefNull, kWasmAnyRef, kExprReturn])
.exportFunc();
const main = builder.instantiate().exports.main;
......
......@@ -14,7 +14,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
let except = builder.addException(kSig_v_r);
builder.addFunction("throw_null", kSig_v_v)
.addBody([
kExprRefNull,
kExprRefNull, kWasmAnyRef,
kExprThrow, except,
]).exportFunc();
let instance = builder.instantiate();
......@@ -33,7 +33,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprLocalGet, 0,
kExprI32Eqz,
kExprIf, kWasmAnyRef,
kExprRefNull,
kExprRefNull, kWasmAnyRef,
kExprThrow, except,
kExprElse,
kExprI32Const, 42,
......@@ -43,7 +43,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
kExprBrOnExn, 0, except,
kExprRethrow,
kExprEnd,
kExprRefIsNull,
kExprRefIsNull, kWasmAnyRef,
kExprIf, kWasmI32,
kExprI32Const, 23,
kExprElse,
......@@ -191,7 +191,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
let except = builder.addException(kSig_v_r);
builder.addFunction('br_on_exn_nullref', kSig_v_v)
.addBody([
kExprRefNull,
kExprRefNull, kWasmExnRef,
kExprBrOnExn, 0, except,
kExprDrop
]).exportFunc();
......@@ -240,7 +240,7 @@ load("test/mjsunit/wasm/exceptions-utils.js");
let except = builder.addException(kSig_v_r);
builder.addFunction('rethrow_nullref', kSig_v_v)
.addBody([
kExprRefNull,
kExprRefNull, kWasmExnRef,
kExprRethrow
]).exportFunc();
let instance = builder.instantiate();
......
This diff is collapsed.
......@@ -1126,18 +1126,23 @@ class WasmModuleBuilder {
data_view.setFloat64(0, global.init, true);
section.emit_bytes(byte_view);
break;
case kWasmAnyFunc:
case kWasmAnyRef:
case kWasmNullRef:
section.emit_u8(kExprRefNull);
section.emit_u8(kWasmAnyRef);
assertEquals(global.function_index, undefined);
break;
case kWasmAnyFunc:
if (global.function_index !== undefined) {
section.emit_u8(kExprRefFunc);
section.emit_u32v(global.function_index);
} else {
section.emit_u8(kExprRefNull);
section.emit_u8(kWasmAnyFunc);
}
break;
case kWasmExnRef:
section.emit_u8(kExprRefNull);
section.emit_u8(kWasmExnRef);
break;
}
} else {
......@@ -1227,6 +1232,7 @@ class WasmModuleBuilder {
for (let index of init.array) {
if (index === null) {
section.emit_u8(kExprRefNull);
section.emit_u8(kWasmAnyFunc);
section.emit_u8(kExprEnd);
} else {
section.emit_u8(kExprRefFunc);
......
......@@ -306,8 +306,7 @@ TEST_F(FunctionBodyDecoderTest, Int32Const1) {
TEST_F(FunctionBodyDecoderTest, RefNull) {
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.r_v(), {kExprRefNull});
ExpectValidates(sigs.n_v(), {kExprRefNull});
ExpectValidates(sigs.r_v(), {kExprRefNull, kLocalAnyRef});
}
TEST_F(FunctionBodyDecoderTest, RefFunc) {
......@@ -2667,9 +2666,11 @@ TEST_F(FunctionBodyDecoderTest, SelectWithType) {
ExpectValidates(sigs.l_l(),
{WASM_SELECT_L(WASM_I64V_1(0), WASM_I64V_1(0), WASM_ZERO)});
ExpectValidates(sigs.r_r(),
{WASM_SELECT_R(WASM_REF_NULL, WASM_REF_NULL, WASM_ZERO)});
{WASM_SELECT_R(WASM_REF_NULL(kLocalAnyRef),
WASM_REF_NULL(kLocalAnyRef), WASM_ZERO)});
ExpectValidates(sigs.a_a(),
{WASM_SELECT_A(WASM_REF_NULL, WASM_REF_NULL, WASM_ZERO)});
{WASM_SELECT_A(WASM_REF_NULL(kLocalFuncRef),
WASM_REF_NULL(kLocalFuncRef), WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, SelectWithType_fail) {
......@@ -3209,17 +3210,16 @@ TEST_F(FunctionBodyDecoderTest, TableGrow) {
byte tab_ref = builder.AddTable(kWasmAnyRef, 10, true, 20);
byte tab_null = builder.AddTable(kWasmNullRef, 10, true, 20);
ExpectFailure(sigs.i_a(),
{WASM_TABLE_GROW(tab_func, WASM_REF_NULL, WASM_ONE)});
ExpectFailure(sigs.i_n(),
{WASM_TABLE_GROW(tab_null, WASM_REF_NULL, WASM_ONE)});
ExpectFailure(
sigs.i_a(),
{WASM_TABLE_GROW(tab_func, WASM_REF_NULL(kLocalFuncRef), WASM_ONE)});
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.i_a(),
{WASM_TABLE_GROW(tab_func, WASM_REF_NULL, WASM_ONE)});
ExpectValidates(sigs.i_r(),
{WASM_TABLE_GROW(tab_ref, WASM_REF_NULL, WASM_ONE)});
ExpectValidates(sigs.i_n(),
{WASM_TABLE_GROW(tab_null, WASM_REF_NULL, WASM_ONE)});
ExpectValidates(
sigs.i_a(),
{WASM_TABLE_GROW(tab_func, WASM_REF_NULL(kLocalFuncRef), WASM_ONE)});
ExpectValidates(
sigs.i_r(),
{WASM_TABLE_GROW(tab_ref, WASM_REF_NULL(kLocalAnyRef), WASM_ONE)});
// FuncRef table cannot be initialized with an anyref value.
ExpectFailure(sigs.i_r(),
{WASM_TABLE_GROW(tab_func, WASM_GET_LOCAL(0), WASM_ONE)});
......@@ -3237,8 +3237,9 @@ TEST_F(FunctionBodyDecoderTest, TableGrow) {
ExpectFailure(sigs.i_r(),
{WASM_TABLE_GROW(tab_null, WASM_GET_LOCAL(0), WASM_ONE)});
// Check that the table index gets verified.
ExpectFailure(sigs.i_r(),
{WASM_TABLE_GROW(tab_ref + 2, WASM_REF_NULL, WASM_ONE)});
ExpectFailure(
sigs.i_r(),
{WASM_TABLE_GROW(tab_ref + 2, WASM_REF_NULL(kLocalAnyRef), WASM_ONE)});
}
TEST_F(FunctionBodyDecoderTest, TableSize) {
......@@ -3253,19 +3254,17 @@ TEST_F(FunctionBodyDecoderTest, TableSize) {
TEST_F(FunctionBodyDecoderTest, TableFill) {
byte tab_func = builder.AddTable(kWasmFuncRef, 10, true, 20);
byte tab_ref = builder.AddTable(kWasmAnyRef, 10, true, 20);
byte tab_null = builder.AddTable(kWasmNullRef, 10, true, 20);
ExpectFailure(sigs.v_a(),
{WASM_TABLE_FILL(tab_func, WASM_ONE, WASM_REF_NULL, WASM_ONE)});
ExpectFailure(sigs.v_n(),
{WASM_TABLE_FILL(tab_null, WASM_ONE, WASM_REF_NULL, WASM_ONE)});
{WASM_TABLE_FILL(tab_func, WASM_ONE,
WASM_REF_NULL(kLocalFuncRef), WASM_ONE)});
WASM_FEATURE_SCOPE(anyref);
ExpectValidates(sigs.v_a(), {WASM_TABLE_FILL(tab_func, WASM_ONE,
WASM_REF_NULL, WASM_ONE)});
ExpectValidates(sigs.v_r(), {WASM_TABLE_FILL(tab_ref, WASM_ONE, WASM_REF_NULL,
WASM_ONE)});
ExpectValidates(sigs.v_n(), {WASM_TABLE_FILL(tab_null, WASM_ONE,
WASM_REF_NULL, WASM_ONE)});
ExpectValidates(sigs.v_a(),
{WASM_TABLE_FILL(tab_func, WASM_ONE,
WASM_REF_NULL(kLocalFuncRef), WASM_ONE)});
ExpectValidates(sigs.v_r(),
{WASM_TABLE_FILL(tab_ref, WASM_ONE,
WASM_REF_NULL(kLocalAnyRef), WASM_ONE)});
// FuncRef table cannot be initialized with an anyref value.
ExpectFailure(sigs.v_r(), {WASM_TABLE_FILL(tab_func, WASM_ONE,
WASM_GET_LOCAL(0), WASM_ONE)});
......@@ -3278,17 +3277,20 @@ TEST_F(FunctionBodyDecoderTest, TableFill) {
ExpectValidates(sigs.v_n(), {WASM_TABLE_FILL(tab_ref, WASM_ONE,
WASM_GET_LOCAL(0), WASM_ONE)});
// Check that the table index gets verified.
ExpectFailure(sigs.v_r(), {WASM_TABLE_FILL(tab_ref + 2, WASM_ONE,
WASM_REF_NULL, WASM_ONE)});
ExpectFailure(sigs.v_r(),
{WASM_TABLE_FILL(tab_ref + 2, WASM_ONE,
WASM_REF_NULL(kLocalAnyRef), WASM_ONE)});
}
TEST_F(FunctionBodyDecoderTest, TableOpsWithoutTable) {
{
WASM_FEATURE_SCOPE(anyref);
ExpectFailure(sigs.i_v(), {WASM_TABLE_GROW(0, WASM_REF_NULL, WASM_ONE)});
ExpectFailure(sigs.i_v(),
{WASM_TABLE_GROW(0, WASM_REF_NULL(kLocalAnyRef), WASM_ONE)});
ExpectFailure(sigs.i_v(), {WASM_TABLE_SIZE(0)});
ExpectFailure(sigs.i_r(),
{WASM_TABLE_FILL(0, WASM_ONE, WASM_REF_NULL, WASM_ONE)});
ExpectFailure(
sigs.i_r(),
{WASM_TABLE_FILL(0, WASM_ONE, WASM_REF_NULL(kLocalAnyRef), WASM_ONE)});
}
{
WASM_FEATURE_SCOPE(bulk_memory);
......@@ -3519,7 +3521,7 @@ TEST_F(WasmOpcodeLengthTest, Statements) {
TEST_F(WasmOpcodeLengthTest, MiscExpressions) {
ExpectLength(5, kExprF32Const);
ExpectLength(9, kExprF64Const);
ExpectLength(1, kExprRefNull);
ExpectLength(2, kExprRefNull);
ExpectLength(2, kExprLocalGet);
ExpectLength(2, kExprLocalSet);
ExpectLength(2, kExprGlobalGet);
......
......@@ -29,11 +29,12 @@ namespace module_decoder_unittest {
#define WASM_INIT_EXPR_F32(val) WASM_F32(val), kExprEnd
#define WASM_INIT_EXPR_I64(val) WASM_I64(val), kExprEnd
#define WASM_INIT_EXPR_F64(val) WASM_F64(val), kExprEnd
#define WASM_INIT_EXPR_REF_NULL WASM_REF_NULL, kExprEnd
#define WASM_INIT_EXPR_ANY_REF_NULL WASM_REF_NULL(kLocalAnyRef), kExprEnd
#define WASM_INIT_EXPR_FUNC_REF_NULL WASM_REF_NULL(kLocalFuncRef), kExprEnd
#define WASM_INIT_EXPR_REF_FUNC(val) WASM_REF_FUNC(val), kExprEnd
#define WASM_INIT_EXPR_GLOBAL(index) WASM_GET_GLOBAL(index), kExprEnd
#define REF_NULL_ELEMENT kExprRefNull, kExprEnd
#define REF_NULL_ELEMENT kExprRefNull, kLocalFuncRef, kExprEnd
#define REF_FUNC_ELEMENT(v) kExprRefFunc, U32V_1(v), kExprEnd
#define EMPTY_BODY 0
......@@ -269,8 +270,8 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) {
ENTRY_COUNT(2), // --
kLocalAnyRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_NULL, // init
kLocalAnyRef, // local type
WASM_INIT_EXPR_ANY_REF_NULL, // init
kLocalFuncRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_FUNC(1)), // init
SECTION(Element, // section name
......@@ -299,7 +300,7 @@ TEST_F(WasmModuleVerifyTest, AnyRefGlobal) {
EXPECT_EQ(WasmInitExpr::kRefNullConst, global->init.kind);
global = &result.value()->globals[1];
EXPECT_EQ(kWasmAnyRef, global->type);
EXPECT_EQ(kWasmFuncRef, global->type);
EXPECT_FALSE(global->mutability);
EXPECT_EQ(WasmInitExpr::kRefFuncConst, global->init.kind);
EXPECT_EQ(uint32_t{1}, global->init.val.function_index);
......@@ -314,24 +315,24 @@ TEST_F(WasmModuleVerifyTest, FuncRefGlobal) {
SIGNATURES_SECTION_VOID_VOID,
// funcs ---------------------------------------------------------------
TWO_EMPTY_FUNCTIONS(SIG_INDEX(0)),
SECTION(Global, // --
ENTRY_COUNT(2), // --
kLocalFuncRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_NULL, // init
kLocalFuncRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_FUNC(1)), // init
SECTION(Element, // section name
ENTRY_COUNT(2), // entry count
DECLARATIVE, // flags 0
kExternalFunction, // type
ENTRY_COUNT(1), // func entry count
FUNC_INDEX(0), // func index
DECLARATIVE_WITH_ELEMENTS, // flags 1
kLocalFuncRef, // local type
ENTRY_COUNT(1), // func ref count
REF_FUNC_ELEMENT(1)), // func ref
SECTION(Global, // --
ENTRY_COUNT(2), // --
kLocalFuncRef, // local type
0, // immutable
WASM_INIT_EXPR_FUNC_REF_NULL, // init
kLocalFuncRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_FUNC(1)), // init
SECTION(Element, // section name
ENTRY_COUNT(2), // entry count
DECLARATIVE, // flags 0
kExternalFunction, // type
ENTRY_COUNT(1), // func entry count
FUNC_INDEX(0), // func index
DECLARATIVE_WITH_ELEMENTS, // flags 1
kLocalFuncRef, // local type
ENTRY_COUNT(1), // func ref count
REF_FUNC_ELEMENT(1)), // func ref
TWO_EMPTY_BODIES};
{
// Should decode to two globals.
......@@ -354,34 +355,6 @@ TEST_F(WasmModuleVerifyTest, FuncRefGlobal) {
}
}
TEST_F(WasmModuleVerifyTest, NullRefGlobal) {
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
// sig#0 ---------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs ---------------------------------------------------------------
TWO_EMPTY_FUNCTIONS(SIG_INDEX(0)),
SECTION(Global, // --
ENTRY_COUNT(1), // --
kLocalNullRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_NULL), // init
TWO_EMPTY_BODIES};
{
// Should decode to one global.
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(1u, result.value()->globals.size());
EXPECT_EQ(2u, result.value()->functions.size());
EXPECT_EQ(0u, result.value()->data_segments.size());
const WasmGlobal* global = &result.value()->globals[0];
EXPECT_EQ(kWasmNullRef, global->type);
EXPECT_FALSE(global->mutability);
EXPECT_EQ(WasmInitExpr::kRefNullConst, global->init.kind);
}
}
TEST_F(WasmModuleVerifyTest, InvalidFuncRefGlobal) {
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
......@@ -398,27 +371,6 @@ TEST_F(WasmModuleVerifyTest, InvalidFuncRefGlobal) {
EXPECT_FAILURE(data);
}
TEST_F(WasmModuleVerifyTest, InvalidNullRefGlobal) {
WASM_FEATURE_SCOPE(anyref);
// Initialize nullref from funcref
static const byte data[] = {
// sig#0 ---------------------------------------------------------------
SIGNATURES_SECTION_VOID_VOID,
// funcs ---------------------------------------------------------------
TWO_EMPTY_FUNCTIONS(SIG_INDEX(0)),
SECTION(Global, // --
ENTRY_COUNT(1), // --
kLocalNullRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_FUNC(1)), // init
TWO_EMPTY_BODIES};
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_NOT_OK(
result,
"type error in global initialization, expected nullref, got funcref");
}
TEST_F(WasmModuleVerifyTest, AnyRefGlobalWithGlobalInit) {
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {
......@@ -2337,7 +2289,7 @@ TEST_F(WasmInitExprDecodeTest, InitExpr_f64) {
TEST_F(WasmInitExprDecodeTest, InitExpr_AnyRef) {
WASM_FEATURE_SCOPE(anyref);
static const byte data[] = {kExprRefNull, kExprEnd};
static const byte data[] = {kExprRefNull, kLocalAnyRef, kExprEnd};
WasmInitExpr expr = DecodeInitExpr(data, data + sizeof(data));
EXPECT_EQ(WasmInitExpr::kRefNullConst, expr.kind);
}
......@@ -2623,7 +2575,7 @@ TEST_F(WasmModuleVerifyTest, DeclarativeElementSegmentMissingForGlobal) {
// global definitions ----------------------------------------------------
SECTION(Global, // section name
ENTRY_COUNT(1), // entry count
kLocalAnyRef, // local type
kLocalFuncRef, // local type
0, // immutable
WASM_INIT_EXPR_REF_FUNC(0)), // init
// code ------------------------------------------------------------------
......@@ -2734,7 +2686,8 @@ TEST_F(WasmModuleVerifyTest, DataCountSegmentCount_omitted) {
#undef WASM_INIT_EXPR_F32
#undef WASM_INIT_EXPR_I64
#undef WASM_INIT_EXPR_F64
#undef WASM_INIT_EXPR_REF_NULL
#undef WASM_INIT_EXPR_ANY_REF_NULL
#undef WASM_INIT_EXPR_FUNC_REF_NULL
#undef WASM_INIT_EXPR_REF_FUNC
#undef WASM_INIT_EXPR_GLOBAL
#undef REF_NULL_ELEMENT
......
......@@ -34,7 +34,7 @@ TEST_F(WasmCapiTest, HostRef) {
FunctionSig r_i_sig(1, 1, ri_reps);
uint32_t func_index = builder()->AddImport(CStrVector("f"), &r_r_sig);
const bool kMutable = true;
const WasmInitExpr global_init(WasmInitExpr::kRefNullConst, 0);
const WasmInitExpr global_init(WasmInitExpr::kRefNullConst);
uint32_t global_index = builder()->AddExportedGlobal(
kWasmAnyRef, kMutable, global_init, CStrVector("global"));
uint32_t table_index = builder()->AddTable(kWasmAnyRef, 10);
......
......@@ -21,23 +21,10 @@
'proposals/js-types/linking': [FAIL],
# TODO(v8:10556): Remove sub-typing in the reference-types implementation
'proposals/bulk-memory-operations/binary': [FAIL],
'proposals/bulk-memory-operations/bulk': [FAIL],
'proposals/bulk-memory-operations/elem': [FAIL],
'proposals/bulk-memory-operations/imports': [FAIL],
'proposals/reference-types/binary': [FAIL],
'proposals/reference-types/bulk': [FAIL],
'proposals/reference-types/elem': [FAIL],
'proposals/reference-types/global': [FAIL],
'proposals/reference-types/linking': [FAIL],
'proposals/reference-types/ref_func': [FAIL],
'proposals/reference-types/ref_is_null': [FAIL],
'proposals/reference-types/ref_null': [FAIL],
'proposals/reference-types/table_get': [FAIL],
'proposals/reference-types/table_grow': [FAIL],
'proposals/reference-types/table_set': [FAIL],
'proposals/reference-types/table_size': [FAIL],
'proposals/reference-types/unreached-invalid': [FAIL],
# TODO(wasm): This test declares a table larger than allowed by the spec.
......
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