diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2022-08-29 12:48:46 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-29 12:48:46 -0700 |
commit | bd630d707253a9838a3d0306e4be680942ff0715 (patch) | |
tree | b51a786d3afd3ee97a78eb0d3923fb6ad59565d5 | |
parent | 8108ce28e66f1002932f6e5dc9dd4f23c8b8a9f3 (diff) | |
download | binaryen-bd630d707253a9838a3d0306e4be680942ff0715.tar.gz binaryen-bd630d707253a9838a3d0306e4be680942ff0715.tar.bz2 binaryen-bd630d707253a9838a3d0306e4be680942ff0715.zip |
Implement `extern.externalize` and `extern.internalize` (#4975)
These new GC instructions infallibly convert between `extern` and `any`
references now that those types are not in the same hierarchy.
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rwxr-xr-x | scripts/fuzz_opt.py | 4 | ||||
-rwxr-xr-x | scripts/gen-s-parser.py | 2 | ||||
-rw-r--r-- | src/gen-s-parser.inc | 58 | ||||
-rw-r--r-- | src/ir/effects.h | 4 | ||||
-rw-r--r-- | src/passes/Print.cpp | 6 | ||||
-rw-r--r-- | src/wasm-binary.h | 2 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 10 | ||||
-rw-r--r-- | src/wasm.h | 2 | ||||
-rw-r--r-- | src/wasm/wasm-binary.cpp | 11 | ||||
-rw-r--r-- | src/wasm/wasm-stack.cpp | 8 | ||||
-rw-r--r-- | src/wasm/wasm-validator.cpp | 35 | ||||
-rw-r--r-- | src/wasm/wasm.cpp | 6 | ||||
-rw-r--r-- | test/lit/extern-conversions.wast | 33 |
14 files changed, 171 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 049652cfe..7e005f6e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ Current Trunk - HeapType::ext has been restored but is no longer a subtype of HeapType::any to match the latest updates in the GC spec. (#4898) - `i31ref` and `dataref` are now nullable to match the latest GC spec. (#4843) +- Add support for `extern.externalize` and `extern.internalize`. (#4975) v109 ---- diff --git a/scripts/fuzz_opt.py b/scripts/fuzz_opt.py index 3ff1cb554..d287bee97 100755 --- a/scripts/fuzz_opt.py +++ b/scripts/fuzz_opt.py @@ -270,8 +270,10 @@ def init_important_initial_contents(): INITIAL_CONTENTS_IGNORE = [ # not all relaxed SIMD instructions are implemented in the interpreter 'relaxed-simd.wast', - # TODO fuzzer and interpreter support for strings + # TODO: fuzzer and interpreter support for strings 'strings.wast', + # TODO: fuzzer and interpreter support for extern conversions + 'extern-conversions.wast', # ignore DWARF because it is incompatible with multivalue atm 'zlib.wasm', 'cubescript.wasm', diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index bb56b76e3..93fc676e4 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -604,6 +604,8 @@ instructions = [ ("ref.as_func", "makeRefAs(s, RefAsFunc)"), ("ref.as_data", "makeRefAs(s, RefAsData)"), ("ref.as_i31", "makeRefAs(s, RefAsI31)"), + ("extern.internalize", "makeRefAs(s, ExternInternalize)"), + ("extern.externalize", "makeRefAs(s, ExternExternalize)"), ("string.new_wtf8", "makeStringNew(s, StringNewWTF8)"), ("string.new_wtf16", "makeStringNew(s, StringNewWTF16)"), ("string.new_wtf8_array", "makeStringNew(s, StringNewWTF8Array)"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index caf4f5578..4803f60b5 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -185,9 +185,25 @@ switch (op[0]) { default: goto parse_error; } } - case 'e': - if (strcmp(op, "else") == 0) { return makeThenOrElse(s); } - goto parse_error; + case 'e': { + switch (op[1]) { + case 'l': + if (strcmp(op, "else") == 0) { return makeThenOrElse(s); } + goto parse_error; + case 'x': { + switch (op[7]) { + case 'e': + if (strcmp(op, "extern.externalize") == 0) { return makeRefAs(s, ExternExternalize); } + goto parse_error; + case 'i': + if (strcmp(op, "extern.internalize") == 0) { return makeRefAs(s, ExternInternalize); } + goto parse_error; + default: goto parse_error; + } + } + default: goto parse_error; + } + } case 'f': { switch (op[1]) { case '3': { @@ -3816,13 +3832,37 @@ switch (op[0]) { default: goto parse_error; } } - case 'e': - if (op == "else"sv) { - auto ret = makeThenOrElse(ctx, in); - CHECK_ERR(ret); - return *ret; + case 'e': { + switch (op[1]) { + case 'l': + if (op == "else"sv) { + auto ret = makeThenOrElse(ctx, in); + CHECK_ERR(ret); + return *ret; + } + goto parse_error; + case 'x': { + switch (op[7]) { + case 'e': + if (op == "extern.externalize"sv) { + auto ret = makeRefAs(ctx, in, ExternExternalize); + CHECK_ERR(ret); + return *ret; + } + goto parse_error; + case 'i': + if (op == "extern.internalize"sv) { + auto ret = makeRefAs(ctx, in, ExternInternalize); + CHECK_ERR(ret); + return *ret; + } + goto parse_error; + default: goto parse_error; + } + } + default: goto parse_error; } - goto parse_error; + } case 'f': { switch (op[1]) { case '3': { diff --git a/src/ir/effects.h b/src/ir/effects.h index 5e50441ac..f5fdc5184 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -726,6 +726,10 @@ private: parent.implicitTrap = true; } void visitRefAs(RefAs* curr) { + if (curr->op == ExternInternalize || curr->op == ExternExternalize) { + // These conversions are infallible. + return; + } // traps when the arg is not valid parent.implicitTrap = true; // Note: We could be more precise here and report the lack of a possible diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 5fb17af76..dc247c737 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2214,6 +2214,12 @@ struct PrintExpressionContents case RefAsI31: printMedium(o, "ref.as_i31"); break; + case ExternInternalize: + printMedium(o, "extern.internalize"); + break; + case ExternExternalize: + printMedium(o, "extern.externalize"); + break; default: WASM_UNREACHABLE("invalid ref.is_*"); } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index e150915a0..562196580 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1125,6 +1125,8 @@ enum ASTNodes { BrOnNonFunc = 0x63, BrOnNonData = 0x64, BrOnNonI31 = 0x65, + ExternInternalize = 0x70, + ExternExternalize = 0x71, StringNewWTF8 = 0x80, StringNewWTF16 = 0x81, StringConst = 0x82, diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index fec560e35..56336b237 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -1874,6 +1874,9 @@ public: trap("not an i31"); } break; + case ExternInternalize: + case ExternExternalize: + WASM_UNREACHABLE("unimplemented extern conversion"); default: WASM_UNREACHABLE("unimplemented ref.as_*"); } @@ -2226,6 +2229,13 @@ public: Flow visitStringSliceIter(StringSliceIter* curr) { return Flow(NONCONSTANT_FLOW); } + Flow visitRefAs(RefAs* curr) { + // TODO: Remove this once interpretation is implemented. + if (curr->op == ExternInternalize || curr->op == ExternExternalize) { + return Flow(NONCONSTANT_FLOW); + } + return ExpressionRunner<SubType>::visitRefAs(curr); + } void trap(const char* why) override { throw NonconstantException(); } diff --git a/src/wasm.h b/src/wasm.h index 62a63e493..7b895a0cf 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -568,6 +568,8 @@ enum RefAsOp { RefAsFunc, RefAsData, RefAsI31, + ExternInternalize, + ExternExternalize, }; enum BrOnOp { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index df99fabb9..d449d4bdb 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -3978,7 +3978,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) { } if (opcode == BinaryConsts::RefAsFunc || opcode == BinaryConsts::RefAsData || - opcode == BinaryConsts::RefAsI31) { + opcode == BinaryConsts::RefAsI31 || + opcode == BinaryConsts::ExternInternalize || + opcode == BinaryConsts::ExternExternalize) { visitRefAs((curr = allocator.alloc<RefAs>())->cast<RefAs>(), opcode); break; } @@ -7347,6 +7349,12 @@ void WasmBinaryBuilder::visitRefAs(RefAs* curr, uint8_t code) { case BinaryConsts::RefAsI31: curr->op = RefAsI31; break; + case BinaryConsts::ExternInternalize: + curr->op = ExternInternalize; + break; + case BinaryConsts::ExternExternalize: + curr->op = ExternExternalize; + break; default: WASM_UNREACHABLE("invalid code for ref.as_*"); } @@ -7356,6 +7364,7 @@ void WasmBinaryBuilder::visitRefAs(RefAs* curr, uint8_t code) { } curr->finalize(); } + void WasmBinaryBuilder::throwError(std::string text) { throw ParseException(text, 0, pos); } diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 13bd09927..fddb7e41c 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -2170,6 +2170,14 @@ void BinaryInstWriter::visitRefAs(RefAs* curr) { case RefAsI31: o << int8_t(BinaryConsts::GCPrefix) << U32LEB(BinaryConsts::RefAsI31); break; + case ExternInternalize: + o << int8_t(BinaryConsts::GCPrefix) + << U32LEB(BinaryConsts::ExternInternalize); + break; + case ExternExternalize: + o << int8_t(BinaryConsts::GCPrefix) + << U32LEB(BinaryConsts::ExternExternalize); + break; default: WASM_UNREACHABLE("invalid ref.as_*"); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index 532529d2a..75cbcf53a 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -424,6 +424,7 @@ public: void visitMemoryGrow(MemoryGrow* curr); void visitRefNull(RefNull* curr); void visitRefIs(RefIs* curr); + void visitRefAs(RefAs* curr); void visitRefFunc(RefFunc* curr); void visitRefEq(RefEq* curr); void visitTableGet(TableGet* curr); @@ -2125,6 +2126,40 @@ void FunctionValidator::visitRefIs(RefIs* curr) { "ref.is_*'s argument should be a reference type"); } +void FunctionValidator::visitRefAs(RefAs* curr) { + switch (curr->op) { + default: + // TODO: validate all the other ref.as_* + break; + case ExternInternalize: { + shouldBeTrue(getModule()->features.hasGC(), + curr, + "extern.internalize requries GC to be enabled"); + if (curr->type == Type::unreachable) { + return; + } + shouldBeSubType(curr->value->type, + Type(HeapType::ext, Nullable), + curr->value, + "extern.internalize value should be an externref"); + break; + } + case ExternExternalize: { + shouldBeTrue(getModule()->features.hasGC(), + curr, + "extern.externalize requries GC to be enabled"); + if (curr->type == Type::unreachable) { + return; + } + shouldBeSubType(curr->value->type, + Type(HeapType::any, Nullable), + curr->value, + "extern.externalize value should be an anyref"); + break; + } + } +} + void FunctionValidator::visitRefFunc(RefFunc* curr) { // If we are not in a function, this is a global location like a table. We // allow RefFunc there as we represent tables that way regardless of what diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp index 8226e9079..56db8d7a9 100644 --- a/src/wasm/wasm.cpp +++ b/src/wasm/wasm.cpp @@ -1122,6 +1122,12 @@ void RefAs::finalize() { case RefAsI31: type = Type(HeapType::i31, NonNullable); break; + case ExternInternalize: + type = Type(HeapType::any, value->type.getNullability()); + break; + case ExternExternalize: + type = Type(HeapType::ext, value->type.getNullability()); + break; default: WASM_UNREACHABLE("invalid ref.as_*"); } diff --git a/test/lit/extern-conversions.wast b/test/lit/extern-conversions.wast new file mode 100644 index 000000000..403b558f6 --- /dev/null +++ b/test/lit/extern-conversions.wast @@ -0,0 +1,33 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. + +;; Check that extern conversion instructions are emitted properly in the binary format. + +;; RUN: wasm-opt %s -all --roundtrip -S -o - | filecheck %s + +(module + ;; CHECK: (type $ref|any|_=>_ref|extern| (func (param (ref any)) (result (ref extern)))) + + ;; CHECK: (type $externref_=>_anyref (func (param externref) (result anyref))) + + ;; CHECK: (func $extern.externalize (param $x (ref any)) (result (ref extern)) + ;; CHECK-NEXT: (extern.externalize + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $extern.externalize (param $x (ref any)) (result (ref extern)) + (extern.externalize + (local.get $x) + ) + ) + + ;; CHECK: (func $extern.internalize (param $x externref) (result anyref) + ;; CHECK-NEXT: (extern.internalize + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $extern.internalize (param $x (ref null extern)) (result (ref null any)) + (extern.internalize + (local.get $x) + ) + ) +) |