Skip to content

Commit 4e1eaa7

Browse files
authored
Update the binary format for exact heap types (#7502)
Although we now store exactness on `Type` instead of `HeapType`, the binary format still encodes exactness as part of the heap type. Update the binary parser to read exactness wherever a heap type (but not just a type index) is expected and update the binary writer similarly.
1 parent 9aa06be commit 4e1eaa7

File tree

4 files changed

+104
-137
lines changed

4 files changed

+104
-137
lines changed

src/wasm-binary.h

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,6 @@ enum SegmentFlag {
307307
enum BrOnCastFlag {
308308
InputNullable = 1 << 0,
309309
OutputNullable = 1 << 1,
310-
InputExact = 1 << 2,
311-
OutputExact = 1 << 3,
312310
};
313311

314312
enum EncodedType {
@@ -334,7 +332,6 @@ enum EncodedType {
334332
eqref = -0x13, // 0x6d
335333
nonnullable = -0x1c, // 0x64
336334
nullable = -0x1d, // 0x63
337-
exact = -0x1e, // 0x62
338335
contref = -0x18, // 0x68
339336
nullcontref = -0x0b, // 0x75
340337
// exception handling
@@ -351,6 +348,8 @@ enum EncodedType {
351348
SubFinal = 0x4f,
352349
Shared = 0x65,
353350
SharedLEB = -0x1b, // Also 0x65 as an SLEB128
351+
Exact = 0x62,
352+
ExactLEB = -0x1e, // Also 0x62 as an SLEB128
354353
Rec = 0x4e,
355354
Descriptor = 0x4d,
356355
Describes = 0x4c,
@@ -1135,8 +1134,6 @@ enum ASTNodes {
11351134
I31GetS = 0x1d,
11361135
I31GetU = 0x1e,
11371136
RefI31Shared = 0x1f,
1138-
RefTestRT = 0x20,
1139-
RefCastRT = 0x21,
11401137

11411138
// Shared GC Opcodes
11421139

@@ -1378,7 +1375,7 @@ class WasmBinaryWriter {
13781375

13791376
// Writes an arbitrary heap type, which may be indexed or one of the
13801377
// basic types like funcref.
1381-
void writeHeapType(HeapType type);
1378+
void writeHeapType(HeapType type, Exactness exact);
13821379
// Writes an indexed heap type. Note that this is encoded differently than a
13831380
// general heap type because it does not allow negative values for basic heap
13841381
// types.
@@ -1509,8 +1506,7 @@ class WasmBinaryReader {
15091506
Type getType();
15101507
// Get a type given the initial S32LEB has already been read, and is provided.
15111508
Type getType(int code);
1512-
Type getTypeNoExact(int code);
1513-
HeapType getHeapType();
1509+
std::pair<HeapType, Exactness> getHeapType();
15141510
HeapType getIndexedHeapType();
15151511

15161512
Type getConcreteType();

src/wasm-type.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ class Type {
382382
bool isExact() const {
383383
return isRef() && !getHeapType().isBasic() && (id & ExactMask);
384384
}
385-
bool isInexact() const { return isRef() && !(id & ExactMask); }
385+
bool isInexact() const { return isRef() && !isExact(); }
386386
HeapType getHeapType() const {
387387
assert(isRef());
388388
HeapType masked(id & ~NullMask);

src/wasm/wasm-binary.cpp

Lines changed: 82 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ void WasmBinaryWriter::writeTypes() {
279279
}
280280
if (super) {
281281
o << U32LEB(1);
282-
writeHeapType(*super);
282+
writeHeapType(*super, Inexact);
283283
} else {
284284
o << U32LEB(0);
285285
}
@@ -289,11 +289,11 @@ void WasmBinaryWriter::writeTypes() {
289289
}
290290
if (auto desc = type.getDescribedType()) {
291291
o << uint8_t(BinaryConsts::EncodedType::Describes);
292-
writeHeapType(*desc);
292+
writeHeapType(*desc, Inexact);
293293
}
294294
if (auto desc = type.getDescriptorType()) {
295295
o << uint8_t(BinaryConsts::EncodedType::Descriptor);
296-
writeHeapType(*desc);
296+
writeHeapType(*desc, Inexact);
297297
}
298298
switch (type.getKind()) {
299299
case HeapTypeKind::Func: {
@@ -322,7 +322,7 @@ void WasmBinaryWriter::writeTypes() {
322322
break;
323323
case HeapTypeKind::Cont:
324324
o << uint8_t(BinaryConsts::EncodedType::Cont);
325-
writeHeapType(type.getContinuation().type);
325+
writeHeapType(type.getContinuation().type, Inexact);
326326
break;
327327
case HeapTypeKind::Basic:
328328
WASM_UNREACHABLE("unexpected kind");
@@ -1574,12 +1574,6 @@ void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) {
15741574

15751575
void WasmBinaryWriter::writeType(Type type) {
15761576
if (type.isRef()) {
1577-
// Exact references are introduced by the custom descriptors feature, but
1578-
// can be used internally even when it is not enabled. In that case, we have
1579-
// to generalize the types to be inexact before writing them.
1580-
if (!wasm->features.hasCustomDescriptors()) {
1581-
type = Type(type.getHeapType(), type.getNullability(), Inexact);
1582-
}
15831577
// The only reference types allowed without GC are funcref, externref, and
15841578
// exnref. We internally use more refined versions of those types, but we
15851579
// cannot emit those without GC.
@@ -1596,12 +1590,6 @@ void WasmBinaryWriter::writeType(Type type) {
15961590
type = Type(type.getHeapType().getTop(), Nullable);
15971591
}
15981592
}
1599-
// If the type is exact, emit the exact prefix and continue on without
1600-
// considering exactness.
1601-
if (type.isExact()) {
1602-
o << S32LEB(BinaryConsts::EncodedType::exact);
1603-
type = Type(type.getHeapType(), type.getNullability(), Inexact);
1604-
}
16051593
auto heapType = type.getHeapType();
16061594
if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) {
16071595
switch (heapType.getBasic(Unshared)) {
@@ -1657,7 +1645,7 @@ void WasmBinaryWriter::writeType(Type type) {
16571645
} else {
16581646
o << S32LEB(BinaryConsts::EncodedType::nonnullable);
16591647
}
1660-
writeHeapType(type.getHeapType());
1648+
writeHeapType(type.getHeapType(), type.getExactness());
16611649
return;
16621650
}
16631651
int ret = 0;
@@ -1688,14 +1676,20 @@ void WasmBinaryWriter::writeType(Type type) {
16881676
o << S32LEB(ret);
16891677
}
16901678

1691-
void WasmBinaryWriter::writeHeapType(HeapType type) {
1679+
void WasmBinaryWriter::writeHeapType(HeapType type, Exactness exactness) {
16921680
// ref.null always has a bottom heap type in Binaryen IR, but those types are
16931681
// only actually valid with GC. Otherwise, emit the corresponding valid top
16941682
// types instead.
1683+
if (!wasm->features.hasCustomDescriptors()) {
1684+
exactness = Inexact;
1685+
}
16951686
if (!wasm->features.hasGC()) {
16961687
type = type.getTop();
16971688
}
1698-
1689+
assert(!type.isBasic() || exactness == Inexact);
1690+
if (exactness == Exact) {
1691+
o << uint8_t(BinaryConsts::EncodedType::Exact);
1692+
}
16991693
if (!type.isBasic()) {
17001694
o << S64LEB(getTypeIndex(type)); // TODO: Actually s33
17011695
return;
@@ -2190,43 +2184,41 @@ Signature WasmBinaryReader::getBlockType() {
21902184
return Signature(Type::none, getType(code));
21912185
}
21922186

2193-
Type WasmBinaryReader::getTypeNoExact(int code) {
2187+
Type WasmBinaryReader::getType(int code) {
21942188
Type type;
21952189
if (getBasicType(code, type)) {
21962190
return type;
21972191
}
2192+
auto [heapType, exactness] = getHeapType();
21982193
switch (code) {
21992194
case BinaryConsts::EncodedType::nullable:
2200-
return Type(getHeapType(), Nullable);
2195+
return Type(heapType, Nullable, exactness);
22012196
case BinaryConsts::EncodedType::nonnullable:
2202-
return Type(getHeapType(), NonNullable);
2197+
return Type(heapType, NonNullable, exactness);
22032198
default:
22042199
throwError("invalid wasm type: " + std::to_string(code));
22052200
}
22062201
WASM_UNREACHABLE("unexpected type");
22072202
}
22082203

2209-
Type WasmBinaryReader::getType(int code) {
2210-
if (code == BinaryConsts::EncodedType::exact) {
2211-
auto type = getTypeNoExact(getS32LEB());
2212-
if (!type.isRef()) {
2213-
throwError("invalid exact prefix on non-reference type");
2214-
}
2215-
return Type(type.getHeapType(), type.getNullability(), Exact);
2216-
}
2217-
return getTypeNoExact(code);
2218-
}
2219-
22202204
Type WasmBinaryReader::getType() { return getType(getS32LEB()); }
22212205

2222-
HeapType WasmBinaryReader::getHeapType() {
2206+
std::pair<HeapType, Exactness> WasmBinaryReader::getHeapType() {
22232207
auto type = getS64LEB(); // TODO: Actually s33
2208+
auto exactness = Inexact;
2209+
if (type == BinaryConsts::EncodedType::ExactLEB) {
2210+
exactness = Exact;
2211+
type = getS64LEB(); // TODO: Actually s33
2212+
}
22242213
// Single heap types are negative; heap type indices are non-negative
22252214
if (type >= 0) {
22262215
if (size_t(type) >= types.size()) {
2227-
throwError("invalid signature index: " + std::to_string(type));
2216+
throwError("invalid type index: " + std::to_string(type));
22282217
}
2229-
return types[type];
2218+
return {types[type], exactness};
2219+
}
2220+
if (exactness == Exact) {
2221+
throwError("invalid type index: " + std::to_string(type));
22302222
}
22312223
auto share = Unshared;
22322224
if (type == BinaryConsts::EncodedType::SharedLEB) {
@@ -2235,11 +2227,9 @@ HeapType WasmBinaryReader::getHeapType() {
22352227
}
22362228
HeapType ht;
22372229
if (getBasicHeapType(type, ht)) {
2238-
return ht.getBasic(share);
2239-
} else {
2240-
throwError("invalid wasm heap type: " + std::to_string(type));
2230+
return {ht.getBasic(share), Inexact};
22412231
}
2242-
WASM_UNREACHABLE("unexpected type");
2232+
throwError("invalid wasm heap type: " + std::to_string(type));
22432233
}
22442234

22452235
HeapType WasmBinaryReader::getIndexedHeapType() {
@@ -2361,23 +2351,34 @@ void WasmBinaryReader::readMemories() {
23612351
void WasmBinaryReader::readTypes() {
23622352
TypeBuilder builder(getU32LEB());
23632353

2364-
auto readHeapType = [&]() -> HeapType {
2354+
auto readHeapType = [&]() -> std::pair<HeapType, Exactness> {
23652355
int64_t htCode = getS64LEB(); // TODO: Actually s33
2356+
auto exactness = Inexact;
2357+
if (htCode == BinaryConsts::EncodedType::ExactLEB) {
2358+
exactness = Exact;
2359+
htCode = getS64LEB(); // TODO: Actually s33
2360+
}
2361+
if (htCode >= 0) {
2362+
if (size_t(htCode) >= builder.size()) {
2363+
throwError("invalid type index: " + std::to_string(htCode));
2364+
}
2365+
return {builder.getTempHeapType(size_t(htCode)), exactness};
2366+
}
2367+
if (exactness == Exact) {
2368+
throwError("invalid type index: " + std::to_string(htCode));
2369+
}
23662370
auto share = Unshared;
23672371
if (htCode == BinaryConsts::EncodedType::SharedLEB) {
23682372
share = Shared;
23692373
htCode = getS64LEB(); // TODO: Actually s33
23702374
}
23712375
HeapType ht;
23722376
if (getBasicHeapType(htCode, ht)) {
2373-
return ht.getBasic(share);
2374-
}
2375-
if (size_t(htCode) >= builder.size()) {
2376-
throwError("invalid type index: " + std::to_string(htCode));
2377+
return {ht.getBasic(share), Inexact};
23772378
}
2378-
return builder.getTempHeapType(size_t(htCode));
2379+
throwError("invalid wasm heap type: " + std::to_string(htCode));
23792380
};
2380-
auto makeTypeNoExact = [&](int32_t typeCode) {
2381+
auto makeType = [&](int32_t typeCode) {
23812382
Type type;
23822383
if (getBasicType(typeCode, type)) {
23832384
return type;
@@ -2390,29 +2391,18 @@ void WasmBinaryReader::readTypes() {
23902391
? Nullable
23912392
: NonNullable;
23922393

2393-
HeapType ht = readHeapType();
2394+
auto [ht, exactness] = readHeapType();
23942395
if (ht.isBasic()) {
2395-
return Type(ht, nullability);
2396+
return Type(ht, nullability, exactness);
23962397
}
23972398

2398-
return builder.getTempRefType(ht, nullability);
2399+
return builder.getTempRefType(ht, nullability, exactness);
23992400
}
24002401
default:
24012402
throwError("unexpected type index: " + std::to_string(typeCode));
24022403
}
24032404
WASM_UNREACHABLE("unexpected type");
24042405
};
2405-
auto makeType = [&](int32_t typeCode) {
2406-
if (typeCode == BinaryConsts::EncodedType::exact) {
2407-
auto type = makeTypeNoExact(getS32LEB());
2408-
if (!type.isRef()) {
2409-
throwError("unexpected exact prefix on non-reference type");
2410-
}
2411-
return builder.getTempRefType(
2412-
type.getHeapType(), type.getNullability(), Exact);
2413-
}
2414-
return makeTypeNoExact(typeCode);
2415-
};
24162406
auto readType = [&]() { return makeType(getS32LEB()); };
24172407

24182408
auto readSignatureDef = [&]() {
@@ -2431,7 +2421,10 @@ void WasmBinaryReader::readTypes() {
24312421
};
24322422

24332423
auto readContinuationDef = [&]() {
2434-
HeapType ht = readHeapType();
2424+
auto [ht, exactness] = readHeapType();
2425+
if (exactness != Inexact) {
2426+
throw ParseException("invalid exact type in cont definition");
2427+
}
24352428
if (!ht.isSignature()) {
24362429
throw ParseException("cont types must be built from function types");
24372430
}
@@ -3046,8 +3039,12 @@ Result<> WasmBinaryReader::readInst() {
30463039
return builder.visitCatchAll();
30473040
case BinaryConsts::Delegate:
30483041
return builder.visitDelegate(getU32LEB());
3049-
case BinaryConsts::RefNull:
3050-
return builder.makeRefNull(getHeapType());
3042+
case BinaryConsts::RefNull: {
3043+
auto [heapType, exactness] = getHeapType();
3044+
// Exactness is allowed but doesn't matter, since we always use the bottom
3045+
// heap type.
3046+
return builder.makeRefNull(heapType);
3047+
}
30513048
case BinaryConsts::RefIsNull:
30523049
return builder.makeRefIsNull();
30533050
case BinaryConsts::RefFunc:
@@ -4282,34 +4279,36 @@ Result<> WasmBinaryReader::readInst() {
42824279
return builder.makeI31Get(true);
42834280
case BinaryConsts::I31GetU:
42844281
return builder.makeI31Get(false);
4285-
case BinaryConsts::RefTest:
4286-
return builder.makeRefTest(Type(getHeapType(), NonNullable));
4287-
case BinaryConsts::RefTestNull:
4288-
return builder.makeRefTest(Type(getHeapType(), Nullable));
4289-
case BinaryConsts::RefTestRT:
4290-
return builder.makeRefTest(getType());
4291-
case BinaryConsts::RefCast:
4292-
return builder.makeRefCast(Type(getHeapType(), NonNullable));
4293-
case BinaryConsts::RefCastNull:
4294-
return builder.makeRefCast(Type(getHeapType(), Nullable));
4295-
case BinaryConsts::RefCastRT:
4296-
return builder.makeRefCast(getType());
4282+
case BinaryConsts::RefTest: {
4283+
auto [heapType, exactness] = getHeapType();
4284+
return builder.makeRefTest(Type(heapType, NonNullable, exactness));
4285+
}
4286+
case BinaryConsts::RefTestNull: {
4287+
auto [heapType, exactness] = getHeapType();
4288+
return builder.makeRefTest(Type(heapType, Nullable, exactness));
4289+
}
4290+
case BinaryConsts::RefCast: {
4291+
auto [heapType, exactness] = getHeapType();
4292+
return builder.makeRefCast(Type(heapType, NonNullable, exactness));
4293+
}
4294+
case BinaryConsts::RefCastNull: {
4295+
auto [heapType, exactness] = getHeapType();
4296+
return builder.makeRefCast(Type(heapType, Nullable, exactness));
4297+
}
42974298
case BinaryConsts::BrOnCast:
42984299
case BinaryConsts::BrOnCastFail: {
42994300
auto flags = getInt8();
4300-
auto label = getU32LEB();
43014301
auto srcNull = (flags & BinaryConsts::BrOnCastFlag::InputNullable)
43024302
? Nullable
43034303
: NonNullable;
43044304
auto dstNull = (flags & BinaryConsts::BrOnCastFlag::OutputNullable)
43054305
? Nullable
43064306
: NonNullable;
4307-
auto srcExact =
4308-
(flags & BinaryConsts::BrOnCastFlag::InputExact) ? Exact : Inexact;
4309-
auto dstExact =
4310-
(flags & BinaryConsts::BrOnCastFlag::OutputExact) ? Exact : Inexact;
4311-
auto in = Type(getHeapType(), srcNull, srcExact);
4312-
auto cast = Type(getHeapType(), dstNull, dstExact);
4307+
auto label = getU32LEB();
4308+
auto [srcType, srcExact] = getHeapType();
4309+
auto [dstType, dstExact] = getHeapType();
4310+
auto in = Type(srcType, srcNull, srcExact);
4311+
auto cast = Type(dstType, dstNull, dstExact);
43134312
auto kind = op == BinaryConsts::BrOnCast ? BrOnCast : BrOnCastFail;
43144313
return builder.makeBrOn(label, kind, in, cast);
43154314
}

0 commit comments

Comments
 (0)