[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: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Cr-Commit-Position: refs/heads/master@{#67752}
This commit is contained in:
parent
b5939c7589
commit
eb23cef034
@ -660,6 +660,8 @@ Node* WasmGraphBuilder::Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
|
||||
break;
|
||||
case wasm::kExprF64Mod:
|
||||
return BuildF64Mod(left, right);
|
||||
case wasm::kExprRefEq:
|
||||
return gasm_->TaggedEqual(left, right);
|
||||
case wasm::kExprI32AsmjsDivS:
|
||||
return BuildI32AsmjsDivS(left, right);
|
||||
case wasm::kExprI32AsmjsDivU:
|
||||
|
@ -41,22 +41,18 @@ struct WasmException;
|
||||
return true; \
|
||||
}())
|
||||
|
||||
#define RET_ON_PROTOTYPE_OPCODE(feat) \
|
||||
#define CHECK_PROTOTYPE_OPCODE_GEN(feat, opt_break) \
|
||||
DCHECK(!this->module_ || this->module_->origin == kWasmOrigin); \
|
||||
if (!this->enabled_.has_##feat()) { \
|
||||
this->error("Invalid opcode (enable with --experimental-wasm-" #feat ")"); \
|
||||
opt_break \
|
||||
} else { \
|
||||
this->detected_->Add(kFeature_##feat); \
|
||||
}
|
||||
|
||||
#define CHECK_PROTOTYPE_OPCODE(feat) \
|
||||
DCHECK(!this->module_ || this->module_->origin == kWasmOrigin); \
|
||||
if (!this->enabled_.has_##feat()) { \
|
||||
this->error("Invalid opcode (enable with --experimental-wasm-" #feat ")"); \
|
||||
break; \
|
||||
} else { \
|
||||
this->detected_->Add(kFeature_##feat); \
|
||||
}
|
||||
#define CHECK_PROTOTYPE_OPCODE(feat) CHECK_PROTOTYPE_OPCODE_GEN(feat, break;)
|
||||
|
||||
#define RET_ON_PROTOTYPE_OPCODE(feat) CHECK_PROTOTYPE_OPCODE_GEN(feat, )
|
||||
|
||||
#define OPCODE_ERROR(opcode, message) \
|
||||
(this->errorf(this->pc_, "%s: %s", WasmOpcodes::OpcodeName(opcode), \
|
||||
@ -3524,10 +3520,11 @@ class WasmFullDecoder : public WasmDecoder<validate> {
|
||||
}
|
||||
|
||||
void BuildSimplePrototypeOperator(WasmOpcode opcode) {
|
||||
if (WasmOpcodes::IsAnyRefOpcode(opcode)) {
|
||||
if (opcode == kExprRefIsNull) {
|
||||
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);
|
||||
BuildSimpleOperator(opcode, sig);
|
||||
}
|
||||
@ -3598,6 +3595,8 @@ class EmptyInterface {
|
||||
#undef TRACE_INST_FORMAT
|
||||
#undef VALIDATE
|
||||
#undef CHECK_PROTOTYPE_OPCODE
|
||||
#undef RET_ON_PROTOTYPE_OPCODE
|
||||
#undef CHECK_PROTOTYPE_OPCODE_GEN
|
||||
#undef OPCODE_ERROR
|
||||
|
||||
} // namespace wasm
|
||||
|
@ -25,8 +25,10 @@ class Simd128;
|
||||
// For every two types connected by a line, the top type is a
|
||||
// (direct) subtype of the bottom type.
|
||||
//
|
||||
// AnyRef
|
||||
// / | \
|
||||
// AnyRef
|
||||
// / \
|
||||
// / EqRef
|
||||
// / / \
|
||||
// FuncRef ExnRef OptRef(S)
|
||||
// \ | / \
|
||||
// I32 I64 F32 F64 NullRef Ref(S)
|
||||
@ -38,7 +40,7 @@ class Simd128;
|
||||
// - "ref" types per https://github.com/WebAssembly/function-references
|
||||
// - "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.
|
||||
#define FOREACH_VALUE_TYPE(V) \
|
||||
V(Stmt, -1, Void, None, 'v', "<stmt>") \
|
||||
@ -71,11 +73,15 @@ class ValueType {
|
||||
constexpr ValueType() : bit_field_(KindField::encode(kStmt)) {}
|
||||
explicit constexpr ValueType(Kind kind)
|
||||
: bit_field_(KindField::encode(kind)) {
|
||||
#if V8_HAS_CXX14_CONSTEXPR
|
||||
DCHECK(!has_immediate());
|
||||
#endif
|
||||
}
|
||||
constexpr ValueType(Kind kind, uint32_t ref_index)
|
||||
: bit_field_(KindField::encode(kind) | RefIndexField::encode(ref_index)) {
|
||||
#if V8_HAS_CXX14_CONSTEXPR
|
||||
DCHECK(has_immediate());
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr Kind kind() const { return KindField::decode(bit_field_); }
|
||||
@ -110,27 +116,25 @@ class ValueType {
|
||||
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.
|
||||
bool IsSubTypeOf(ValueType other) const {
|
||||
return (*this == other) ||
|
||||
(kind() == kNullRef &&
|
||||
(other.kind() == kAnyRef || other.kind() == kFuncRef ||
|
||||
other.kind() == kExnRef || other.kind() == kOptRef)) ||
|
||||
(other.kind() == kAnyRef &&
|
||||
(kind() == kFuncRef || kind() == kExnRef || kind() == kOptRef ||
|
||||
kind() == kRef)) ||
|
||||
constexpr bool IsSubTypeOf(ValueType other) const {
|
||||
return (*this == other) || (other.kind() == kAnyRef && IsReferenceType()) ||
|
||||
(kind() == kNullRef && other.kind() != kRef &&
|
||||
other.IsReferenceType()) ||
|
||||
(other.kind() == kEqRef &&
|
||||
(kind() == kExnRef || kind() == kOptRef || kind() == kRef)) ||
|
||||
(kind() == kRef && other.kind() == kOptRef &&
|
||||
ref_index() == other.ref_index());
|
||||
}
|
||||
|
||||
bool IsReferenceType() const {
|
||||
constexpr bool IsReferenceType() const {
|
||||
return kind() == kAnyRef || kind() == kFuncRef || kind() == kNullRef ||
|
||||
kind() == kExnRef || kind() == kRef || kind() == kOptRef ||
|
||||
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.
|
||||
static ValueType CommonSubType(ValueType a, ValueType b) {
|
||||
if (a == b) return a;
|
||||
@ -143,12 +147,14 @@ class ValueType {
|
||||
// {a} and {b} are not each other's subtype.
|
||||
// If one of them is not nullable, their greatest subtype is bottom,
|
||||
// otherwise null.
|
||||
return (a.kind() == kRef || b.kind() == kRef) ? ValueType(kBottom)
|
||||
: ValueType(kNullRef);
|
||||
if (a.kind() == kRef || b.kind() == kRef) return ValueType(kBottom);
|
||||
return ValueType(kNullRef);
|
||||
}
|
||||
|
||||
ValueTypeCode value_type_code() const {
|
||||
constexpr ValueTypeCode value_type_code() const {
|
||||
#if V8_HAS_CXX14_CONSTEXPR
|
||||
DCHECK_NE(kBottom, kind());
|
||||
#endif
|
||||
|
||||
constexpr ValueTypeCode kValueTypeCode[] = {
|
||||
#define TYPE_CODE(kind, log2Size, code, ...) kLocal##code,
|
||||
@ -159,8 +165,10 @@ class ValueType {
|
||||
return kValueTypeCode[kind()];
|
||||
}
|
||||
|
||||
MachineType machine_type() const {
|
||||
constexpr MachineType machine_type() const {
|
||||
#if V8_HAS_CXX14_CONSTEXPR
|
||||
DCHECK_NE(kBottom, kind());
|
||||
#endif
|
||||
|
||||
constexpr MachineType kMachineType[] = {
|
||||
#define MACH_TYPE(kind, log2Size, code, machineType, ...) \
|
||||
@ -172,7 +180,7 @@ class ValueType {
|
||||
return kMachineType[kind()];
|
||||
}
|
||||
|
||||
MachineRepresentation machine_representation() {
|
||||
constexpr MachineRepresentation machine_representation() const {
|
||||
return machine_type().representation();
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,35 @@ WASM_EXEC_TEST(BasicStruct) {
|
||||
kExprEnd};
|
||||
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);
|
||||
builder->WriteTo(&buffer);
|
||||
|
||||
@ -168,6 +197,9 @@ WASM_EXEC_TEST(BasicStruct) {
|
||||
|
||||
CHECK_EQ(52, testing::CallWasmFunctionForTesting(isolate, instance, &thrower,
|
||||
"m", 0, nullptr));
|
||||
|
||||
CHECK_EQ(0b1001, testing::CallWasmFunctionForTesting(
|
||||
isolate, instance, &thrower, "n", 0, nullptr));
|
||||
}
|
||||
|
||||
WASM_EXEC_TEST(BasicArray) {
|
||||
|
@ -436,6 +436,7 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
|
||||
#define WASM_REF_FUNC(val) kExprRefFunc, val
|
||||
#define WASM_REF_IS_NULL(val) val, kExprRefIsNull
|
||||
#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) \
|
||||
default_value, length, WASM_GC_OP(kExprArrayNew), static_cast<byte>(index)
|
||||
|
Loading…
Reference in New Issue
Block a user