From 98623296a9f9ecf0b9906af77a897f89942b8781 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Thu, 27 Feb 2025 20:51:05 -0800 Subject: [PATCH 1/3] Add a custom descriptors feature Although the proposal is called "Custom RTTs," the more precise term for its main feature is "custom descriptors." To decrease long-term confusion at the expense of some possible short-term confusion, name the corresponding new feature "custom descriptors." This feature will guard the use of both descriptor/describes clauses and exact reference types. --- src/tools/tool-options.h | 2 ++ src/wasm-binary.h | 1 + src/wasm-features.h | 16 +++++++++++++--- src/wasm/wasm-binary.cpp | 2 ++ src/wasm/wasm.cpp | 9 +++++---- test/binaryen.js/kitchen-sink.js.txt | 2 +- test/example/c-api-kitchen-sink.txt | 2 +- test/lit/help/wasm-as.test | 6 ++++++ test/lit/help/wasm-ctor-eval.test | 6 ++++++ test/lit/help/wasm-dis.test | 6 ++++++ test/lit/help/wasm-emscripten-finalize.test | 6 ++++++ test/lit/help/wasm-merge.test | 6 ++++++ test/lit/help/wasm-metadce.test | 6 ++++++ test/lit/help/wasm-opt.test | 6 ++++++ test/lit/help/wasm-reduce.test | 6 ++++++ test/lit/help/wasm-split.test | 6 ++++++ test/lit/help/wasm2js.test | 6 ++++++ 17 files changed, 85 insertions(+), 9 deletions(-) diff --git a/src/tools/tool-options.h b/src/tools/tool-options.h index bff71bb4138..caecd50badc 100644 --- a/src/tools/tool-options.h +++ b/src/tools/tool-options.h @@ -106,6 +106,8 @@ struct ToolOptions : public Options { .addFeature(FeatureSet::StackSwitching, "stack switching") .addFeature(FeatureSet::SharedEverything, "shared-everything threads") .addFeature(FeatureSet::FP16, "float 16 operations") + .addFeature(FeatureSet::CustomDescriptors, + "custom descriptors and exact references") .add("--enable-typed-function-references", "", "Deprecated compatibility flag", diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 9765afd0cbe..2915db46864 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -398,6 +398,7 @@ extern const char* SharedEverythingFeature; extern const char* FP16Feature; extern const char* BulkMemoryOptFeature; extern const char* CallIndirectOverlongFeature; +extern const char* CustomDescriptorsFeature; enum Subsection { NameModule = 0, diff --git a/src/wasm-features.h b/src/wasm-features.h index 7ada02e9979..a7c3ce0c4f5 100644 --- a/src/wasm-features.h +++ b/src/wasm-features.h @@ -54,11 +54,12 @@ struct FeatureSet { // that we can automatically generate tool flags that set it, but otherwise // it does nothing. Binaryen always accepts LEB call-indirect encodings. CallIndirectOverlong = 1 << 20, + CustomDescriptors = 1 << 21, MVP = None, // Keep in sync with llvm default features: // https://github.com/llvm/llvm-project/blob/c7576cb89d6c95f03968076e902d3adfd1996577/clang/lib/Basic/Targets/WebAssembly.cpp#L150-L153 Default = SignExt | MutableGlobals, - All = (1 << 21) - 1, + All = (1 << 22) - 1, }; static std::string toString(Feature f) { @@ -105,9 +106,14 @@ struct FeatureSet { return "bulk-memory-opt"; case CallIndirectOverlong: return "call-indirect-overlong"; - default: - WASM_UNREACHABLE("unexpected feature"); + case CustomDescriptors: + return "custom-descriptors"; + case MVP: + case Default: + case All: + break; } + WASM_UNREACHABLE("unexpected feature"); } std::string toString() const { @@ -159,6 +165,9 @@ struct FeatureSet { assert(has || !hasBulkMemory()); return has; } + bool hasCustomDescriptors() const { + return (features & CustomDescriptors) != 0; + } bool hasAll() const { return (features & All) != 0; } void set(FeatureSet f, bool v = true) { @@ -184,6 +193,7 @@ struct FeatureSet { void setSharedEverything(bool v = true) { set(SharedEverything, v); } void setFP16(bool v = true) { set(FP16, v); } void setBulkMemoryOpt(bool v = true) { set(BulkMemoryOpt, v); } + void setCustomDescriptors(bool v = true) { set(CustomDescriptors, v); } void setMVP() { features = MVP; } void setAll() { features = All; } diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index f9d9225bbc7..7099df8f2fe 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1358,6 +1358,8 @@ void WasmBinaryWriter::writeFeaturesSection() { return BinaryConsts::CustomSections::BulkMemoryOptFeature; case FeatureSet::CallIndirectOverlong: return BinaryConsts::CustomSections::CallIndirectOverlongFeature; + case FeatureSet::CustomDescriptors: + return BinaryConsts::CustomSections::CustomDescriptorsFeature; case FeatureSet::None: case FeatureSet::Default: case FeatureSet::All: diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index c8a82311c30..d023109ce7c 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -28,8 +28,8 @@ Name RETURN_FLOW("*return:)*"); Name RETURN_CALL_FLOW("*return-call:)*"); Name NONCONSTANT_FLOW("*nonconstant:)*"); -namespace BinaryConsts { -namespace CustomSections { +namespace BinaryConsts::CustomSections { + const char* Name = "name"; const char* SourceMapUrl = "sourceMappingURL"; const char* Dylink = "dylink"; @@ -59,8 +59,9 @@ const char* SharedEverythingFeature = "shared-everything"; const char* FP16Feature = "fp16"; const char* BulkMemoryOptFeature = "bulk-memory-opt"; const char* CallIndirectOverlongFeature = "call-indirect-overlong"; -} // namespace CustomSections -} // namespace BinaryConsts +const char* CustomDescriptorsFeature = "custom-descriptors"; + +} // namespace BinaryConsts::CustomSections Name STACK_POINTER("__stack_pointer"); Name MODULE("module"); diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt index 16e19a6a0c9..711972ad48b 100644 --- a/test/binaryen.js/kitchen-sink.js.txt +++ b/test/binaryen.js/kitchen-sink.js.txt @@ -33,7 +33,7 @@ Features.RelaxedSIMD: 4096 Features.ExtendedConst: 8192 Features.Strings: 16384 Features.MultiMemory: 32768 -Features.All: 2097151 +Features.All: 4194303 InvalidId: 0 BlockId: 1 IfId: 2 diff --git a/test/example/c-api-kitchen-sink.txt b/test/example/c-api-kitchen-sink.txt index 2e319129e13..0f9ee08a0f7 100644 --- a/test/example/c-api-kitchen-sink.txt +++ b/test/example/c-api-kitchen-sink.txt @@ -47,7 +47,7 @@ BinaryenFeatureMemory64: 2048 BinaryenFeatureRelaxedSIMD: 4096 BinaryenFeatureExtendedConst: 8192 BinaryenFeatureStrings: 16384 -BinaryenFeatureAll: 2097151 +BinaryenFeatureAll: 4194303 (f32.neg (f32.const -33.61199951171875) ) diff --git a/test/lit/help/wasm-as.test b/test/lit/help/wasm-as.test index 63f4f03adf8..5b4d447dcc8 100644 --- a/test/lit/help/wasm-as.test +++ b/test/lit/help/wasm-as.test @@ -128,6 +128,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm-ctor-eval.test b/test/lit/help/wasm-ctor-eval.test index 2960ddb9ae0..65fb90a5a48 100644 --- a/test/lit/help/wasm-ctor-eval.test +++ b/test/lit/help/wasm-ctor-eval.test @@ -135,6 +135,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm-dis.test b/test/lit/help/wasm-dis.test index 1efe7ffd82b..5cde2253168 100644 --- a/test/lit/help/wasm-dis.test +++ b/test/lit/help/wasm-dis.test @@ -121,6 +121,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm-emscripten-finalize.test b/test/lit/help/wasm-emscripten-finalize.test index 1ed447b16b1..86ee95bb967 100644 --- a/test/lit/help/wasm-emscripten-finalize.test +++ b/test/lit/help/wasm-emscripten-finalize.test @@ -163,6 +163,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm-merge.test b/test/lit/help/wasm-merge.test index 0e3b5f8b166..efb04605e84 100644 --- a/test/lit/help/wasm-merge.test +++ b/test/lit/help/wasm-merge.test @@ -151,6 +151,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm-metadce.test b/test/lit/help/wasm-metadce.test index 5d01bc62c43..5f27422710a 100644 --- a/test/lit/help/wasm-metadce.test +++ b/test/lit/help/wasm-metadce.test @@ -775,6 +775,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and +;; CHECK-NEXT: exact references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and +;; CHECK-NEXT: exact references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test index cc80f52c037..37db459dbd6 100644 --- a/test/lit/help/wasm-opt.test +++ b/test/lit/help/wasm-opt.test @@ -787,6 +787,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and +;; CHECK-NEXT: exact references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and +;; CHECK-NEXT: exact references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm-reduce.test b/test/lit/help/wasm-reduce.test index 9cd47473841..1c342285896 100644 --- a/test/lit/help/wasm-reduce.test +++ b/test/lit/help/wasm-reduce.test @@ -160,6 +160,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm-split.test b/test/lit/help/wasm-split.test index 1e4267dc26f..ed770ec9a77 100644 --- a/test/lit/help/wasm-split.test +++ b/test/lit/help/wasm-split.test @@ -260,6 +260,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and exact +;; CHECK-NEXT: references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test index 6e9c7732a3a..ffc3e7b4841 100644 --- a/test/lit/help/wasm2js.test +++ b/test/lit/help/wasm2js.test @@ -738,6 +738,12 @@ ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-fp16 Disable float 16 operations ;; CHECK-NEXT: +;; CHECK-NEXT: --enable-custom-descriptors Enable custom descriptors and +;; CHECK-NEXT: exact references +;; CHECK-NEXT: +;; CHECK-NEXT: --disable-custom-descriptors Disable custom descriptors and +;; CHECK-NEXT: exact references +;; CHECK-NEXT: ;; CHECK-NEXT: --enable-typed-function-references Deprecated compatibility flag ;; CHECK-NEXT: ;; CHECK-NEXT: --disable-typed-function-references Deprecated compatibility flag From 82a699db12f0a46fcd7e556c180b1d27df332238 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 28 Feb 2025 13:31:32 -0800 Subject: [PATCH 2/3] update another test --- ...rip-target-features_roundtrip_print-features_all-features.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/passes/strip-target-features_roundtrip_print-features_all-features.txt b/test/passes/strip-target-features_roundtrip_print-features_all-features.txt index 2cd2573f10b..34976434e9d 100644 --- a/test/passes/strip-target-features_roundtrip_print-features_all-features.txt +++ b/test/passes/strip-target-features_roundtrip_print-features_all-features.txt @@ -19,6 +19,7 @@ --enable-fp16 --enable-bulk-memory-opt --enable-call-indirect-overlong +--enable-custom-descriptors (module (type $0 (func (result v128 externref))) (func $foo (type $0) (result v128 externref) From e11c72928240ed513fd2ae431860e3512b25d4ec Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 28 Feb 2025 18:03:43 -0800 Subject: [PATCH 3/3] Parsing and emitting of exact types Implement text and binary parsing of exact references types, including parsing of reference type shorthands with the exact prefix. Also implement binary writing of exact types. When writing exact types when custom descriptors are not enabled, generalize the types to their inexact versions. This is very similar to how we generalize function types when GC is not enabled, for instance. --- scripts/test/fuzzing.py | 4 +- src/parser/contexts.h | 18 +++++-- src/parser/parsers.h | 73 ++++++++++++++++++---------- src/wasm-binary.h | 2 + src/wasm/wasm-binary.cpp | 38 ++++++++++++++- test/lit/basic/exact-references.wast | 53 ++++++++++++++++++++ 6 files changed, 153 insertions(+), 35 deletions(-) create mode 100644 test/lit/basic/exact-references.wast diff --git a/scripts/test/fuzzing.py b/scripts/test/fuzzing.py index 270a9cf6b80..32b4f44f078 100644 --- a/scripts/test/fuzzing.py +++ b/scripts/test/fuzzing.py @@ -100,7 +100,9 @@ 'stack_switching_suspend.wast', 'stack_switching_resume.wast', 'stack_switching_resume_throw.wast', - 'stack_switching_switch.wast' + 'stack_switching_switch.wast', + # TODO: fuzzer support for exact references + 'exact-references.wast', ] diff --git a/src/parser/contexts.h b/src/parser/contexts.h index 55601b174c5..3b9cb21c3e0 100644 --- a/src/parser/contexts.h +++ b/src/parser/contexts.h @@ -127,7 +127,9 @@ struct NullTypeParserCtx { TypeT makeF64() { return Ok{}; } TypeT makeV128() { return Ok{}; } - TypeT makeRefType(HeapTypeT, Nullability) { return Ok{}; } + TypeT makeRefType(HeapTypeT, Nullability, Exactness) { return Ok{}; } + + HeapTypeT getHeapTypeFromRefType(TypeT) { return Ok{}; } TupleElemListT makeTupleElemList() { return Ok{}; } void appendTupleElem(TupleElemListT&, TypeT) {} @@ -257,10 +259,13 @@ template struct TypeParserCtx { TypeT makeF64() { return Type::f64; } TypeT makeV128() { return Type::v128; } - TypeT makeRefType(HeapTypeT ht, Nullability nullability) { - return Type(ht, nullability); + TypeT + makeRefType(HeapTypeT ht, Nullability nullability, Exactness exactness) { + return Type(ht, nullability, exactness); } + HeapTypeT getHeapTypeFromRefType(TypeT t) { return t.getHeapType(); } + std::vector makeTupleElemList() { return {}; } void appendTupleElem(std::vector& elems, Type elem) { elems.push_back(elem); @@ -1115,10 +1120,13 @@ struct ParseTypeDefsCtx : TypeParserCtx { : TypeParserCtx(typeIndices), in(in), builder(builder), names(builder.size()) {} - TypeT makeRefType(HeapTypeT ht, Nullability nullability) { - return builder.getTempRefType(ht, nullability); + TypeT + makeRefType(HeapTypeT ht, Nullability nullability, Exactness exactness) { + return builder.getTempRefType(ht, nullability, exactness); } + HeapTypeT getHeapTypeFromRefType(TypeT t) { return t.getHeapType(); } + TypeT makeTupleType(const std::vector types) { return builder.getTempTupleType(types); } diff --git a/src/parser/parsers.h b/src/parser/parsers.h index 4ba88fd8bf2..b54de9979c6 100644 --- a/src/parser/parsers.h +++ b/src/parser/parsers.h @@ -30,6 +30,8 @@ using namespace std::string_view_literals; template Result absheaptype(Ctx&, Shareability); template Result heaptype(Ctx&); +template +MaybeResult maybeReftypeAbbrev(Ctx&); template MaybeResult maybeReftype(Ctx&); template Result reftype(Ctx&); template MaybeResult tupletype(Ctx&); @@ -456,68 +458,85 @@ template Result heaptype(Ctx& ctx) { // | 'i31ref' => i31ref // | 'structref' => structref // | 'arrayref' => arrayref -// | '(' ref null? t:heaptype ')' => ref null? t -template MaybeResult maybeReftype(Ctx& ctx) { +// | ... +template +MaybeResult maybeReftypeAbbrev(Ctx& ctx) { if (ctx.in.takeKeyword("funcref"sv)) { - return ctx.makeRefType(ctx.makeFuncType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeFuncType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("externref"sv)) { - return ctx.makeRefType(ctx.makeExternType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeExternType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("anyref"sv)) { - return ctx.makeRefType(ctx.makeAnyType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeAnyType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("eqref"sv)) { - return ctx.makeRefType(ctx.makeEqType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeEqType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("i31ref"sv)) { - return ctx.makeRefType(ctx.makeI31Type(Unshared), Nullable); + return ctx.makeRefType(ctx.makeI31Type(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("structref"sv)) { - return ctx.makeRefType(ctx.makeStructType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeStructType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("arrayref"sv)) { - return ctx.makeRefType(ctx.makeArrayType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeArrayType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("exnref"sv)) { - return ctx.makeRefType(ctx.makeExnType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeExnType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("stringref"sv)) { - return ctx.makeRefType(ctx.makeStringType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeStringType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("contref"sv)) { - return ctx.makeRefType(ctx.makeContType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeContType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullref"sv)) { - return ctx.makeRefType(ctx.makeNoneType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNoneType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullexternref"sv)) { - return ctx.makeRefType(ctx.makeNoextType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNoextType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullfuncref"sv)) { - return ctx.makeRefType(ctx.makeNofuncType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNofuncType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullexnref"sv)) { - return ctx.makeRefType(ctx.makeNoexnType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNoexnType(Unshared), Nullable, Inexact); } if (ctx.in.takeKeyword("nullcontref"sv)) { - return ctx.makeRefType(ctx.makeNocontType(Unshared), Nullable); + return ctx.makeRefType(ctx.makeNocontType(Unshared), Nullable, Inexact); } + return {}; +} - if (!ctx.in.takeSExprStart("ref"sv)) { - return {}; +// reftype ::= ... +// | '(' 'exact' (ref null ht):shorthand ')' => ref null exact ht +// | '(' ref null? exact? ht:heaptype ')' => ref null? t +template MaybeResult maybeReftype(Ctx& ctx) { + if (ctx.in.takeSExprStart("exact"sv)) { + auto rt = maybeReftypeAbbrev(ctx); + CHECK_ERR(rt); + if (!rt) { + return ctx.in.err("expected reftype shorthand"); + } + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of reftype"); + } + return ctx.makeRefType(ctx.getHeapTypeFromRefType(*rt), Nullable, Exact); } - auto nullability = ctx.in.takeKeyword("null"sv) ? Nullable : NonNullable; - - auto type = heaptype(ctx); - CHECK_ERR(type); - - if (!ctx.in.takeRParen()) { - return ctx.in.err("expected end of reftype"); + if (ctx.in.takeSExprStart("ref"sv)) { + auto nullability = ctx.in.takeKeyword("null"sv) ? Nullable : NonNullable; + auto exactness = ctx.in.takeKeyword("exact"sv) ? Exact : Inexact; + auto type = heaptype(ctx); + CHECK_ERR(type); + if (!ctx.in.takeRParen()) { + return ctx.in.err("expected end of reftype"); + } + return ctx.makeRefType(*type, nullability, exactness); } - return ctx.makeRefType(*type, nullability); + return maybeReftypeAbbrev(ctx); } template Result reftype(Ctx& ctx) { diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 2915db46864..bf5d9708583 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -327,6 +327,7 @@ enum EncodedType { eqref = -0x13, // 0x6d nonnullable = -0x1c, // 0x64 nullable = -0x1d, // 0x63 + exact = -0x1e, // 0x62 contref = -0x18, // 0x68 nullcontref = -0x0b, // 0x75 // exception handling @@ -1497,6 +1498,7 @@ class WasmBinaryReader { Type getType(); // Get a type given the initial S32LEB has already been read, and is provided. Type getType(int code); + Type getTypeNoExact(int code); HeapType getHeapType(); HeapType getIndexedHeapType(); diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index 7099df8f2fe..f7abd4908f3 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -1551,6 +1551,12 @@ void WasmBinaryWriter::writeInlineBuffer(const char* data, size_t size) { void WasmBinaryWriter::writeType(Type type) { if (type.isRef()) { + // Exact references are introduced by the custom descriptors feature, but + // can be used internally even when it is not enabled. In that case, we have + // to generalize the types to be inexact before writing them. + if (!wasm->features.hasCustomDescriptors()) { + type = Type(type.getHeapType(), type.getNullability(), Inexact); + } // The only reference types allowed without GC are funcref, externref, and // exnref. We internally use more refined versions of those types, but we // cannot emit those without GC. @@ -1567,6 +1573,12 @@ void WasmBinaryWriter::writeType(Type type) { type = Type(type.getHeapType().getTop(), Nullable); } } + // If the type is exact, emit the exact prefix and continue on without + // considering exactness. + if (type.isExact()) { + o << S32LEB(BinaryConsts::EncodedType::exact); + type = Type(type.getHeapType(), type.getNullability(), Inexact); + } auto heapType = type.getHeapType(); if (type.isNullable() && heapType.isBasic() && !heapType.isShared()) { switch (heapType.getBasic(Unshared)) { @@ -2155,7 +2167,7 @@ Signature WasmBinaryReader::getBlockType() { return Signature(Type::none, getType(code)); } -Type WasmBinaryReader::getType(int code) { +Type WasmBinaryReader::getTypeNoExact(int code) { Type type; if (getBasicType(code, type)) { return type; @@ -2171,6 +2183,17 @@ Type WasmBinaryReader::getType(int code) { WASM_UNREACHABLE("unexpected type"); } +Type WasmBinaryReader::getType(int code) { + if (code == BinaryConsts::EncodedType::exact) { + auto type = getTypeNoExact(getS32LEB()); + if (!type.isRef()) { + throwError("invalid exact prefix on non-reference type"); + } + return Type(type.getHeapType(), type.getNullability(), Exact); + } + return getTypeNoExact(code); +} + Type WasmBinaryReader::getType() { return getType(getS32LEB()); } HeapType WasmBinaryReader::getHeapType() { @@ -2331,7 +2354,7 @@ void WasmBinaryReader::readTypes() { } return builder.getTempHeapType(size_t(htCode)); }; - auto makeType = [&](int32_t typeCode) { + auto makeTypeNoExact = [&](int32_t typeCode) { Type type; if (getBasicType(typeCode, type)) { return type; @@ -2356,6 +2379,17 @@ void WasmBinaryReader::readTypes() { } WASM_UNREACHABLE("unexpected type"); }; + auto makeType = [&](int32_t typeCode) { + if (typeCode == BinaryConsts::EncodedType::exact) { + auto type = makeTypeNoExact(getS32LEB()); + if (!type.isRef()) { + throwError("unexpected exact prefix on non-reference type"); + } + return builder.getTempRefType( + type.getHeapType(), type.getNullability(), Exact); + } + return makeTypeNoExact(typeCode); + }; auto readType = [&]() { return makeType(getS32LEB()); }; auto readSignatureDef = [&]() { diff --git a/test/lit/basic/exact-references.wast b/test/lit/basic/exact-references.wast new file mode 100644 index 00000000000..73130afbd33 --- /dev/null +++ b/test/lit/basic/exact-references.wast @@ -0,0 +1,53 @@ +;; 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 + +;; Also check that if we emit a binary without custom descriptors enabled, the +;; types are generalized to be inexact. + +;; RUN: wasm-opt %s -all --disable-custom-descriptors -g -o - | wasm-opt -all -S -o - \ +;; RUN: | filecheck %s --check-prefix=NO-EXACT + +(module + ;; CHECK-TEXT: (type $foo (struct (field (exact anyref)) (field (ref exact any)) (field (ref null exact $foo)) (field (ref exact $foo)))) + ;; CHECK-BIN: (type $foo (struct (field (exact anyref)) (field (ref exact any)) (field (ref null exact $foo)) (field (ref exact $foo)))) + ;; NO-EXACT: (type $foo (struct (field anyref) (field (ref any)) (field (ref null $foo)) (field (ref $foo)))) + (type $foo (struct (field (exact anyref) (ref exact any) (ref null exact $foo) (ref exact $foo)))) + + + ;; CHECK-TEXT: (import "" "g1" (global $g1 (exact anyref))) + ;; CHECK-BIN: (import "" "g1" (global $g1 (exact anyref))) + ;; NO-EXACT: (import "" "g1" (global $g1 anyref)) + (import "" "g1" (global $g1 (exact anyref))) + + ;; CHECK-TEXT: (import "" "g2" (global $g2 (ref exact any))) + ;; CHECK-BIN: (import "" "g2" (global $g2 (ref exact any))) + ;; NO-EXACT: (import "" "g2" (global $g2 (ref any))) + (import "" "g2" (global $g2 (ref exact any))) + + ;; CHECK-TEXT: (import "" "g3" (global $g3 (ref null exact $foo))) + ;; CHECK-BIN: (import "" "g3" (global $g3 (ref null exact $foo))) + ;; NO-EXACT: (import "" "g3" (global $g3 (ref null $foo))) + (import "" "g3" (global $g3 (ref null exact $foo))) + + ;; CHECK-TEXT: (import "" "g4" (global $g4 (ref exact $foo))) + ;; CHECK-BIN: (import "" "g4" (global $g4 (ref exact $foo))) + ;; NO-EXACT: (import "" "g4" (global $g4 (ref $foo))) + (import "" "g4" (global $g4 (ref exact $foo))) +) +;; CHECK-BIN-NODEBUG: (type $0 (struct (field (exact anyref)) (field (ref exact any)) (field (ref null exact $0)) (field (ref exact $0)))) + +;; CHECK-BIN-NODEBUG: (import "" "g1" (global $gimport$0 (exact anyref))) + +;; CHECK-BIN-NODEBUG: (import "" "g2" (global $gimport$1 (ref exact any))) + +;; CHECK-BIN-NODEBUG: (import "" "g3" (global $gimport$2 (ref null exact $0))) + +;; CHECK-BIN-NODEBUG: (import "" "g4" (global $gimport$3 (ref exact $0)))