Commit 327c2d2a authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Introduce HeapTypeImmediate

Some operations require an immediate argument that represents a heap
type. This CL introduces a class to represent it and uses it where
appropriate. Also, the redundant TypeIndexImmediate is removed.

Bug: v8:7748
Change-Id: Ib4b1d50764a79f5dd3240688f8165c39745eaad8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2260874
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68522}
parent 46f9931c
......@@ -5289,7 +5289,12 @@ Node* WasmGraphBuilder::ArrayNew(uint32_t array_index,
return a;
}
Node* WasmGraphBuilder::RttCanon(uint32_t type_index) {
Node* WasmGraphBuilder::RttCanon(wasm::HeapType type) {
if (is_generic_heap_type(type)) {
// TODO(7748): Implement this.
UNIMPLEMENTED();
}
uint32_t type_index = static_cast<uint32_t>(type);
// This logic is duplicated from module-instantiate.cc.
// TODO(jkummerow): Find a nicer solution.
int map_index = 0;
......
......@@ -397,7 +397,7 @@ class WasmGraphBuilder {
Node* ArraySet(Node* array_object, const wasm::ArrayType* type, Node* index,
Node* value, wasm::WasmCodePosition position);
Node* ArrayLen(Node* array_object, wasm::WasmCodePosition position);
Node* RttCanon(uint32_t type_index);
Node* RttCanon(wasm::HeapType type);
bool has_simd() const { return has_simd_; }
......
......@@ -3609,7 +3609,7 @@ class LiftoffCompiler {
unsupported(decoder, kGC, "array.len");
}
void RttCanon(FullDecoder* decoder, const TypeIndexImmediate<validate>& imm,
void RttCanon(FullDecoder* decoder, const HeapTypeImmediate<validate>& imm,
Value* result) {
// TODO(7748): Implement.
unsupported(decoder, kGC, "rtt.canon");
......
......@@ -255,6 +255,8 @@ ValueType read_value_type(Decoder* decoder, const byte* pc,
} // namespace value_type_reader
// Helpers for decoding different kinds of immediates which follow bytecodes.
// TODO(manoskouk): Some Immediates look at current pc and some introduce an
// offset. Clear up this situation.
template <Decoder::ValidateFlag validate>
struct LocalIndexImmediate {
uint32_t index;
......@@ -319,17 +321,6 @@ struct ImmF64Immediate {
}
};
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;
......@@ -475,6 +466,8 @@ struct TableIndexImmediate {
}
};
// TODO(jkummerow): Introduce a common superclass for StructIndexImmediate and
// ArrayIndexImmediate? Maybe even FunctionIndexImmediate too?
template <Decoder::ValidateFlag validate>
struct StructIndexImmediate {
uint32_t index = 0;
......@@ -508,17 +501,6 @@ struct ArrayIndexImmediate {
}
};
// TODO(jkummerow): Make this a superclass of StructIndexImmediate and
// ArrayIndexImmediate? Maybe even FunctionIndexImmediate too?
template <Decoder::ValidateFlag validate>
struct TypeIndexImmediate {
uint32_t index = 0;
uint32_t length = 0;
inline TypeIndexImmediate(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<validate>(pc, &length, "type index");
}
};
template <Decoder::ValidateFlag validate>
struct CallIndirectImmediate {
uint32_t table_index;
......@@ -735,6 +717,54 @@ struct TableCopyImmediate {
}
};
template <Decoder::ValidateFlag validate>
struct HeapTypeImmediate {
uint32_t length = 1;
HeapType type = static_cast<HeapType>(kHeapFunc - 1);
inline HeapTypeImmediate(const WasmFeatures& enabled, Decoder* decoder,
const byte* pc) {
// Check for negative 1-byte value first, e.g. -0x10 == funcref. If there's
// no match, we'll read a uint32 from the same offset later.
uint8_t heap_index =
decoder->read_u8<validate>(pc + 1, "heap type immediate");
switch (static_cast<ValueTypeCode>(heap_index)) {
#define REF_TYPE_CASE(heap_type, feature) \
case kLocal##heap_type##Ref: { \
type = kHeap##heap_type; \
if (!VALIDATE(enabled.has_##feature())) { \
decoder->errorf( \
pc, "invalid heap type '%s', enable with --experimental-wasm-%s", \
ValueType::heap_name(kHeap##heap_type).c_str(), #feature); \
} \
break; \
}
REF_TYPE_CASE(Func, reftypes)
REF_TYPE_CASE(Extern, reftypes)
REF_TYPE_CASE(Eq, gc)
REF_TYPE_CASE(Exn, eh)
#undef REF_TYPE_CASE
default:
uint32_t type_index = decoder->read_u32v<validate>(
pc + 1, &length, "heap type immediate");
if (!VALIDATE(type_index < kV8MaxWasmTypes)) {
decoder->errorf(pc + 1,
"Type index %u is greater than the maximum number "
"%zu of type definitions supported by V8",
type_index, kV8MaxWasmTypes);
break;
}
type = static_cast<HeapType>(type_index);
if (!VALIDATE(enabled.has_typed_funcref())) {
decoder->errorf(pc,
"invalid heap type %d, enable with "
"--experimental-wasm-typed-funcref",
type_index);
}
break;
}
}
};
// An entry on the value stack.
struct ValueBase {
const byte* pc = nullptr;
......@@ -950,7 +980,7 @@ struct ControlBase {
const ArrayIndexImmediate<validate>& imm, const Value& index, \
const Value& value) \
F(ArrayLen, const Value& array_obj, Value* result) \
F(RttCanon, const TypeIndexImmediate<validate>& imm, Value* result) \
F(RttCanon, const HeapTypeImmediate<validate>& imm, Value* result) \
F(PassThrough, const Value& from, Value* to)
// Generic Wasm bytecode decoder with utilities for decoding immediates,
......@@ -1118,15 +1148,6 @@ class WasmDecoder : public Decoder {
return true;
}
inline bool Validate(const byte* pc, RefNullImmediate<validate>& imm) {
if (!VALIDATE(imm.type.is_nullable())) {
errorf(pc + 1, "ref.null does not exist for %s",
imm.type.type_name().c_str());
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];
......@@ -1187,15 +1208,6 @@ class WasmDecoder : public Decoder {
return true;
}
inline bool Validate(const byte* pc, TypeIndexImmediate<validate>& imm) {
if (!VALIDATE(module_ != nullptr && (module_->has_struct(imm.index) ||
module_->has_array(imm.index)))) {
errorf(pc, "invalid type index: %u", imm.index);
return false;
}
return true;
}
inline bool CanReturnCall(const FunctionSig* target_sig) {
if (target_sig == nullptr) return false;
size_t num_returns = sig_->return_count();
......@@ -1431,6 +1443,19 @@ class WasmDecoder : public Decoder {
return true;
}
inline bool Validate(const byte* pc, HeapTypeImmediate<validate>& imm) {
if (!VALIDATE(is_generic_heap_type(imm.type) ||
module_->has_array(static_cast<uint32_t>(imm.type)) ||
module_->has_struct(static_cast<uint32_t>(imm.type)))) {
errorf(
pc,
"Type index %u does not refer to a struct or array type definition",
imm.type);
return false;
}
return true;
}
static uint32_t OpcodeLength(WasmDecoder* decoder, const byte* pc) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
switch (opcode) {
......@@ -1524,7 +1549,7 @@ class WasmDecoder : public Decoder {
return 1 + imm.length;
}
case kExprRefNull: {
RefNullImmediate<validate> imm(WasmFeatures::All(), decoder, pc);
HeapTypeImmediate<validate> imm(WasmFeatures::All(), decoder, pc);
return 1 + imm.length;
}
case kExprRefIsNull: {
......@@ -1681,9 +1706,9 @@ class WasmDecoder : public Decoder {
return 2 + imm.length;
}
case kExprRttCanon: {
// TODO(7748): Introduce "HeapTypeImmediate" and use it here.
TypeIndexImmediate<validate> heaptype(decoder, pc + 2);
return 2 + heaptype.length;
HeapTypeImmediate<validate> imm(WasmFeatures::All(), decoder,
pc + 2);
return 2 + imm.length;
}
case kExprRttSub:
// TODO(7748): Implement.
......@@ -2533,9 +2558,9 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
case kExprRefNull: {
CHECK_PROTOTYPE_OPCODE(reftypes);
RefNullImmediate<validate> imm(this->enabled_, this, this->pc_);
HeapTypeImmediate<validate> imm(this->enabled_, this, this->pc_);
if (!this->Validate(this->pc_, imm)) break;
Value* value = Push(imm.type);
Value* value = Push(ValueType::Ref(imm.type, kNullable));
CALL_INTERFACE_IF_REACHABLE(RefNull, value);
len = 1 + imm.length;
break;
......@@ -3457,12 +3482,10 @@ class WasmFullDecoder : public WasmDecoder<validate> {
break;
}
case kExprRttCanon: {
// TODO(7748): Introduce HeapTypeImmediate and use that here.
TypeIndexImmediate<validate> imm(this, this->pc_ + len);
HeapTypeImmediate<validate> imm(this->enabled_, this, this->pc_ + 1);
len += imm.length;
if (!this->Validate(this->pc_ + len, imm)) break;
Value* value =
Push(ValueType::Rtt(static_cast<HeapType>(imm.index), 1));
Value* value = Push(ValueType::Rtt(imm.type, 1));
CALL_INTERFACE_IF_REACHABLE(RttCanon, imm, value);
break;
}
......
......@@ -701,9 +701,9 @@ class WasmGraphBuildingInterface {
result->node = BUILD(ArrayLen, array_obj.node, decoder->position());
}
void RttCanon(FullDecoder* decoder, const TypeIndexImmediate<validate>& imm,
void RttCanon(FullDecoder* decoder, const HeapTypeImmediate<validate>& imm,
Value* result) {
result->node = BUILD(RttCanon, imm.index);
result->node = BUILD(RttCanon, imm.type);
}
void PassThrough(FullDecoder* decoder, const Value& from, Value* to) {
......
......@@ -1661,19 +1661,15 @@ class ModuleDecoderImpl : public Decoder {
}
case kExprRefNull: {
if (enabled_features_.has_reftypes() || enabled_features_.has_eh()) {
RefNullImmediate<Decoder::kValidate> imm(WasmFeatures::All(), this,
pc() - 1);
if (!imm.type.is_reference_type()) {
errorf(pc() - 1, "ref.null is not supported for %s",
imm.type.type_name().c_str());
break;
}
HeapTypeImmediate<Decoder::kValidate> imm(WasmFeatures::All(), this,
pc() - 1);
expr.kind = WasmInitExpr::kRefNullConst;
len = imm.length;
ValueType type = ValueType::Ref(imm.type, kNullable);
if (expected != kWasmStmt &&
!IsSubtypeOf(imm.type, expected, module_.get())) {
!IsSubtypeOf(type, expected, module_.get())) {
errorf(pos, "type error in init expression, expected %s, got %s",
expected.type_name().c_str(), imm.type.type_name().c_str());
expected.type_name().c_str(), type.type_name().c_str());
}
break;
}
......@@ -2027,8 +2023,8 @@ class ModuleDecoderImpl : public Decoder {
if (failed()) return index;
switch (opcode) {
case kExprRefNull: {
RefNullImmediate<kValidate> imm(WasmFeatures::All(), this,
this->pc() - 1);
HeapTypeImmediate<kValidate> imm(WasmFeatures::All(), this,
this->pc() - 1);
consume_bytes(imm.length, "ref.null immediate");
index = WasmElemSegment::kNullIndex;
break;
......
......@@ -193,7 +193,6 @@ class ValueType {
}
constexpr uint32_t heap_type_code() const {
CONSTEXPR_DCHECK(encoding_needs_heap_type());
switch (heap_type()) {
case kHeapFunc:
return kLocalFuncRef;
......@@ -239,23 +238,38 @@ class ValueType {
return kShortName[kind()];
}
const std::string type_name() const {
static std::string heap_name(HeapType type) {
switch (type) {
case kHeapFunc:
return std::string("func");
case kHeapExtern:
return std::string("extern");
case kHeapEq:
return std::string("eq");
case kHeapExn:
return std::string("exn");
default:
return std::to_string(static_cast<uint32_t>(type));
}
}
std::string type_name() const {
std::ostringstream buf;
switch (kind()) {
case kRef:
buf << "(ref " << heap_name() << ")";
buf << "(ref " << heap_name(heap_type()) << ")";
break;
case kOptRef:
if (is_generic_heap_type(heap_type())) {
// We prefer the shorthand to be backwards-compatible with previous
// proposals.
buf << heap_name() << "ref";
buf << heap_name(heap_type()) << "ref";
} else {
buf << "(ref null " << heap_name() << ")";
buf << "(ref null " << heap_name(heap_type()) << ")";
}
break;
case kRtt:
buf << "(rtt " << depth() << " " << heap_name() + ")";
buf << "(rtt " << depth() << " " << heap_name(heap_type()) + ")";
break;
default:
buf << kind_name();
......@@ -281,21 +295,6 @@ class ValueType {
return kTypeName[kind()];
}
const std::string heap_name() const {
switch (heap_type()) {
case kHeapFunc:
return std::string("func");
case kHeapExtern:
return std::string("extern");
case kHeapEq:
return std::string("eq");
case kHeapExn:
return std::string("exn");
default:
return std::to_string(static_cast<uint32_t>(heap_type()));
}
}
uint32_t bit_field_;
};
......
......@@ -569,7 +569,7 @@ void WasmModuleBuilder::WriteTo(ZoneBuffer* buffer) const {
break;
case WasmInitExpr::kRefNullConst:
buffer->write_u8(kExprRefNull);
WriteValueType(buffer, global.type);
buffer->write_u32v(global.type.heap_type_code());
break;
case WasmInitExpr::kRefFuncConst:
UNIMPLEMENTED();
......
......@@ -219,7 +219,7 @@ TEST(WasmRefAsNonNull) {
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)))),
WASM_REF_NULL(type_index)))),
kExprEnd});
tester.CompileModule();
......@@ -288,11 +288,11 @@ TEST(WasmRefEq) {
WASM_I32V(1)),
WASM_I32_ADD(WASM_I32_SHL( // false
WASM_REF_EQ(WASM_GET_LOCAL(n_local_index),
WASM_REF_NULL_GC(type_index)),
WASM_REF_NULL(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_REF_NULL(type_index),
WASM_REF_NULL(type_index)),
WASM_I32V(3))))),
kExprEnd});
......
......@@ -3159,8 +3159,8 @@ class WasmInterpreterInternals {
break;
}
case kExprRefNull: {
RefNullImmediate<Decoder::kNoValidate> imm(WasmFeatures::All(),
&decoder, code->at(pc));
HeapTypeImmediate<Decoder::kNoValidate> imm(WasmFeatures::All(),
&decoder, code->at(pc));
len = 1 + imm.length;
Push(WasmValue(isolate_->factory()->null_value()));
break;
......
......@@ -439,8 +439,6 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
struct_obj, value, WASM_GC_OP(kExprStructSet), static_cast<byte>(typeidx), \
static_cast<byte>(fieldidx)
#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_AS_NON_NULL(val) val, kExprRefAsNonNull
......
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