Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions scripts/test/fuzzing.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@
'vacuum-stack-switching.wast'
# TODO: fuzzer support for custom descriptors
'custom-descriptors.wast',
# TODO: fuzzer support for exact heap types
'exact.wast',
]


Expand Down
4 changes: 4 additions & 0 deletions src/parser/contexts.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ struct NullTypeParserCtx {
Result<Index> getTypeIndex(Name) { return 1; }
Result<HeapTypeT> getHeapTypeFromIdx(Index) { return Ok{}; }

HeapTypeT makeExact(HeapTypeT) { return Ok{}; }

DataStringT makeDataString() { return Ok{}; }
void appendDataString(DataStringT&, std::string_view) {}

Expand Down Expand Up @@ -251,6 +253,8 @@ template<typename Ctx> struct TypeParserCtx {
return HeapTypes::nocont.getBasic(share);
}

HeapTypeT makeExact(HeapTypeT type) { return type.with(Exact); }

TypeT makeI32() { return Type::i32; }
TypeT makeI64() { return Type::i64; }
TypeT makeF32() { return Type::f32; }
Expand Down
10 changes: 10 additions & 0 deletions src/parser/parsers.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ Result<typename Ctx::HeapTypeT> absheaptype(Ctx& ctx, Shareability share) {
}

// heaptype ::= x:typeidx => types[x]
// | '(' 'exact' x:typeidx ')' => exact types[x]
// | t:absheaptype => unshared t
// | '(' 'shared' t:absheaptype ')' => shared t
template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx) {
Expand All @@ -442,6 +443,15 @@ template<typename Ctx> Result<typename Ctx::HeapTypeT> heaptype(Ctx& ctx) {
return *t;
}

if (ctx.in.takeSExprStart("exact"sv)) {
auto t = typeidx(ctx);
CHECK_ERR(t);
if (!ctx.in.takeRParen()) {
return ctx.in.err("expected end of exact heap type");
}
return ctx.makeExact(*t);
}

auto share = ctx.in.takeSExprStart("shared"sv) ? Shared : Unshared;
auto t = absheaptype(ctx, share);
CHECK_ERR(t);
Expand Down
2 changes: 2 additions & 0 deletions src/wasm-binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ enum EncodedType {
SubFinal = 0x4f,
Shared = 0x65,
SharedLEB = -0x1b, // Also 0x65 as an SLEB128
Exact = 0x62,
ExactLEB = -0x1e, // Also 0x62 as an SLEB128
Rec = 0x4e,
Descriptor = 0x4d,
Describes = 0x4c,
Expand Down
41 changes: 30 additions & 11 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ uint32_t WasmBinaryWriter::getElementSegmentIndex(Name name) const {
}

uint32_t WasmBinaryWriter::getTypeIndex(HeapType type) const {
auto it = indexedTypes.indices.find(type);
auto it = indexedTypes.indices.find(type.with(Inexact));
#ifndef NDEBUG
if (it == indexedTypes.indices.end()) {
std::cout << "Missing type: " << type << '\n';
Expand Down Expand Up @@ -1668,8 +1668,10 @@ void WasmBinaryWriter::writeHeapType(HeapType type) {
if (!wasm->features.hasGC()) {
type = type.getTop();
}

if (!type.isBasic()) {
if (type.isExact()) {
o << uint8_t(BinaryConsts::EncodedType::Exact);
}
o << S64LEB(getTypeIndex(type)); // TODO: Actually s33
return;
}
Expand Down Expand Up @@ -2183,12 +2185,20 @@ Type WasmBinaryReader::getType() { return getType(getS32LEB()); }

HeapType WasmBinaryReader::getHeapType() {
auto type = getS64LEB(); // TODO: Actually s33
auto exactness = Inexact;
if (type == BinaryConsts::EncodedType::ExactLEB) {
exactness = Exact;
type = getS64LEB(); // TODO: Actually s33
}
// Single heap types are negative; heap type indices are non-negative
if (type >= 0) {
if (size_t(type) >= types.size()) {
throwError("invalid signature index: " + std::to_string(type));
throwError("invalid type index: " + std::to_string(type));
}
return types[type];
return types[type].with(exactness);
}
if (exactness == Exact) {
throwError("invalid type index: " + std::to_string(type));
}
auto share = Unshared;
if (type == BinaryConsts::EncodedType::SharedLEB) {
Expand All @@ -2198,10 +2208,8 @@ HeapType WasmBinaryReader::getHeapType() {
HeapType ht;
if (getBasicHeapType(type, ht)) {
return ht.getBasic(share);
} else {
throwError("invalid wasm heap type: " + std::to_string(type));
}
WASM_UNREACHABLE("unexpected type");
throwError("invalid wasm heap type: " + std::to_string(type));
}

HeapType WasmBinaryReader::getIndexedHeapType() {
Expand Down Expand Up @@ -2325,6 +2333,20 @@ void WasmBinaryReader::readTypes() {

auto readHeapType = [&]() -> HeapType {
int64_t htCode = getS64LEB(); // TODO: Actually s33
auto exactness = Inexact;
if (htCode == BinaryConsts::EncodedType::ExactLEB) {
exactness = Exact;
htCode = getS64LEB(); // TODO: Actually s33
}
if (htCode >= 0) {
if (size_t(htCode) >= builder.size()) {
throwError("invalid type index: " + std::to_string(htCode));
}
return builder.getTempHeapType(size_t(htCode)).with(exactness);
}
if (exactness == Exact) {
throwError("invalid type index: " + std::to_string(htCode));
}
auto share = Unshared;
if (htCode == BinaryConsts::EncodedType::SharedLEB) {
share = Shared;
Expand All @@ -2334,10 +2356,7 @@ void WasmBinaryReader::readTypes() {
if (getBasicHeapType(htCode, ht)) {
return ht.getBasic(share);
}
if (size_t(htCode) >= builder.size()) {
throwError("invalid type index: " + std::to_string(htCode));
}
return builder.getTempHeapType(size_t(htCode));
throwError("invalid wasm heap type: " + std::to_string(htCode));
};
auto makeType = [&](int32_t typeCode) {
Type type;
Expand Down
4 changes: 2 additions & 2 deletions src/wasm/wasm-type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1621,9 +1621,9 @@ void TypePrinter::printHeapTypeName(HeapType type) {
if (type.isBasic()) {
print(type);
} else {
generator(type).name.print(os);
generator(type.with(Inexact)).name.print(os);
#if TRACE_CANONICALIZATION
os << "(;" << ((type.getID() >> 4) % 1000) << ";) ";
os << "(;" << ((type.with(Inexact).getID() >> 4) % 1000) << ";) ";
#endif
}
if (type.isExact()) {
Expand Down
52 changes: 52 additions & 0 deletions test/lit/basic/exact.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.

;; RUN: wasm-opt %s -all -o %t.text.wast -g -S
;; RUN: wasm-as %s -all -g -o %t.wasm
;; RUN: wasm-dis %t.wasm -all -o %t.bin.wast
;; RUN: wasm-as %s -all -o %t.nodebug.wasm
;; RUN: wasm-dis %t.nodebug.wasm -all -o %t.bin.nodebug.wast
;; RUN: cat %t.text.wast | filecheck %s --check-prefix=CHECK-TEXT
;; RUN: cat %t.bin.wast | filecheck %s --check-prefix=CHECK-BIN
;; RUN: cat %t.bin.nodebug.wast | filecheck %s --check-prefix=CHECK-BIN-NODEBUG

(module
(rec
;; CHECK-TEXT: (rec
;; CHECK-TEXT-NEXT: (type $a (struct (field (ref null (exact $a))) (field (ref (exact $b)))))
;; CHECK-BIN: (rec
;; CHECK-BIN-NEXT: (type $a (struct (field (ref null (exact $a))) (field (ref (exact $b)))))
(type $a (struct (field (ref null (exact 0)) (ref (exact 1)))))
;; CHECK-TEXT: (type $b (struct (field (ref (exact $a))) (field (ref null (exact $b)))))
;; CHECK-BIN: (type $b (struct (field (ref (exact $a))) (field (ref null (exact $b)))))
(type $b (struct (field (ref (exact $a)) (ref null (exact $b)))))
)

;; CHECK-TEXT: (type $2 (func (param (ref null (exact $a)) (ref (exact $b))) (result (ref (exact $a)) (ref null (exact $b)))))

;; CHECK-TEXT: (func $foo (type $2) (param $0 (ref null (exact $a))) (param $1 (ref (exact $b))) (result (ref (exact $a)) (ref null (exact $b)))
;; CHECK-TEXT-NEXT: (local $2 (ref null (exact $a)))
;; CHECK-TEXT-NEXT: (unreachable)
;; CHECK-TEXT-NEXT: )
;; CHECK-BIN: (type $2 (func (param (ref null (exact $a)) (ref (exact $b))) (result (ref (exact $a)) (ref null (exact $b)))))

;; CHECK-BIN: (func $foo (type $2) (param $0 (ref null (exact $a))) (param $1 (ref (exact $b))) (result (ref (exact $a)) (ref null (exact $b)))
;; CHECK-BIN-NEXT: (local $2 (ref null (exact $a)))
;; CHECK-BIN-NEXT: (unreachable)
;; CHECK-BIN-NEXT: )
(func $foo (param (ref null (exact $a)) (ref (exact $b)))
(result (ref (exact $a)) (ref null (exact $b)))
(local (ref null (exact $a)))
(unreachable)
)
)
;; CHECK-BIN-NODEBUG: (rec
;; CHECK-BIN-NODEBUG-NEXT: (type $0 (struct (field (ref null (exact $0))) (field (ref (exact $1)))))

;; CHECK-BIN-NODEBUG: (type $1 (struct (field (ref (exact $0))) (field (ref null (exact $1)))))

;; CHECK-BIN-NODEBUG: (type $2 (func (param (ref null (exact $0)) (ref (exact $1))) (result (ref (exact $0)) (ref null (exact $1)))))

;; CHECK-BIN-NODEBUG: (func $0 (type $2) (param $0 (ref null (exact $0))) (param $1 (ref (exact $1))) (result (ref (exact $0)) (ref null (exact $1)))
;; CHECK-BIN-NODEBUG-NEXT: (local $2 (ref null (exact $0)))
;; CHECK-BIN-NODEBUG-NEXT: (unreachable)
;; CHECK-BIN-NODEBUG-NEXT: )