Commit eb23cef0 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Implement ref.eq

Changes:
- Implement subtyping for eqref.
- (Driveby) Declare more functions as constexpr in ValueType.
- Make minor changes needed to handle ref.eq.
- Write an elementary test.

Bug: v8:7748
Change-Id: I11d54227798ce56de70f3a6f83305b2f80b2f57f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2193715
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67752}
parent b5939c75
...@@ -660,6 +660,8 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, Node* right, ...@@ -660,6 +660,8 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
break; break;
case wasm::kExprF64Mod: case wasm::kExprF64Mod:
return BuildF64Mod(left, right); return BuildF64Mod(left, right);
case wasm::kExprRefEq:
return gasm_->TaggedEqual(left, right);
case wasm::kExprI32AsmjsDivS: case wasm::kExprI32AsmjsDivS:
return BuildI32AsmjsDivS(left, right); return BuildI32AsmjsDivS(left, right);
case wasm::kExprI32AsmjsDivU: case wasm::kExprI32AsmjsDivU:
......
...@@ -41,22 +41,18 @@ struct WasmException; ...@@ -41,22 +41,18 @@ struct WasmException;
return true; \ return true; \
}()) }())
#define RET_ON_PROTOTYPE_OPCODE(feat) \ #define CHECK_PROTOTYPE_OPCODE_GEN(feat, opt_break) \
DCHECK(!this->module_ || this->module_->origin == kWasmOrigin); \ DCHECK(!this->module_ || this->module_->origin == kWasmOrigin); \
if (!this->enabled_.has_##feat()) { \ if (!this->enabled_.has_##feat()) { \
this->error("Invalid opcode (enable with --experimental-wasm-" #feat ")"); \ this->error("Invalid opcode (enable with --experimental-wasm-" #feat ")"); \
opt_break \
} else { \ } else { \
this->detected_->Add(kFeature_##feat); \ this->detected_->Add(kFeature_##feat); \
} }
#define CHECK_PROTOTYPE_OPCODE(feat) \ #define CHECK_PROTOTYPE_OPCODE(feat) CHECK_PROTOTYPE_OPCODE_GEN(feat, break;)
DCHECK(!this->module_ || this->module_->origin == kWasmOrigin); \
if (!this->enabled_.has_##feat()) { \ #define RET_ON_PROTOTYPE_OPCODE(feat) CHECK_PROTOTYPE_OPCODE_GEN(feat, )
this->error("Invalid opcode (enable with --experimental-wasm-" #feat ")"); \
break; \
} else { \
this->detected_->Add(kFeature_##feat); \
}
#define OPCODE_ERROR(opcode, message) \ #define OPCODE_ERROR(opcode, message) \
(this->errorf(this->pc_, "%s: %s", WasmOpcodes::OpcodeName(opcode), \ (this->errorf(this->pc_, "%s: %s", WasmOpcodes::OpcodeName(opcode), \
...@@ -3524,10 +3520,11 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -3524,10 +3520,11 @@ class WasmFullDecoder : public WasmDecoder<validate> {
} }
void BuildSimplePrototypeOperator(WasmOpcode opcode) { void BuildSimplePrototypeOperator(WasmOpcode opcode) {
if (WasmOpcodes::IsAnyRefOpcode(opcode)) { if (opcode == kExprRefIsNull) {
RET_ON_PROTOTYPE_OPCODE(anyref); RET_ON_PROTOTYPE_OPCODE(anyref);
} else if (opcode == kExprRefEq) {
RET_ON_PROTOTYPE_OPCODE(gc);
} }
// TODO(7748): Add RefEq support here.
const FunctionSig* sig = WasmOpcodes::Signature(opcode); const FunctionSig* sig = WasmOpcodes::Signature(opcode);
BuildSimpleOperator(opcode, sig); BuildSimpleOperator(opcode, sig);
} }
...@@ -3598,6 +3595,8 @@ class EmptyInterface { ...@@ -3598,6 +3595,8 @@ class EmptyInterface {
#undef TRACE_INST_FORMAT #undef TRACE_INST_FORMAT
#undef VALIDATE #undef VALIDATE
#undef CHECK_PROTOTYPE_OPCODE #undef CHECK_PROTOTYPE_OPCODE
#undef RET_ON_PROTOTYPE_OPCODE
#undef CHECK_PROTOTYPE_OPCODE_GEN
#undef OPCODE_ERROR #undef OPCODE_ERROR
} // namespace wasm } // namespace wasm
......
...@@ -26,7 +26,9 @@ class Simd128; ...@@ -26,7 +26,9 @@ class Simd128;
// (direct) subtype of the bottom type. // (direct) subtype of the bottom type.
// //
// AnyRef // AnyRef
// / | \ // / \
// / EqRef
// / / \
// FuncRef ExnRef OptRef(S) // FuncRef ExnRef OptRef(S)
// \ | / \ // \ | / \
// I32 I64 F32 F64 NullRef Ref(S) // I32 I64 F32 F64 NullRef Ref(S)
...@@ -38,7 +40,7 @@ class Simd128; ...@@ -38,7 +40,7 @@ class Simd128;
// - "ref" types per https://github.com/WebAssembly/function-references // - "ref" types per https://github.com/WebAssembly/function-references
// - "optref"/"eqref" per https://github.com/WebAssembly/gc // - "optref"/"eqref" per https://github.com/WebAssembly/gc
// //
// TODO(7748): Extend this with eqref, struct and function subtyping. // TODO(7748): Extend this with struct and function subtyping.
// Keep up to date with funcref vs. anyref subtyping. // Keep up to date with funcref vs. anyref subtyping.
#define FOREACH_VALUE_TYPE(V) \ #define FOREACH_VALUE_TYPE(V) \
V(Stmt, -1, Void, None, 'v', "<stmt>") \ V(Stmt, -1, Void, None, 'v', "<stmt>") \
...@@ -71,11 +73,15 @@ class ValueType { ...@@ -71,11 +73,15 @@ class ValueType {
constexpr ValueType() : bit_field_(KindField::encode(kStmt)) {} constexpr ValueType() : bit_field_(KindField::encode(kStmt)) {}
explicit constexpr ValueType(Kind kind) explicit constexpr ValueType(Kind kind)
: bit_field_(KindField::encode(kind)) { : bit_field_(KindField::encode(kind)) {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK(!has_immediate()); DCHECK(!has_immediate());
#endif
} }
constexpr ValueType(Kind kind, uint32_t ref_index) constexpr ValueType(Kind kind, uint32_t ref_index)
: bit_field_(KindField::encode(kind) | RefIndexField::encode(ref_index)) { : bit_field_(KindField::encode(kind) | RefIndexField::encode(ref_index)) {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK(has_immediate()); DCHECK(has_immediate());
#endif
} }
constexpr Kind kind() const { return KindField::decode(bit_field_); } constexpr Kind kind() const { return KindField::decode(bit_field_); }
...@@ -110,27 +116,25 @@ class ValueType { ...@@ -110,27 +116,25 @@ class ValueType {
return bit_field_ != other.bit_field_; return bit_field_ != other.bit_field_;
} }
// TODO(7748): Extend this with eqref, struct and function subtyping. // TODO(7748): Extend this with struct and function subtyping.
// Keep up to date with funcref vs. anyref subtyping. // Keep up to date with funcref vs. anyref subtyping.
bool IsSubTypeOf(ValueType other) const { constexpr bool IsSubTypeOf(ValueType other) const {
return (*this == other) || return (*this == other) || (other.kind() == kAnyRef && IsReferenceType()) ||
(kind() == kNullRef && (kind() == kNullRef && other.kind() != kRef &&
(other.kind() == kAnyRef || other.kind() == kFuncRef || other.IsReferenceType()) ||
other.kind() == kExnRef || other.kind() == kOptRef)) || (other.kind() == kEqRef &&
(other.kind() == kAnyRef && (kind() == kExnRef || kind() == kOptRef || kind() == kRef)) ||
(kind() == kFuncRef || kind() == kExnRef || kind() == kOptRef ||
kind() == kRef)) ||
(kind() == kRef && other.kind() == kOptRef && (kind() == kRef && other.kind() == kOptRef &&
ref_index() == other.ref_index()); ref_index() == other.ref_index());
} }
bool IsReferenceType() const { constexpr bool IsReferenceType() const {
return kind() == kAnyRef || kind() == kFuncRef || kind() == kNullRef || return kind() == kAnyRef || kind() == kFuncRef || kind() == kNullRef ||
kind() == kExnRef || kind() == kRef || kind() == kOptRef || kind() == kExnRef || kind() == kRef || kind() == kOptRef ||
kind() == kEqRef; kind() == kEqRef;
} }
// TODO(7748): Extend this with eqref, struct and function subtyping. // TODO(7748): Extend this with struct and function subtyping.
// Keep up to date with funcref vs. anyref subtyping. // Keep up to date with funcref vs. anyref subtyping.
static ValueType CommonSubType(ValueType a, ValueType b) { static ValueType CommonSubType(ValueType a, ValueType b) {
if (a == b) return a; if (a == b) return a;
...@@ -143,12 +147,14 @@ class ValueType { ...@@ -143,12 +147,14 @@ class ValueType {
// {a} and {b} are not each other's subtype. // {a} and {b} are not each other's subtype.
// If one of them is not nullable, their greatest subtype is bottom, // If one of them is not nullable, their greatest subtype is bottom,
// otherwise null. // otherwise null.
return (a.kind() == kRef || b.kind() == kRef) ? ValueType(kBottom) if (a.kind() == kRef || b.kind() == kRef) return ValueType(kBottom);
: ValueType(kNullRef); return ValueType(kNullRef);
} }
ValueTypeCode value_type_code() const { constexpr ValueTypeCode value_type_code() const {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK_NE(kBottom, kind()); DCHECK_NE(kBottom, kind());
#endif
constexpr ValueTypeCode kValueTypeCode[] = { constexpr ValueTypeCode kValueTypeCode[] = {
#define TYPE_CODE(kind, log2Size, code, ...) kLocal##code, #define TYPE_CODE(kind, log2Size, code, ...) kLocal##code,
...@@ -159,8 +165,10 @@ class ValueType { ...@@ -159,8 +165,10 @@ class ValueType {
return kValueTypeCode[kind()]; return kValueTypeCode[kind()];
} }
MachineType machine_type() const { constexpr MachineType machine_type() const {
#if V8_HAS_CXX14_CONSTEXPR
DCHECK_NE(kBottom, kind()); DCHECK_NE(kBottom, kind());
#endif
constexpr MachineType kMachineType[] = { constexpr MachineType kMachineType[] = {
#define MACH_TYPE(kind, log2Size, code, machineType, ...) \ #define MACH_TYPE(kind, log2Size, code, machineType, ...) \
...@@ -172,7 +180,7 @@ class ValueType { ...@@ -172,7 +180,7 @@ class ValueType {
return kMachineType[kind()]; return kMachineType[kind()];
} }
MachineRepresentation machine_representation() { constexpr MachineRepresentation machine_representation() const {
return machine_type().representation(); return machine_type().representation();
} }
......
...@@ -129,6 +129,35 @@ WASM_EXEC_TEST(BasicStruct) { ...@@ -129,6 +129,35 @@ WASM_EXEC_TEST(BasicStruct) {
kExprEnd}; kExprEnd};
m->EmitCode(m_code, sizeof(m_code)); m->EmitCode(m_code, sizeof(m_code));
// Test ref.eq
WasmFunctionBuilder* n = builder->AddFunction(sigs.i_v());
uint32_t n_local_index = n->AddLocal(kOptRefType);
n->builder()->AddExport(CStrVector("n"), n);
byte n_code[] = {
WASM_SET_LOCAL(n_local_index,
WASM_STRUCT_NEW(type_index, WASM_I32V(55), WASM_I32V(66))),
WASM_I32_ADD(
WASM_I32_SHL(
WASM_REF_EQ( // true
WASM_GET_LOCAL(n_local_index), WASM_GET_LOCAL(n_local_index)),
WASM_I32V(0)),
WASM_I32_ADD(
WASM_I32_SHL(WASM_REF_EQ( // false
WASM_GET_LOCAL(n_local_index),
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))))),
kExprEnd};
n->EmitCode(n_code, sizeof(n_code));
// Result: 0b1001
ZoneBuffer buffer(&zone); ZoneBuffer buffer(&zone);
builder->WriteTo(&buffer); builder->WriteTo(&buffer);
...@@ -168,6 +197,9 @@ WASM_EXEC_TEST(BasicStruct) { ...@@ -168,6 +197,9 @@ WASM_EXEC_TEST(BasicStruct) {
CHECK_EQ(52, testing::CallWasmFunctionForTesting(isolate, instance, &thrower, CHECK_EQ(52, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
"m", 0, nullptr)); "m", 0, nullptr));
CHECK_EQ(0b1001, testing::CallWasmFunctionForTesting(
isolate, instance, &thrower, "n", 0, nullptr));
} }
WASM_EXEC_TEST(BasicArray) { WASM_EXEC_TEST(BasicArray) {
......
...@@ -436,6 +436,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) { ...@@ -436,6 +436,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_REF_FUNC(val) kExprRefFunc, val #define WASM_REF_FUNC(val) kExprRefFunc, val
#define WASM_REF_IS_NULL(val) val, kExprRefIsNull #define WASM_REF_IS_NULL(val) val, kExprRefIsNull
#define WASM_REF_AS_NON_NULL(val) val, kExprRefAsNonNull #define WASM_REF_AS_NON_NULL(val) val, kExprRefAsNonNull
#define WASM_REF_EQ(lhs, rhs) lhs, rhs, kExprRefEq
#define WASM_ARRAY_NEW(index, default_value, length) \ #define WASM_ARRAY_NEW(index, default_value, length) \
default_value, length, WASM_GC_OP(kExprArrayNew), static_cast<byte>(index) default_value, length, WASM_GC_OP(kExprArrayNew), static_cast<byte>(index)
......
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