diff --git a/scripts/gen-s-parser.py b/scripts/gen-s-parser.py index 3012c7dd7ab..39c56241fbf 100755 --- a/scripts/gen-s-parser.py +++ b/scripts/gen-s-parser.py @@ -686,6 +686,7 @@ ("string.new_lossy_utf8_array", "makeStringNew(StringNewLossyUTF8Array)"), ("string.new_wtf16_array", "makeStringNew(StringNewWTF16Array)"), ("string.from_code_point", "makeStringNew(StringNewFromCodePoint)"), + ("string.from_char_code", "makeStringNew(StringNewFromCharCode)"), ("string.const", "makeStringConst()"), ("string.measure_utf8", "makeStringMeasure(StringMeasureUTF8)"), ("string.measure_wtf16", "makeStringMeasure(StringMeasureWTF16)"), diff --git a/src/gen-s-parser.inc b/src/gen-s-parser.inc index 7d7e5887b2c..990a075bb90 100644 --- a/src/gen-s-parser.inc +++ b/src/gen-s-parser.inc @@ -5202,12 +5202,23 @@ switch (buf[0]) { default: goto parse_error; } } - case 'f': - if (op == "string.from_code_point"sv) { - CHECK_ERR(makeStringNew(ctx, pos, annotations, StringNewFromCodePoint)); - return Ok{}; + case 'f': { + switch (buf[13]) { + case 'h': + if (op == "string.from_char_code"sv) { + CHECK_ERR(makeStringNew(ctx, pos, annotations, StringNewFromCharCode)); + return Ok{}; + } + goto parse_error; + case 'o': + if (op == "string.from_code_point"sv) { + CHECK_ERR(makeStringNew(ctx, pos, annotations, StringNewFromCodePoint)); + return Ok{}; + } + goto parse_error; + default: goto parse_error; } - goto parse_error; + } case 'm': { switch (buf[15]) { case 'u': diff --git a/src/ir/child-typer.h b/src/ir/child-typer.h index 1e121cedce2..ee40698a0e1 100644 --- a/src/ir/child-typer.h +++ b/src/ir/child-typer.h @@ -1306,6 +1306,7 @@ template struct ChildTyper : OverriddenVisitor { return; } case StringNewFromCodePoint: + case StringNewFromCharCode: note(&curr->ref, Type::i32); return; } diff --git a/src/ir/effects.h b/src/ir/effects.h index bd61f3c942b..874ec7b7e6e 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -1252,6 +1252,9 @@ class EffectAnalyzer { // Invalid code points. parent.implicitTrap = true; return; + case StringNewFromCharCode: + // Invalid code points are ok. + return; } } void visitStringConst(StringConst* curr) {} diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 2140e46adb0..b63aa44a2c4 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -2582,6 +2582,9 @@ struct PrintExpressionContents case StringNewFromCodePoint: printMedium(o, "string.from_code_point"); break; + case StringNewFromCharCode: + printMedium(o, "string.from_char_code"); + break; default: WASM_UNREACHABLE("invalid string.new*"); } diff --git a/src/passes/StringLifting.cpp b/src/passes/StringLifting.cpp index 2c8572c025a..d583de90133 100644 --- a/src/passes/StringLifting.cpp +++ b/src/passes/StringLifting.cpp @@ -49,6 +49,8 @@ struct StringLifting : public Pass { Name lengthImport; Name charCodeAtImport; Name substringImport; + Name fromCharCodeImport; + Name castImport; void run(Module* module) override { // Whether we found any work to do. @@ -195,6 +197,18 @@ struct StringLifting : public Pass { } substringImport = func->name; found = true; + } else if (func->base == "fromCharCode") { + if (type.getHeapType() != Signature(i32, refExtern)) { + Fatal() << "StringLifting: bad type for fromCharCode: " << type; + } + fromCharCodeImport = func->name; + found = true; + } else if (func->base == "cast") { + if (type.getHeapType() != Signature(externref, refExtern)) { + Fatal() << "StringLifting: bad type for cast: " << type; + } + castImport = func->name; + found = true; } else { std::cerr << "warning: unknown strings import: " << func->base << '\n'; } @@ -276,6 +290,20 @@ struct StringLifting : public Pass { .makeStringSliceWTF(curr->operands[0], curr->operands[1], curr->operands[2])); + } else if (curr->target == parent.fromCharCodeImport) { + replaceCurrent( + Builder(*getModule()) + .makeStringNew(StringNewFromCharCode, curr->operands[0])); + } else if (curr->target == parent.castImport) { + replaceCurrent(Builder(*getModule()) + .makeRefCast(curr->operands[0], + Type(HeapType::string, NonNullable))); + } + + // An expression that previously had type externref was replaced by a + // subtype, stringref; refinalize the IR. + if (getCurrent() != curr) { + modified = true; } } diff --git a/src/passes/StringLowering.cpp b/src/passes/StringLowering.cpp index ccb2f0366df..bc382fbfdf2 100644 --- a/src/passes/StringLowering.cpp +++ b/src/passes/StringLowering.cpp @@ -219,15 +219,15 @@ struct StringLowering : public StringGathering { // First, run the gathering operation so all string.consts are in one place. StringGathering::run(module); + // Replace string.* etc. operations with imported ones. + replaceInstructions(module); + // Remove all HeapType::string etc. in favor of externref. updateTypes(module); // Lower the string.const globals into imports. makeImports(module); - // Replace string.* etc. operations with imported ones. - replaceInstructions(module); - // ReFinalize to apply all the above changes. ReFinalize().run(getPassRunner(), module); @@ -360,6 +360,8 @@ struct StringLowering : public StringGathering { Name lengthImport; Name charCodeAtImport; Name substringImport; + Name fromCharCodeImport; + Name castImport; // Creates an imported string function, returning its name (which is equal to // the true name of the import, if there is no conflict). @@ -404,6 +406,10 @@ struct StringLowering : public StringGathering { // string.substring: string, start, end -> string substringImport = addImport(module, "substring", {nullExt, Type::i32, Type::i32}, nnExt); + // string.fromCharCode: charCode -> ext + fromCharCodeImport = addImport(module, "fromCharCode", Type::i32, nnExt); + // string.cast: externref -> string + castImport = addImport(module, "cast", nullExt, nnExt); // Replace the string instructions in parallel. struct Replacer : public WalkerPass> { @@ -417,6 +423,36 @@ struct StringLowering : public StringGathering { Replacer(StringLowering& lowering) : lowering(lowering) {} + void visitRefCast(RefCast* curr) { + // Ignore non-string RefCast. + if (!curr->type.isString()) { + return; + } + + Builder builder(*getModule()); + if (curr->type.isNonNullableRef()) { + replaceCurrent( + builder.makeCall(lowering.castImport, {curr->ref}, lowering.nnExt)); + return; + } + + // Casts cannot be found outside of a function (e.g. a global + // initializer) since ref.cast is not a constant expression. + assert(getFunction()); + // (if (ref.is_null (local.tee $temp (..))) + // (then (ref.null noextern)) + // (else (call $cast (local.get $temp))) + // ) + auto temp = Builder::addVar(getFunction(), lowering.nullExt); + replaceCurrent(builder.makeIf( + builder.makeRefIsNull( + builder.makeLocalTee(temp, curr->ref, lowering.nullExt)), + builder.makeRefNull(HeapType::ext), + builder.makeCall(lowering.castImport, + {builder.makeLocalGet(temp, lowering.nullExt)}, + lowering.nnExt))); + } + void visitStringNew(StringNew* curr) { Builder builder(*getModule()); switch (curr->op) { @@ -429,6 +465,10 @@ struct StringLowering : public StringGathering { replaceCurrent(builder.makeCall( lowering.fromCodePointImport, {curr->ref}, lowering.nnExt)); return; + case StringNewFromCharCode: + replaceCurrent(builder.makeCall( + lowering.fromCharCodeImport, {curr->ref}, lowering.nnExt)); + return; default: WASM_UNREACHABLE("TODO: all of string.new*"); } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 470f58be2e7..93cc987723d 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -1270,6 +1270,10 @@ enum ASTNodes { StringCompare = 0xa8, StringFromCodePoint = 0xa9, StringHash = 0xaa, + // Not in stringref specification. Corresponds to JS string builtin + // from_char_code. The stringref proposal is not active, so we don't need to + // worry about compatibility. + StringFromCharCode = 0xab, StringNewWTF16Array = 0xb1, StringEncodeWTF16Array = 0xb3, StringNewLossyUTF8Array = 0xb4, diff --git a/src/wasm-builder.h b/src/wasm-builder.h index fc8218f0a46..dc5c3fc4e14 100644 --- a/src/wasm-builder.h +++ b/src/wasm-builder.h @@ -1294,7 +1294,8 @@ class Builder { Expression* ref, Expression* start = nullptr, Expression* end = nullptr) { - assert((start && end) != (op == StringNewFromCodePoint)); + assert((start && end) != + (op == StringNewFromCodePoint || op == StringNewFromCharCode)); auto* ret = wasm.allocator.alloc(); ret->op = op; ret->ref = ref; diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index a1a976c8c85..7dad5c78ceb 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -2753,6 +2753,15 @@ class ExpressionRunner : public OverriddenVisitor { std::string str = wtf16.str(); return Literal(str); } + case StringNewFromCharCode: { + // Only output the last 16 bits, and unlike StringNewFromCodePoint, do + // not trap on invalid code points. + uint32_t charCode = ptr.getSingleValue().getUnsigned() & 0xFFFF; + std::stringstream wtf16; + String::writeWTF16CodePoint(wtf16, charCode); + std::string str = wtf16.str(); + return Literal(str); + } default: // TODO: others return Flow(NONCONSTANT_FLOW); diff --git a/src/wasm.h b/src/wasm.h index 657433874fd..94b034f124a 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -625,6 +625,7 @@ enum StringNewOp { StringNewLossyUTF8Array, StringNewWTF16Array, StringNewFromCodePoint, + StringNewFromCharCode, }; enum StringMeasureOp { diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp index aede8e67b0d..c407da9b46a 100644 --- a/src/wasm/wasm-binary.cpp +++ b/src/wasm/wasm-binary.cpp @@ -5149,6 +5149,8 @@ Result<> WasmBinaryReader::readInst() { return builder.makeStringNew(StringNewWTF16Array); case BinaryConsts::StringFromCodePoint: return builder.makeStringNew(StringNewFromCodePoint); + case BinaryConsts::StringFromCharCode: + return builder.makeStringNew(StringNewFromCharCode); case BinaryConsts::StringAsWTF16: // This turns into nothing because we do not represent stringviews in // the IR. diff --git a/src/wasm/wasm-ir-builder.cpp b/src/wasm/wasm-ir-builder.cpp index 5c2be29006f..93d14f39332 100644 --- a/src/wasm/wasm-ir-builder.cpp +++ b/src/wasm/wasm-ir-builder.cpp @@ -2805,7 +2805,7 @@ Result<> IRBuilder::makeRefAs(RefAsOp op) { Result<> IRBuilder::makeStringNew(StringNewOp op) { StringNew curr; curr.op = op; - if (op == StringNewFromCodePoint) { + if (op == StringNewFromCodePoint || op == StringNewFromCharCode) { CHECK_ERR(visitStringNew(&curr)); push(builder.makeStringNew(op, curr.ref)); return Ok{}; diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp index 9009a425c7d..5d198e53075 100644 --- a/src/wasm/wasm-stack.cpp +++ b/src/wasm/wasm-stack.cpp @@ -3006,6 +3006,9 @@ void BinaryInstWriter::visitStringNew(StringNew* curr) { case StringNewFromCodePoint: o << U32LEB(BinaryConsts::StringFromCodePoint); break; + case StringNewFromCharCode: + o << U32LEB(BinaryConsts::StringFromCharCode); + break; default: WASM_UNREACHABLE("invalid string.new*"); } diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp index a8255e931e8..f05f2ec96b8 100644 --- a/src/wasm/wasm-validator.cpp +++ b/src/wasm/wasm-validator.cpp @@ -3247,8 +3247,15 @@ void FunctionValidator::visitRefTest(RefTest* curr) { } void FunctionValidator::visitRefCast(RefCast* curr) { - shouldBeTrue( - getModule()->features.hasGC(), curr, "ref.cast requires gc [--enable-gc]"); + // Allows RefCast to strings when at least strings are enabled and not GC. + // Otherwise, RefCast requires GC to be enabled. + bool isStringCast = (curr->type.isString()) || + (curr->type == Type::unreachable) || + (curr->type.getHeapType().isMaybeShared(HeapType::noext)); + shouldBeTrue(getModule()->features.hasGC() || + (isStringCast && getModule()->features.hasStrings()), + curr, + "ref.cast requires gc [--enable-gc]"); // Require descriptors to be valid even if the ref is unreachable. if (curr->desc && curr->desc->type != Type::unreachable) { @@ -4368,6 +4375,17 @@ void FunctionValidator::visitStringNew(StringNew* curr) { shouldBeTrue( !curr->end, curr, "string.from_code_point should not have end"); return; + case StringNewFromCharCode: + shouldBeEqualOrFirstIsUnreachable( + curr->ref->type, + Type(Type::i32), + curr, + "string.from_char_code char code must be i32"); + shouldBeTrue( + !curr->start, curr, "string.from_char_code should not have start"); + shouldBeTrue( + !curr->end, curr, "string.from_char_code should not have end"); + return; } WASM_UNREACHABLE("unexpected op"); } diff --git a/test/lit/exec/strings.wast b/test/lit/exec/strings.wast index 4a149a587a3..0de5c8965a4 100644 --- a/test/lit/exec/strings.wast +++ b/test/lit/exec/strings.wast @@ -469,6 +469,30 @@ ) ) + ;; CHECK: [fuzz-exec] export from_char_code + ;; CHECK-NEXT: [fuzz-exec] note result: from_char_code => string("A") + (func $from_char_code (export "from_char_code") (result stringref) + (string.from_char_code + (i32.const 65) + ) + ) + + ;; CHECK: [fuzz-exec] export from_char_code.no_trap + ;; CHECK-NEXT: [fuzz-exec] note result: from_char_code.no_trap => string("\uffff") + (func $from_char_code.no_trap (export "from_char_code.no_trap") (result stringref) + (string.from_char_code + (i32.const -1) + ) + ) + + ;; CHECK: [fuzz-exec] export from_char_code.upper_bits + ;; CHECK-NEXT: [fuzz-exec] note result: from_char_code.upper_bits => string("A") + (func $from_char_code.upper_bits (export "from_char_code.upper_bits") (result stringref) + (string.from_char_code + (i32.const 0x10041) + ) + ) + ;; CHECK: [fuzz-exec] export string.measure ;; CHECK-NEXT: [fuzz-exec] note result: string.measure => 5 (func $string.measure (export "string.measure") (result i32) @@ -620,6 +644,15 @@ ;; CHECK: [fuzz-exec] export invalid_code_point ;; CHECK-NEXT: [trap invalid code point] +;; CHECK: [fuzz-exec] export from_char_code +;; CHECK-NEXT: [fuzz-exec] note result: from_char_code => string("A") + +;; CHECK: [fuzz-exec] export from_char_code.no_trap +;; CHECK-NEXT: [fuzz-exec] note result: from_char_code.no_trap => string("\uffff") + +;; CHECK: [fuzz-exec] export from_char_code.upper_bits +;; CHECK-NEXT: [fuzz-exec] note result: from_char_code.upper_bits => string("A") + ;; CHECK: [fuzz-exec] export string.measure ;; CHECK-NEXT: [fuzz-exec] note result: string.measure => 5 @@ -648,6 +681,9 @@ ;; CHECK-NEXT: [fuzz-exec] comparing eq.3 ;; CHECK-NEXT: [fuzz-exec] comparing eq.4 ;; CHECK-NEXT: [fuzz-exec] comparing eq.5 +;; CHECK-NEXT: [fuzz-exec] comparing from_char_code +;; CHECK-NEXT: [fuzz-exec] comparing from_char_code.no_trap +;; CHECK-NEXT: [fuzz-exec] comparing from_char_code.upper_bits ;; CHECK-NEXT: [fuzz-exec] comparing get_codeunit ;; CHECK-NEXT: [fuzz-exec] comparing invalid_code_point ;; CHECK-NEXT: [fuzz-exec] comparing isolated_high_code_point diff --git a/test/lit/passes/string-gathering.wast b/test/lit/passes/string-gathering.wast index c8724ab27d2..951d6a0a3f3 100644 --- a/test/lit/passes/string-gathering.wast +++ b/test/lit/passes/string-gathering.wast @@ -29,13 +29,13 @@ ;; LOWER: (type $1 (func)) - ;; LOWER: (type $2 (func (param externref externref) (result i32))) + ;; LOWER: (type $2 (func (param i32) (result (ref extern)))) - ;; LOWER: (type $3 (func (param externref) (result i32))) + ;; LOWER: (type $3 (func (param externref externref) (result i32))) - ;; LOWER: (type $4 (func (param (ref null $0) i32 i32) (result (ref extern)))) + ;; LOWER: (type $4 (func (param externref) (result i32))) - ;; LOWER: (type $5 (func (param i32) (result (ref extern)))) + ;; LOWER: (type $5 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; LOWER: (type $6 (func (param externref externref) (result (ref extern)))) @@ -45,32 +45,38 @@ ;; LOWER: (type $9 (func (param externref i32 i32) (result (ref extern)))) + ;; LOWER: (type $10 (func (param externref) (result (ref extern)))) + ;; LOWER: (import "string.const" "0" (global $global (ref extern))) ;; LOWER: (import "string.const" "1" (global $"string.const_\"bar\"" (ref extern))) ;; LOWER: (import "string.const" "2" (global $"string.const_\"other\"" (ref extern))) - ;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $5) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $2) (param i32) (result (ref extern)))) ;; LOWER: (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern)))) ;; LOWER: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32))) - ;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $3) (param externref externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "test" (func $test (type $4) (param externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $3) (param externref externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "length" (func $length (type $4) (param externref) (result i32))) ;; LOWER: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32))) ;; LOWER: (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $2) (param i32) (result (ref extern)))) + + ;; LOWER: (import "wasm:js-string" "cast" (func $cast (type $10) (param externref) (result (ref extern)))) + ;; LOWER: (global $global2 externref (global.get $"string.const_\"bar\"")) (global $global2 (ref null string) (string.const "bar")) @@ -151,13 +157,13 @@ ;; CHECK: (import "a" "b" (global $import (ref string))) ;; LOWER: (type $0 (array (mut i16))) - ;; LOWER: (type $1 (func (param externref externref) (result i32))) + ;; LOWER: (type $1 (func (param i32) (result (ref extern)))) - ;; LOWER: (type $2 (func (param externref) (result i32))) + ;; LOWER: (type $2 (func (param externref externref) (result i32))) - ;; LOWER: (type $3 (func (param (ref null $0) i32 i32) (result (ref extern)))) + ;; LOWER: (type $3 (func (param externref) (result i32))) - ;; LOWER: (type $4 (func (param i32) (result (ref extern)))) + ;; LOWER: (type $4 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; LOWER: (type $5 (func (param externref externref) (result (ref extern)))) @@ -167,6 +173,8 @@ ;; LOWER: (type $8 (func (param externref i32 i32) (result (ref extern)))) + ;; LOWER: (type $9 (func (param externref) (result (ref extern)))) + ;; LOWER: (import "string.const" "0" (global $global1 (ref extern))) ;; LOWER: (import "string.const" "1" (global $global4 (ref extern))) @@ -180,26 +188,30 @@ ;; CHECK: (global $global4 (ref string) (string.const "bar")) ;; CHECK: (global $global2 (ref string) (global.get $global1)) - ;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $3) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $4) (param i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $1) (param i32) (result (ref extern)))) ;; LOWER: (import "wasm:js-string" "concat" (func $concat (type $5) (param externref externref) (result (ref extern)))) ;; LOWER: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $6) (param externref (ref null $0) i32) (result i32))) - ;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "test" (func $test (type $2) (param externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "length" (func $length (type $2) (param externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) ;; LOWER: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $7) (param externref i32) (result i32))) ;; LOWER: (import "wasm:js-string" "substring" (func $substring (type $8) (param externref i32 i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $1) (param i32) (result (ref extern)))) + + ;; LOWER: (import "wasm:js-string" "cast" (func $cast (type $9) (param externref) (result (ref extern)))) + ;; LOWER: (global $global2 (ref extern) (global.get $global1)) (global $global2 (ref string) (string.const "foo")) @@ -227,15 +239,15 @@ ;; CHECK: (global $global (mut (ref string)) (global.get $"string.const_\"foo\"")) ;; LOWER: (type $0 (array (mut i16))) - ;; LOWER: (type $1 (func (param externref externref) (result i32))) + ;; LOWER: (type $1 (func (param i32) (result (ref extern)))) - ;; LOWER: (type $2 (func (param externref) (result i32))) + ;; LOWER: (type $2 (func (param externref externref) (result i32))) - ;; LOWER: (type $3 (func)) + ;; LOWER: (type $3 (func (param externref) (result i32))) - ;; LOWER: (type $4 (func (param (ref null $0) i32 i32) (result (ref extern)))) + ;; LOWER: (type $4 (func)) - ;; LOWER: (type $5 (func (param i32) (result (ref extern)))) + ;; LOWER: (type $5 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; LOWER: (type $6 (func (param externref externref) (result (ref extern)))) @@ -245,28 +257,34 @@ ;; LOWER: (type $9 (func (param externref i32 i32) (result (ref extern)))) + ;; LOWER: (type $10 (func (param externref) (result (ref extern)))) + ;; LOWER: (import "string.const" "0" (global $"string.const_\"foo\"" (ref extern))) - ;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $5) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $1) (param i32) (result (ref extern)))) ;; LOWER: (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern)))) ;; LOWER: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32))) - ;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "test" (func $test (type $2) (param externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "length" (func $length (type $2) (param externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) ;; LOWER: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32))) ;; LOWER: (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $1) (param i32) (result (ref extern)))) + + ;; LOWER: (import "wasm:js-string" "cast" (func $cast (type $10) (param externref) (result (ref extern)))) + ;; LOWER: (global $global (mut (ref extern)) (global.get $"string.const_\"foo\"")) (global $global (mut (ref string)) (string.const "foo")) @@ -275,7 +293,7 @@ ;; CHECK-NEXT: (global.get $"string.const_\"foo\"") ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) - ;; LOWER: (func $a (type $3) + ;; LOWER: (func $a (type $4) ;; LOWER-NEXT: (drop ;; LOWER-NEXT: (global.get $"string.const_\"foo\"") ;; LOWER-NEXT: ) @@ -304,13 +322,13 @@ ;; CHECK: (global $struct (ref $struct) (struct.new $struct ;; CHECK-NEXT: (global.get $string) ;; CHECK-NEXT: )) - ;; LOWER: (type $2 (func (param externref externref) (result i32))) + ;; LOWER: (type $2 (func (param i32) (result (ref extern)))) - ;; LOWER: (type $3 (func (param externref) (result i32))) + ;; LOWER: (type $3 (func (param externref externref) (result i32))) - ;; LOWER: (type $4 (func (param (ref null $0) i32 i32) (result (ref extern)))) + ;; LOWER: (type $4 (func (param externref) (result i32))) - ;; LOWER: (type $5 (func (param i32) (result (ref extern)))) + ;; LOWER: (type $5 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; LOWER: (type $6 (func (param externref externref) (result (ref extern)))) @@ -320,28 +338,34 @@ ;; LOWER: (type $9 (func (param externref i32 i32) (result (ref extern)))) + ;; LOWER: (type $10 (func (param externref) (result (ref extern)))) + ;; LOWER: (import "string.const" "0" (global $string (ref extern))) - ;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $5) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $2) (param i32) (result (ref extern)))) ;; LOWER: (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern)))) ;; LOWER: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32))) - ;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $3) (param externref externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "test" (func $test (type $4) (param externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $3) (param externref externref) (result i32))) - ;; LOWER: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) + ;; LOWER: (import "wasm:js-string" "length" (func $length (type $4) (param externref) (result i32))) ;; LOWER: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32))) ;; LOWER: (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern)))) + ;; LOWER: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $2) (param i32) (result (ref extern)))) + + ;; LOWER: (import "wasm:js-string" "cast" (func $cast (type $10) (param externref) (result (ref extern)))) + ;; LOWER: (global $struct (ref $struct) (struct.new $struct ;; LOWER-NEXT: (global.get $string) ;; LOWER-NEXT: )) diff --git a/test/lit/passes/string-lifting-lowering-roundtrip.wast b/test/lit/passes/string-lifting-lowering-roundtrip.wast new file mode 100644 index 00000000000..a3a95162930 --- /dev/null +++ b/test/lit/passes/string-lifting-lowering-roundtrip.wast @@ -0,0 +1,90 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: wasm-opt %s -all --string-lifting --string-lowering -S -o - | filecheck %s + +;; TODO: Lowering re-adds all string imports, leading to duplicate imports when roundtripping. + +(module + ;; CHECK: (type $0 (func (param i32) (result (ref extern)))) + + ;; CHECK: (type $1 (array (mut i16))) + + ;; CHECK: (type $2 (func (param externref) (result (ref extern)))) + + ;; CHECK: (type $3 (func (param externref externref) (result i32))) + + ;; CHECK: (type $4 (func (param externref) (result i32))) + + ;; CHECK: (rec + ;; CHECK-NEXT: (type $5 (func (param externref))) + + ;; CHECK: (type $6 (func (param i32))) + + ;; CHECK: (type $7 (func (param (ref null $1) i32 i32) (result (ref extern)))) + + ;; CHECK: (type $8 (func (param externref externref) (result (ref extern)))) + + ;; CHECK: (type $9 (func (param externref (ref null $1) i32) (result i32))) + + ;; CHECK: (type $10 (func (param externref i32) (result i32))) + + ;; CHECK: (type $11 (func (param externref i32 i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $0) (param i32) (result (ref extern)))) + (import "wasm:js-string" "fromCharCode" (func $fromCharCode (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (type $2) (param externref) (result (ref extern)))) + (import "wasm:js-string" "cast" (func $cast (param externref) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $7) (param (ref null $1) i32 i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $0) (param i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $8) (param externref externref) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $9) (param externref (ref null $1) i32) (result i32))) + + ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $3) (param externref externref) (result i32))) + + ;; CHECK: (import "wasm:js-string" "test" (func $test (type $4) (param externref) (result i32))) + + ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $3) (param externref externref) (result i32))) + + ;; CHECK: (import "wasm:js-string" "length" (func $length (type $4) (param externref) (result i32))) + + ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $10) (param externref i32) (result i32))) + + ;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $11) (param externref i32 i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode_14 (type $0) (param i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "cast" (func $cast_15 (type $2) (param externref) (result (ref extern)))) + + ;; CHECK: (func $from_char_code (type $6) (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $fromCharCode_14 + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $from_char_code (param $x i32) + (drop + (call $fromCharCode + (local.get $x) + ) + ) + ) + + ;; CHECK: (func $cast_test (type $5) (param $x externref) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (call $cast_15 + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast_test (param $x externref) + (drop + (call $cast + (local.get $x) + ) + ) + ) +) diff --git a/test/lit/passes/string-lifting-no-gc.wast b/test/lit/passes/string-lifting-no-gc.wast new file mode 100644 index 00000000000..4483ee742bb --- /dev/null +++ b/test/lit/passes/string-lifting-no-gc.wast @@ -0,0 +1,43 @@ +;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. +;; RUN: wasm-opt %s --enable-reference-types --string-lifting -S -o - | filecheck %s + +(module + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (param externref) (result (ref extern)))) + (import "wasm:js-string" "cast" (func $cast (param externref) (result (ref extern)))) + + ;; CHECK: (func $cast_test (param $x externref) (result externref) + ;; CHECK-NEXT: (ref.cast (ref string) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast_test (param $x externref) (result externref) + (call $cast + (local.get $x) + ) + ) + + ;; CHECK: (func $cast_null (result externref) + ;; CHECK-NEXT: (ref.cast (ref noextern) + ;; CHECK-NEXT: (ref.null noextern) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast_null (result externref) + (call $cast + (ref.null noextern) + ) + ) + + ;; CHECK: (func $cast_unreachable (result externref) + ;; CHECK-NEXT: (block ;; (replaces unreachable RefCast we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast_unreachable (result externref) + (call $cast + (unreachable) + ) + ) +) diff --git a/test/lit/passes/string-lifting-section.wast b/test/lit/passes/string-lifting-section.wast index 5406494432a..669bb4a1b5d 100644 --- a/test/lit/passes/string-lifting-section.wast +++ b/test/lit/passes/string-lifting-section.wast @@ -9,13 +9,13 @@ ;; CHECK: (type $1 (func)) - ;; CHECK: (type $2 (func (param externref externref) (result i32))) + ;; CHECK: (type $2 (func (param i32) (result (ref extern)))) - ;; CHECK: (type $3 (func (param externref) (result i32))) + ;; CHECK: (type $3 (func (param externref externref) (result i32))) - ;; CHECK: (type $4 (func (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (type $4 (func (param externref) (result i32))) - ;; CHECK: (type $5 (func (param i32) (result (ref extern)))) + ;; CHECK: (type $5 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; CHECK: (type $6 (func (param externref externref) (result (ref extern)))) @@ -25,6 +25,8 @@ ;; CHECK: (type $9 (func (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (type $10 (func (param externref) (result (ref extern)))) + ;; CHECK: (import "string.const" "0" (global $"string.const_\"bar\"" (ref extern))) ;; CHECK: (import "string.const" "1" (global $"string.const_\"foo\"" (ref extern))) @@ -39,26 +41,30 @@ ;; CHECK: (import "string.const" "6" (global $"string.const_\"z\\\\\"" (ref extern))) - ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $5) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $2) (param i32) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32))) - ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $3) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "test" (func $test (type $4) (param externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $3) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "length" (func $length (type $4) (param externref) (result i32))) ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32))) ;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $2) (param i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (type $10) (param externref) (result (ref extern)))) + ;; CHECK: (func $consts (type $1) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (string.const "foo") diff --git a/test/lit/passes/string-lifting.wast b/test/lit/passes/string-lifting.wast index da537c59e98..b67125f429d 100644 --- a/test/lit/passes/string-lifting.wast +++ b/test/lit/passes/string-lifting.wast @@ -12,40 +12,46 @@ ;; CHECK: (type $3 (func (param externref i32 i32) (result (ref extern)))) - ;; CHECK: (type $4 (func (param externref externref) (result (ref extern)))) + ;; CHECK: (type $4 (func (param i32) (result (ref extern)))) - ;; CHECK: (type $5 (func (param (ref null $array16) i32 i32) (result (ref extern)))) + ;; CHECK: (type $5 (func (param externref externref) (result (ref extern)))) - ;; CHECK: (type $6 (func (param i32) (result (ref extern)))) + ;; CHECK: (type $6 (func (param externref) (result externref))) - ;; CHECK: (type $7 (func (param externref (ref null $array16) i32) (result i32))) + ;; CHECK: (type $7 (func (param (ref null $array16) i32 i32) (result (ref extern)))) - ;; CHECK: (type $8 (func (param externref i32) (result i32))) + ;; CHECK: (type $8 (func (param externref (ref null $array16) i32) (result i32))) - ;; CHECK: (type $9 (func)) + ;; CHECK: (type $9 (func (param externref i32) (result i32))) - ;; CHECK: (type $10 (func (param (ref $array16)))) + ;; CHECK: (type $10 (func (param externref) (result (ref extern)))) - ;; CHECK: (type $11 (func (result externref))) + ;; CHECK: (type $11 (func)) - ;; CHECK: (type $12 (func (param externref (ref $array16)) (result i32))) + ;; CHECK: (type $12 (func (param (ref $array16)))) - ;; CHECK: (type $13 (func (param externref) (result externref))) + ;; CHECK: (type $13 (func (result externref))) - ;; CHECK: (type $14 (func (param externref))) + ;; CHECK: (type $14 (func (param externref (ref $array16)) (result i32))) + + ;; CHECK: (type $15 (func (param i32) (result externref))) + + ;; CHECK: (type $16 (func (param i32 i32) (result externref))) + + ;; CHECK: (type $17 (func (param externref))) ;; CHECK: (import "\'" "foo" (global $string_foo (ref extern))) (import "\'" "foo" (global $string_foo (ref extern))) ;; CHECK: (import "\'" "bar" (global $string_bar (ref extern))) (import "\'" "bar" (global $string_bar (ref extern))) - ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $5) (param (ref null $array16) i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $7) (param (ref null $array16) i32 i32) (result (ref extern)))) (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (param (ref null $array16) i32 i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $6) (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $4) (param i32) (result (ref extern)))) (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (param i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $4) (param externref externref) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $5) (param externref externref) (result (ref extern)))) (import "wasm:js-string" "concat" (func $concat (param externref externref) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $array16) i32) (result i32))) + ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $8) (param externref (ref null $array16) i32) (result i32))) (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (param externref (ref null $array16) i32) (result i32))) ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) (import "wasm:js-string" "equals" (func $equals (param externref externref) (result i32))) @@ -55,10 +61,14 @@ (import "wasm:js-string" "test" (func $test (param externref) (result i32))) ;; CHECK: (import "wasm:js-string" "length" (func $length (type $0) (param externref) (result i32))) (import "wasm:js-string" "length" (func $length (param externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32))) + ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $9) (param externref i32) (result i32))) (import "wasm:js-string" "charCodeAt" (func $charCodeAt (param externref i32) (result i32))) ;; CHECK: (import "wasm:js-string" "substring" (func $substring_foo (type $3) (param externref i32 i32) (result (ref extern)))) (import "wasm:js-string" "substring" (func $substring_foo (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $4) (param i32) (result (ref extern)))) + (import "wasm:js-string" "fromCharCode" (func $fromCharCode (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (type $10) (param externref) (result (ref extern)))) + (import "wasm:js-string" "cast" (func $cast (param externref) (result (ref extern)))) ;; A function from the right module, but an unsupported name. ;; CHECK: (import "wasm:js-string" "wrong-name" (func $wrong-base (type $3) (param externref i32 i32) (result (ref extern)))) @@ -68,7 +78,7 @@ ;; CHECK: (import "oops" "substring" (func $wrong-module (type $3) (param externref i32 i32) (result (ref extern)))) (import "oops" "substring" (func $wrong-module (param externref i32 i32) (result (ref extern)))) - ;; CHECK: (func $func (type $9) + ;; CHECK: (func $func (type $11) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (string.const "foo") ;; CHECK-NEXT: ) @@ -94,7 +104,7 @@ ) ) - ;; CHECK: (func $string.new.gc (type $10) (param $ref (ref $array16)) + ;; CHECK: (func $string.new.gc (type $12) (param $ref (ref $array16)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (string.new_wtf16_array ;; CHECK-NEXT: (local.get $ref) @@ -113,7 +123,7 @@ ) ) - ;; CHECK: (func $string.from_code_point (type $11) (result externref) + ;; CHECK: (func $string.from_code_point (type $13) (result externref) ;; CHECK-NEXT: (string.from_code_point ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) @@ -124,7 +134,7 @@ ) ) - ;; CHECK: (func $string.concat (type $4) (param $a externref) (param $b externref) (result (ref extern)) + ;; CHECK: (func $string.concat (type $5) (param $a externref) (param $b externref) (result (ref extern)) ;; CHECK-NEXT: (string.concat ;; CHECK-NEXT: (local.get $a) ;; CHECK-NEXT: (local.get $b) @@ -137,7 +147,7 @@ ) ) - ;; CHECK: (func $string.encode (type $12) (param $ref externref) (param $array16 (ref $array16)) (result i32) + ;; CHECK: (func $string.encode (type $14) (param $ref externref) (param $array16 (ref $array16)) (result i32) ;; CHECK-NEXT: (string.encode_wtf16_array ;; CHECK-NEXT: (local.get $ref) ;; CHECK-NEXT: (local.get $array16) @@ -213,7 +223,7 @@ ) ) - ;; CHECK: (func $string.slice (type $13) (param $ref externref) (result externref) + ;; CHECK: (func $string.slice (type $6) (param $ref externref) (result externref) ;; CHECK-NEXT: (stringview_wtf16.slice ;; CHECK-NEXT: (local.get $ref) ;; CHECK-NEXT: (i32.const 2) @@ -230,7 +240,62 @@ ) ) - ;; CHECK: (func $wrong (type $14) (param $ref externref) + ;; CHECK: (func $string.from_char_code (type $15) (param $x i32) (result externref) + ;; CHECK-NEXT: (string.from_char_code + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $string.from_char_code (param $x i32) (result externref) + (call $fromCharCode + (local.get $x) + ) + ) + + ;; CHECK: (func $string.cast (type $6) (param $x externref) (result externref) + ;; CHECK-NEXT: (ref.cast (ref string) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $string.cast (param $x externref) (result externref) + (call $cast + (local.get $x) + ) + ) + + ;; CHECK: (func $refinalize (type $16) (param $x i32) (param $c i32) (result externref) + ;; CHECK-NEXT: (if (result (ref string)) + ;; CHECK-NEXT: (local.get $c) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (string.from_char_code + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (string.from_char_code + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $refinalize (param $x i32) (param $c i32) (result externref) + ;; Asserts IR is refinalized when externrefs (in this case, non-nullable) + ;; are replaced with stringrefs. + (if (result (ref extern)) + (local.get $c) + (then + (call $fromCharCode + (local.get $x) + ) + ) + (else + (call $fromCharCode + (local.get $x) + ) + ) + ) + ) + + ;; CHECK: (func $wrong (type $17) (param $ref externref) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (call $wrong-base ;; CHECK-NEXT: (local.get $ref) diff --git a/test/lit/passes/string-lowering-imports-custom-module.wast b/test/lit/passes/string-lowering-imports-custom-module.wast index 2d82bfb74d8..f983887c9be 100644 --- a/test/lit/passes/string-lowering-imports-custom-module.wast +++ b/test/lit/passes/string-lowering-imports-custom-module.wast @@ -9,15 +9,15 @@ (module ;; CHECK: (type $0 (array (mut i16))) - ;; CHECK: (type $1 (func (param externref externref) (result i32))) + ;; CHECK: (type $1 (func (param i32) (result (ref extern)))) - ;; CHECK: (type $2 (func (param externref) (result i32))) + ;; CHECK: (type $2 (func (param externref externref) (result i32))) - ;; CHECK: (type $3 (func)) + ;; CHECK: (type $3 (func (param externref) (result i32))) - ;; CHECK: (type $4 (func (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (type $4 (func)) - ;; CHECK: (type $5 (func (param i32) (result (ref extern)))) + ;; CHECK: (type $5 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; CHECK: (type $6 (func (param externref externref) (result (ref extern)))) @@ -27,31 +27,37 @@ ;; CHECK: (type $9 (func (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (type $10 (func (param externref) (result (ref extern)))) + ;; CHECK: (import "strings" "foo" (global $"string.const_\"foo\"" (ref extern))) - ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $5) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $1) (param i32) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32))) - ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "test" (func $test (type $2) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "length" (func $length (type $2) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32))) ;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $1) (param i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (type $10) (param externref) (result (ref extern)))) + ;; CHECK: (export "const" (func $const)) - ;; CHECK: (func $const (type $3) + ;; CHECK: (func $const (type $4) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (global.get $"string.const_\"foo\"") ;; CHECK-NEXT: ) diff --git a/test/lit/passes/string-lowering-instructions.wast b/test/lit/passes/string-lowering-instructions.wast index 23303838d20..0d925886a8a 100644 --- a/test/lit/passes/string-lowering-instructions.wast +++ b/test/lit/passes/string-lowering-instructions.wast @@ -27,33 +27,33 @@ ;; CHECK: (type $7 (func (param (ref $array16)))) - ;; CHECK: (type $8 (func (param externref) (result i32))) + ;; CHECK: (type $8 (func (param anyref) (result (ref i31)))) - ;; CHECK: (type $9 (func (param externref externref) (result (ref extern)))) + ;; CHECK: (type $9 (func (param externref (ref $array16)) (result i32))) - ;; CHECK: (type $10 (func (param externref (ref $array16)) (result i32))) + ;; CHECK: (type $10 (func (param externref) (result externref))) - ;; CHECK: (type $11 (func (param externref externref) (result i32))) + ;; CHECK: (type $11 (func (param i32) (result externref))) - ;; CHECK: (type $12 (func (param externref) (result externref))) + ;; CHECK: (type $12 (func (param externref))) - ;; CHECK: (type $13 (func (param externref))) + ;; CHECK: (type $13 (func)) - ;; CHECK: (type $14 (func)) + ;; CHECK: (type $14 (func (param externref) (result i32))) - ;; CHECK: (type $15 (func (param externref) (result i32))) + ;; CHECK: (type $15 (func (param externref externref) (result i32))) - ;; CHECK: (type $16 (func (result externref))) + ;; CHECK: (type $16 (func (param externref externref) (result (ref extern)))) - ;; CHECK: (type $17 (func (param externref externref) (result i32))) + ;; CHECK: (type $17 (func (param externref) (result (ref extern)))) - ;; CHECK: (type $18 (func (param externref i32 externref))) + ;; CHECK: (type $18 (func (result externref))) - ;; CHECK: (type $19 (func (param (ref null $array16) i32 i32) (result (ref extern)))) + ;; CHECK: (type $19 (func (param i32) (result (ref extern)))) - ;; CHECK: (type $20 (func (param i32) (result (ref extern)))) + ;; CHECK: (type $20 (func (param externref i32 externref))) - ;; CHECK: (type $21 (func (param externref externref) (result (ref extern)))) + ;; CHECK: (type $21 (func (param (ref null $array16) i32 i32) (result (ref extern)))) ;; CHECK: (type $22 (func (param externref (ref null $array16) i32) (result i32))) @@ -65,30 +65,34 @@ ;; CHECK: (import "string.const" "1" (global $"string.const_\"value\"" (ref extern))) - ;; CHECK: (import "colliding" "name" (func $fromCodePoint (type $14))) + ;; CHECK: (import "colliding" "name" (func $fromCodePoint (type $13))) (import "colliding" "name" (func $fromCodePoint)) - ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $19) (param (ref null $array16) i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $21) (param (ref null $array16) i32 i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint_19 (type $20) (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint_25 (type $19) (param i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $21) (param externref externref) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $16) (param externref externref) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $22) (param externref (ref null $array16) i32) (result i32))) - ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $17) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $15) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "test" (func $test (type $15) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "test" (func $test (type $14) (param externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $17) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $15) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "length" (func $length (type $15) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "length" (func $length (type $14) (param externref) (result i32))) ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $23) (param externref i32) (result i32))) ;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $24) (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $19) (param i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (type $17) (param externref) (result (ref extern)))) + ;; CHECK: (global $string externref (ref.null noextern)) (global $string stringref (ref.null string)) ;; Test we update global nulls. @@ -115,8 +119,8 @@ ) ) - ;; CHECK: (func $string.from_code_point (type $16) (result externref) - ;; CHECK-NEXT: (call $fromCodePoint_19 + ;; CHECK: (func $string.from_code_point (type $18) (result externref) + ;; CHECK-NEXT: (call $fromCodePoint_25 ;; CHECK-NEXT: (i32.const 1) ;; CHECK-NEXT: ) ;; CHECK-NEXT: ) @@ -126,7 +130,7 @@ ) ) - ;; CHECK: (func $string.concat (type $9) (param $0 externref) (param $1 externref) (result (ref extern)) + ;; CHECK: (func $string.concat (type $16) (param $0 externref) (param $1 externref) (result (ref extern)) ;; CHECK-NEXT: (call $concat ;; CHECK-NEXT: (local.get $0) ;; CHECK-NEXT: (local.get $1) @@ -139,7 +143,7 @@ ) ) - ;; CHECK: (func $string.encode (type $10) (param $ref externref) (param $array16 (ref $array16)) (result i32) + ;; CHECK: (func $string.encode (type $9) (param $ref externref) (param $array16 (ref $array16)) (result i32) ;; CHECK-NEXT: (call $intoCharCodeArray ;; CHECK-NEXT: (local.get $ref) ;; CHECK-NEXT: (local.get $array16) @@ -154,7 +158,7 @@ ) ) - ;; CHECK: (func $string.eq (type $11) (param $a externref) (param $b externref) (result i32) + ;; CHECK: (func $string.eq (type $15) (param $a externref) (param $b externref) (result i32) ;; CHECK-NEXT: (call $equals ;; CHECK-NEXT: (local.get $a) ;; CHECK-NEXT: (local.get $b) @@ -167,7 +171,7 @@ ) ) - ;; CHECK: (func $string.compare (type $11) (param $a externref) (param $b externref) (result i32) + ;; CHECK: (func $string.compare (type $15) (param $a externref) (param $b externref) (result i32) ;; CHECK-NEXT: (call $compare ;; CHECK-NEXT: (local.get $a) ;; CHECK-NEXT: (local.get $b) @@ -180,7 +184,7 @@ ) ) - ;; CHECK: (func $string-test (type $8) (param $str externref) (result i32) + ;; CHECK: (func $string-test (type $14) (param $str externref) (result i32) ;; CHECK-NEXT: (call $test ;; CHECK-NEXT: (local.get $str) ;; CHECK-NEXT: ) @@ -191,7 +195,7 @@ ) ) - ;; CHECK: (func $string.length (type $8) (param $ref externref) (result i32) + ;; CHECK: (func $string.length (type $14) (param $ref externref) (result i32) ;; CHECK-NEXT: (call $length ;; CHECK-NEXT: (local.get $ref) ;; CHECK-NEXT: ) @@ -202,7 +206,7 @@ ) ) - ;; CHECK: (func $string.get_codeunit (type $8) (param $ref externref) (result i32) + ;; CHECK: (func $string.get_codeunit (type $14) (param $ref externref) (result i32) ;; CHECK-NEXT: (call $charCodeAt ;; CHECK-NEXT: (local.get $ref) ;; CHECK-NEXT: (i32.const 2) @@ -215,7 +219,7 @@ ) ) - ;; CHECK: (func $string.slice (type $12) (param $ref externref) (result externref) + ;; CHECK: (func $string.slice (type $10) (param $ref externref) (result externref) ;; CHECK-NEXT: (call $substring ;; CHECK-NEXT: (local.get $ref) ;; CHECK-NEXT: (i32.const 2) @@ -230,7 +234,112 @@ ) ) - ;; CHECK: (func $if.string (type $12) (param $ref externref) (result externref) + ;; CHECK: (func $string.from_char_code (type $11) (param $x i32) (result externref) + ;; CHECK-NEXT: (call $fromCharCode + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $string.from_char_code (param $x i32) (result stringref) + (string.from_char_code + (local.get $x) + ) + ) + + ;; CHECK: (func $string.cast (type $17) (param $x externref) (result (ref extern)) + ;; CHECK-NEXT: (call $cast + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $string.cast (param $x externref) (result (ref string)) + (ref.cast (ref string) + (local.get $x) + ) + ) + + ;; CHECK: (func $string.cast.null (type $10) (param $x externref) (result externref) + ;; CHECK-NEXT: (local $1 externref) + ;; CHECK-NEXT: (if (result externref) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (ref.null noextern) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (call $cast + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $string.cast.null (param $x externref) (result stringref) + ;; We replace a nullable cast with conditional ref.is_null. + (ref.cast stringref + (local.get $x) + ) + ) + + ;; CHECK: (func $string.cast.nested (type $17) (param $x externref) (result (ref extern)) + ;; CHECK-NEXT: (local $1 externref) + ;; CHECK-NEXT: (call $cast + ;; CHECK-NEXT: (if (result externref) + ;; CHECK-NEXT: (ref.is_null + ;; CHECK-NEXT: (local.tee $1 + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (then + ;; CHECK-NEXT: (ref.null noextern) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (else + ;; CHECK-NEXT: (call $cast + ;; CHECK-NEXT: (local.get $1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $string.cast.nested (param $x externref) (result (ref string)) + (ref.cast (ref string) + (ref.cast stringref + (local.get $x) + ) + ) + ) + + ;; CHECK: (func $string.cast.operand (type $16) (param $x externref) (param $y externref) (result (ref extern)) + ;; CHECK-NEXT: (call $concat + ;; CHECK-NEXT: (call $cast + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (local.get $y) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $string.cast.operand (param $x externref) (param $y stringref) (result (ref string)) + ;; We replace this string cast with the imported cast function call. + (string.concat + (ref.cast (ref string) + (local.get $x) + ) + (local.get $y) + ) + ) + + ;; CHECK: (func $string.cast.other (type $8) (param $x anyref) (result (ref i31)) + ;; CHECK-NEXT: (ref.cast (ref i31) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $string.cast.other (param $x anyref) (result (ref i31)) + ;; Test that casts to non-string types are not lowered. + (ref.cast (ref i31) + (local.get $x) + ) + ) + + ;; CHECK: (func $if.string (type $10) (param $ref externref) (result externref) ;; CHECK-NEXT: (if (result externref) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (then @@ -254,7 +363,7 @@ ) ) - ;; CHECK: (func $if.string.flip (type $12) (param $ref externref) (result externref) + ;; CHECK: (func $if.string.flip (type $10) (param $ref externref) (result externref) ;; CHECK-NEXT: (if (result externref) ;; CHECK-NEXT: (i32.const 0) ;; CHECK-NEXT: (then @@ -278,7 +387,7 @@ ) ) - ;; CHECK: (func $exported-string-returner (type $16) (result externref) + ;; CHECK: (func $exported-string-returner (type $18) (result externref) ;; CHECK-NEXT: (global.get $"string.const_\"exported\"") ;; CHECK-NEXT: ) (func $exported-string-returner (export "export.1") (result stringref) @@ -287,7 +396,7 @@ (string.const "exported") ) - ;; CHECK: (func $exported-string-receiver (type $18) (param $x externref) (param $y i32) (param $z externref) + ;; CHECK: (func $exported-string-receiver (type $20) (param $x externref) (param $y i32) (param $z externref) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) ;; CHECK-NEXT: ) @@ -312,7 +421,7 @@ ) ) - ;; CHECK: (func $use-struct-of-array (type $14) + ;; CHECK: (func $use-struct-of-array (type $13) ;; CHECK-NEXT: (local $array16 (ref $array16)) ;; CHECK-NEXT: (local $open (ref $array16-open)) ;; CHECK-NEXT: (local $child (ref $array16-child)) @@ -368,7 +477,7 @@ ) ) - ;; CHECK: (func $struct-of-string (type $14) + ;; CHECK: (func $struct-of-string (type $13) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (struct.new $struct-of-string ;; CHECK-NEXT: (ref.null noextern) @@ -411,7 +520,7 @@ ) ) - ;; CHECK: (func $call-param-null (type $13) (param $str externref) + ;; CHECK: (func $call-param-null (type $12) (param $str externref) ;; CHECK-NEXT: (call $call-param-null ;; CHECK-NEXT: (ref.null noextern) ;; CHECK-NEXT: ) diff --git a/test/lit/passes/string-lowering_types.wast b/test/lit/passes/string-lowering_types.wast index 9b20cafbe31..65fe5721ca2 100644 --- a/test/lit/passes/string-lowering_types.wast +++ b/test/lit/passes/string-lowering_types.wast @@ -10,24 +10,24 @@ (rec ;; CHECK: (type $0 (array (mut i16))) - ;; CHECK: (type $1 (func (param externref externref) (result i32))) + ;; CHECK: (type $1 (func (param i32) (result (ref extern)))) - ;; CHECK: (type $2 (func (param externref) (result i32))) + ;; CHECK: (type $2 (func (param externref externref) (result i32))) - ;; CHECK: (type $3 (func (param externref))) + ;; CHECK: (type $3 (func (param externref) (result i32))) - ;; CHECK: (type $4 (func (param (ref extern)))) + ;; CHECK: (type $4 (func (param externref))) - ;; CHECK: (type $5 (func)) + ;; CHECK: (type $5 (func (param (ref extern)))) + + ;; CHECK: (type $6 (func)) ;; CHECK: (type $private (struct (field externref))) (type $private (struct (field stringref))) ) (type $public (func (param stringref))) - ;; CHECK: (type $7 (func (param (ref null $0) i32 i32) (result (ref extern)))) - - ;; CHECK: (type $8 (func (param i32) (result (ref extern)))) + ;; CHECK: (type $8 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; CHECK: (type $9 (func (param externref externref) (result (ref extern)))) @@ -37,35 +37,41 @@ ;; CHECK: (type $12 (func (param externref i32 i32) (result (ref extern)))) - ;; CHECK: (import "a" "b" (func $import (type $3) (param externref))) + ;; CHECK: (type $13 (func (param externref) (result (ref extern)))) + + ;; CHECK: (import "a" "b" (func $import (type $4) (param externref))) (import "a" "b" (func $import (type $public) (param stringref))) - ;; CHECK: (import "a" "b" (func $import-implicit (type $4) (param (ref extern)))) + ;; CHECK: (import "a" "b" (func $import-implicit (type $5) (param (ref extern)))) (import "a" "b" (func $import-implicit (param (ref string)))) - ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $7) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $8) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $8) (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $1) (param i32) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $9) (param externref externref) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $10) (param externref (ref null $0) i32) (result i32))) - ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "test" (func $test (type $2) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "length" (func $length (type $2) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $11) (param externref i32) (result i32))) ;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $12) (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $1) (param i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (type $13) (param externref) (result (ref extern)))) + ;; CHECK: (export "export" (func $export)) - ;; CHECK: (func $export (type $5) + ;; CHECK: (func $export (type $6) ;; CHECK-NEXT: (local $0 (ref $private)) ;; CHECK-NEXT: ) (func $export (export "export") @@ -79,15 +85,15 @@ (module ;; CHECK: (type $0 (array (mut i16))) - ;; CHECK: (type $1 (func (param externref externref) (result i32))) + ;; CHECK: (type $1 (func (param i32) (result (ref extern)))) - ;; CHECK: (type $2 (func (param externref) (result i32))) + ;; CHECK: (type $2 (func (param externref externref) (result i32))) - ;; CHECK: (type $3 (func (result (ref extern)))) + ;; CHECK: (type $3 (func (param externref) (result i32))) - ;; CHECK: (type $4 (func (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (type $4 (func (result (ref extern)))) - ;; CHECK: (type $5 (func (param i32) (result (ref extern)))) + ;; CHECK: (type $5 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; CHECK: (type $6 (func (param externref externref) (result (ref extern)))) @@ -97,33 +103,39 @@ ;; CHECK: (type $9 (func (param externref i32 i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $4) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (type $10 (func (param externref) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $5) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $5) (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $1) (param i32) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $6) (param externref externref) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $7) (param externref (ref null $0) i32) (result i32))) - ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "test" (func $test (type $2) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "length" (func $length (type $2) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $8) (param externref i32) (result i32))) ;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $9) (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $1) (param i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (type $10) (param externref) (result (ref extern)))) + ;; CHECK: (table $table 31 31 funcref) (table $table 31 31 funcref) ;; CHECK: (elem $elem (i32.const 0) $func) (elem $elem (i32.const 0) $func) - ;; CHECK: (func $func (type $3) (result (ref extern)) + ;; CHECK: (func $func (type $4) (result (ref extern)) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (ref.func $func) ;; CHECK-NEXT: ) @@ -145,17 +157,17 @@ ;; CHECK: (type $0 (array (mut i16))) - ;; CHECK: (type $1 (func (param externref externref) (result i32))) + ;; CHECK: (type $1 (func (param i32) (result (ref extern)))) - ;; CHECK: (type $2 (func (param externref) (result i32))) + ;; CHECK: (type $2 (func (param externref externref) (result i32))) - ;; CHECK: (type $3 (func (param (ref extern)))) + ;; CHECK: (type $3 (func (param externref) (result i32))) - ;; CHECK: (type $4 (func)) + ;; CHECK: (type $4 (func (param (ref extern)))) - ;; CHECK: (type $5 (func (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (type $5 (func)) - ;; CHECK: (type $6 (func (param i32) (result (ref extern)))) + ;; CHECK: (type $6 (func (param (ref null $0) i32 i32) (result (ref extern)))) ;; CHECK: (type $7 (func (param externref externref) (result (ref extern)))) @@ -165,31 +177,37 @@ ;; CHECK: (type $10 (func (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (type $11 (func (param externref) (result (ref extern)))) + ;; CHECK: (import "string.const" "0" (global $"string.const_\"whoops\"" (ref extern))) - ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $5) (param (ref null $0) i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $6) (param (ref null $0) i32 i32) (result (ref extern)))) - ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $6) (param i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $1) (param i32) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "concat" (func $concat (type $7) (param externref externref) (result (ref extern)))) ;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $8) (param externref (ref null $0) i32) (result i32))) - ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $2) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "test" (func $test (type $2) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "test" (func $test (type $3) (param externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $2) (param externref externref) (result i32))) - ;; CHECK: (import "wasm:js-string" "length" (func $length (type $2) (param externref) (result i32))) + ;; CHECK: (import "wasm:js-string" "length" (func $length (type $3) (param externref) (result i32))) ;; CHECK: (import "wasm:js-string" "charCodeAt" (func $charCodeAt (type $9) (param externref i32) (result i32))) ;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $10) (param externref i32 i32) (result (ref extern)))) + ;; CHECK: (import "wasm:js-string" "fromCharCode" (func $fromCharCode (type $1) (param i32) (result (ref extern)))) + + ;; CHECK: (import "wasm:js-string" "cast" (func $cast (type $11) (param externref) (result (ref extern)))) + ;; CHECK: (table $table 1 1 funcref) - ;; CHECK: (tag $tag (type $3) (param (ref extern))) + ;; CHECK: (tag $tag (type $4) (param (ref extern))) (tag $tag (type $str) (param (ref string))) (table $table 1 1 funcref) @@ -199,7 +217,7 @@ ;; CHECK: (export "table" (table $table)) (export "table" (table $table)) - ;; CHECK: (func $throw (type $4) + ;; CHECK: (func $throw (type $5) ;; CHECK-NEXT: (throw $tag ;; CHECK-NEXT: (global.get $"string.const_\"whoops\"") ;; CHECK-NEXT: ) diff --git a/test/lit/validation/ref-cast-no-gc.wast b/test/lit/validation/ref-cast-no-gc.wast new file mode 100644 index 00000000000..b713ac4aac9 --- /dev/null +++ b/test/lit/validation/ref-cast-no-gc.wast @@ -0,0 +1,35 @@ +;; Test that casts without GC is a validation error. + +;; RUN: not wasm-opt %s --enable-reference-types --enable-strings 2>&1 | filecheck %s + +;; CHECK: [wasm-validator error in function cast-func] unexpected false: ref.cast requires gc [--enable-gc] +;; CHECK: [wasm-validator error in function cast-nullfunc] unexpected false: ref.cast requires gc [--enable-gc] +;; CHECK: [wasm-validator error in function cast-extern] unexpected false: ref.cast requires gc [--enable-gc] +;; CHECK-NOT: ref.cast requires gc + +(module + (func $cast-func (param $x funcref) (result funcref) + (ref.cast funcref + (local.get $x) + ) + ) + + (func $cast-nullfunc (param $x funcref) (result nullfuncref) + (ref.cast nullfuncref + (local.get $x) + ) + ) + + (func $cast-extern (param $x externref) (result (ref extern)) + (ref.cast (ref extern) + (local.get $x) + ) + ) + + ;; GC is disabled; nullexternref == externref + (func $cast-nullexternref (param $x externref) (result nullexternref) + (ref.cast nullexternref + (local.get $x) + ) + ) +) diff --git a/test/lit/validation/ref-cast-string-no-gc.wast b/test/lit/validation/ref-cast-string-no-gc.wast new file mode 100644 index 00000000000..6ac2c16bff1 --- /dev/null +++ b/test/lit/validation/ref-cast-string-no-gc.wast @@ -0,0 +1,86 @@ +;; Test that string casts without GC are allowed. + +;; RUN: wasm-opt %s --enable-reference-types --enable-strings -S -o - | filecheck %s + +(module + ;; CHECK: (func $cast-from-nullable (param $x externref) (result stringref) + ;; CHECK-NEXT: (ref.cast stringref + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast-from-nullable (param $x externref) (result stringref) + (ref.cast stringref + (local.get $x) + ) + ) + + ;; CHECK: (func $cast-from-non-nullable (param $x externref) (result (ref string)) + ;; CHECK-NEXT: (ref.cast (ref string) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast-from-non-nullable (param $x externref) (result (ref string)) + (ref.cast (ref string) + (local.get $x) + ) + ) + + ;; CHECK: (func $cast-from-string (param $x stringref) (result (ref string)) + ;; CHECK-NEXT: (ref.cast (ref string) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast-from-string (param $x stringref) (result (ref string)) + (ref.cast (ref string) + (local.get $x) + ) + ) + + ;; CHECK: (func $cast-from-null (result (ref noextern)) + ;; CHECK-NEXT: (ref.cast (ref noextern) + ;; CHECK-NEXT: (ref.null noextern) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast-from-null (result (ref noextern)) + ;; A cast of a null is refinalized to have the bottom type. + (ref.cast (ref string) + (ref.null noextern) + ) + ) + + ;; CHECK: (func $cast-from-unreachable (result (ref string)) + ;; CHECK-NEXT: (block ;; (replaces unreachable RefCast we can't emit) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (unreachable) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast-from-unreachable (result (ref string)) + (ref.cast (ref string) + (unreachable) + ) + ) + + ;; CHECK: (func $cast-to-nullexternref (param $x externref) (result nullexternref) + ;; CHECK-NEXT: (ref.cast nullexternref + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast-to-nullexternref (param $x externref) (result nullexternref) + (ref.cast nullexternref + (local.get $x) + ) + ) + + ;; CHECK: (func $cast-to-non-nullable-noextern (param $x externref) (result (ref noextern)) + ;; CHECK-NEXT: (ref.cast (ref noextern) + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $cast-to-non-nullable-noextern (param $x externref) (result (ref noextern)) + (ref.cast (ref noextern) + (local.get $x) + ) + ) +) diff --git a/test/lit/validation/ref-cast-string-no-strings.wast b/test/lit/validation/ref-cast-string-no-strings.wast new file mode 100644 index 00000000000..7c235f9d485 --- /dev/null +++ b/test/lit/validation/ref-cast-string-no-strings.wast @@ -0,0 +1,27 @@ +;; Test that string casts without strings is a validation error. + +;; RUN: not wasm-opt %s --enable-reference-types --disable-strings --disable-gc 2>&1 | filecheck %s + +;; CHECK: [wasm-validator error in function cast] unexpected false: ref.cast requires gc [--enable-gc] +;; CHECK: [wasm-validator error in function cast-nullexternref] unexpected false: ref.cast requires gc [--enable-gc] +;; CHECK: [wasm-validator error in function cast-unreachable] unexpected false: ref.cast requires gc [--enable-gc] + +(module + (func $cast (param $x externref) (result stringref) + (ref.cast stringref + (local.get $x) + ) + ) + + (func $cast-nullexternref (param $x externref) (result nullexternref) + (ref.cast nullexternref + (local.get $x) + ) + ) + + (func $cast-unreachable (result stringref) + (ref.cast stringref + (unreachable) + ) + ) +)