Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StringLowering: Use an array16 type in its own rec group #6302

Merged
merged 11 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions src/passes/StringLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,12 @@ struct StringLowering : public StringGathering {
// First, run the gathering operation so all string.consts are in one place.
StringGathering::run(module);

// Lower the string.const globals into imports.
makeImports(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);

Expand Down Expand Up @@ -230,6 +230,11 @@ struct StringLowering : public StringGathering {
CustomSection{"string.consts", std::move(vec)});
}

// Common types used in imports.
Type nullArray16 = Type(Array(Field(Field::i16, Mutable)), Nullable);
Type nullExt = Type(HeapType::ext, Nullable);
Type nnExt = Type(HeapType::ext, NonNullable);

void updateTypes(Module* module) {
TypeMapper::TypeUpdates updates;

Expand All @@ -240,8 +245,24 @@ struct StringLowering : public StringGathering {
updates[HeapType::stringview_wtf16] = HeapType::ext;
updates[HeapType::stringview_iter] = HeapType::ext;

// The module may have its own array16 type inside a big rec group, but
// imported strings expects that type in its own rec group as part of the
// ABI. Fix that up here. (This is valid to do as this type has no sub- or
// super-types anyhow; it is "plain old data" for communicating with the
// outside.)
auto allTypes = ModuleUtils::collectHeapTypes(*module);
auto array16 = nullArray16.getHeapType();
auto array16Element = array16.getArray().element;
for (auto type : allTypes) {
// Match an array type with no super and that is closed.
if (type.isArray() && !type.getDeclaredSuperType() && !type.isOpen() &&
type.getArray().element == array16Element) {
updates[type] = array16;
}
}

// We consider all types that use strings as modifiable, which means we
// mark them as non-private. That is, we are doing something TypeMapper
// mark them as non-public. That is, we are doing something TypeMapper
// normally does not, as we are changing the external interface/ABI of the
// module: we are changing that ABI from using strings to externs.
auto publicTypes = ModuleUtils::getPublicHeapTypes(*module);
Expand All @@ -268,11 +289,6 @@ struct StringLowering : public StringGathering {
// The name of the module to import string functions from.
Name WasmStringsModule = "wasm:js-string";

// Common types used in imports.
Type nullArray16 = Type(Array(Field(Field::i16, Mutable)), Nullable);
Type nullExt = Type(HeapType::ext, Nullable);
Type nnExt = Type(HeapType::ext, NonNullable);

// Creates an imported string function, returning its name (which is equal to
// the true name of the import, if there is no conflict).
Name addImport(Module* module, Name trueName, Type params, Type results) {
Expand Down
161 changes: 118 additions & 43 deletions test/lit/passes/string-lowering-instructions.wast
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,89 @@
;; RUN: foreach %s %t wasm-opt --string-lowering -all -S -o - | filecheck %s

(module
;; CHECK: (type $array16 (array (mut i16)))
(type $array16 (array (mut i16)))
(rec
;; CHECK: (type $0 (array (mut i16)))

;; CHECK: (type $1 (func (param externref externref) (result i32)))
;; CHECK: (type $1 (func))

;; CHECK: (rec
;; CHECK-NEXT: (type $2 (func (param externref i32 externref)))
;; CHECK: (type $2 (func (param externref externref) (result i32)))

;; CHECK: (type $3 (func (result externref)))
;; CHECK: (rec
;; CHECK-NEXT: (type $3 (func (param externref i32 externref)))

;; CHECK: (type $4 (func (param externref) (result externref)))
;; CHECK: (type $4 (func (result externref)))

;; CHECK: (type $5 (func (param externref) (result externref)))
;; CHECK: (type $struct-of-array (struct (field (ref $0))))
(type $struct-of-array (struct (field (ref $array16))))

;; CHECK: (type $6 (func (param externref) (result i32)))
;; CHECK: (type $array16-imm (array i32))
(type $array16-imm (array i32))

;; CHECK: (type $7 (func (param externref externref) (result i32)))
;; CHECK: (type $array32 (array (mut i32)))
(type $array32 (array (mut i32)))

;; CHECK: (type $8 (func (param externref (ref $array16)) (result i32)))
;; CHECK: (type $array16-open (sub (array (mut i16))))
(type $array16-open (sub (array (mut i16))))

;; CHECK: (type $9 (func (param (ref $array16))))
;; CHECK: (type $array16-child (sub $array16-open (array (mut i16))))
(type $array16-child (sub $array16-open (array (mut i16))))

;; CHECK: (type $10 (func (param externref externref externref externref)))
;; CHECK: (type $array16 (array (mut i16)))
(type $array16 (array (mut i16)))
)

;; CHECK: (type $11 (func (param externref) (result externref)))

;; CHECK: (type $12 (func (param externref) (result externref)))

;; CHECK: (type $13 (func (param externref) (result i32)))

;; CHECK: (type $14 (func (param externref externref) (result i32)))

;; CHECK: (type $15 (func (param externref (ref $0)) (result i32)))

;; CHECK: (type $11 (func))
;; CHECK: (type $16 (func (param (ref $0))))

;; CHECK: (type $12 (func (param (ref null $array16) i32 i32) (result (ref extern))))
;; CHECK: (type $17 (func (param externref externref externref externref)))

;; CHECK: (type $13 (func (param i32) (result (ref extern))))
;; CHECK: (type $18 (func (param (ref null $0) i32 i32) (result (ref extern))))

;; CHECK: (type $14 (func (param externref (ref null $array16) i32) (result i32)))
;; CHECK: (type $19 (func (param i32) (result (ref extern))))

;; CHECK: (type $15 (func (param externref) (result i32)))
;; CHECK: (type $20 (func (param externref (ref null $0) i32) (result i32)))

;; CHECK: (type $16 (func (param externref i32) (result i32)))
;; CHECK: (type $21 (func (param externref) (result i32)))

;; CHECK: (type $17 (func (param externref i32 i32) (result (ref extern))))
;; CHECK: (type $22 (func (param externref i32) (result i32)))

;; CHECK: (type $23 (func (param externref i32 i32) (result (ref extern))))

;; CHECK: (import "string.const" "0" (global $string.const_exported (ref extern)))

;; CHECK: (import "colliding" "name" (func $fromCodePoint (type $11)))
;; CHECK: (import "colliding" "name" (func $fromCodePoint (type $1)))
(import "colliding" "name" (func $fromCodePoint))

;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $12) (param (ref null $array16) i32 i32) (result (ref extern))))
;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $18) (param (ref null $0) i32 i32) (result (ref extern))))

;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint_15 (type $13) (param i32) (result (ref extern))))
;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint_16 (type $19) (param i32) (result (ref extern))))

;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $14) (param externref (ref null $array16) i32) (result i32)))
;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $20) (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" "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 $15) (param externref) (result i32)))
;; CHECK: (import "wasm:js-string" "length" (func $length (type $21) (param externref) (result i32)))

;; CHECK: (import "wasm:js-string" "codePointAt" (func $codePointAt (type $16) (param externref i32) (result i32)))
;; CHECK: (import "wasm:js-string" "codePointAt" (func $codePointAt (type $22) (param externref i32) (result i32)))

;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $17) (param externref i32 i32) (result (ref extern))))
;; CHECK: (import "wasm:js-string" "substring" (func $substring (type $23) (param externref i32 i32) (result (ref extern))))

;; CHECK: (export "export.1" (func $exported-string-returner))

;; CHECK: (export "export.2" (func $exported-string-receiver))

;; CHECK: (func $string.as (type $10) (param $a externref) (param $b externref) (param $c externref) (param $d externref)
;; CHECK: (func $string.as (type $17) (param $a externref) (param $b externref) (param $c externref) (param $d externref)
;; CHECK-NEXT: (local.set $b
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: )
Expand Down Expand Up @@ -101,7 +120,7 @@
)
)

;; CHECK: (func $string.new.gc (type $9) (param $array16 (ref $array16))
;; CHECK: (func $string.new.gc (type $16) (param $array16 (ref $0))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $fromCharCodeArray
;; CHECK-NEXT: (local.get $array16)
Expand All @@ -120,8 +139,8 @@
)
)

;; CHECK: (func $string.from_code_point (type $3) (result externref)
;; CHECK-NEXT: (call $fromCodePoint_15
;; CHECK: (func $string.from_code_point (type $4) (result externref)
;; CHECK-NEXT: (call $fromCodePoint_16
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
Expand All @@ -131,7 +150,7 @@
)
)

;; CHECK: (func $string.encode (type $8) (param $ref externref) (param $array16 (ref $array16)) (result i32)
;; CHECK: (func $string.encode (type $15) (param $ref externref) (param $array16 (ref $0)) (result i32)
;; CHECK-NEXT: (call $intoCharCodeArray
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (local.get $array16)
Expand All @@ -146,7 +165,7 @@
)
)

;; CHECK: (func $string.eq (type $7) (param $a externref) (param $b externref) (result i32)
;; CHECK: (func $string.eq (type $14) (param $a externref) (param $b externref) (result i32)
;; CHECK-NEXT: (call $equals
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (local.get $b)
Expand All @@ -159,7 +178,7 @@
)
)

;; CHECK: (func $string.compare (type $7) (param $a externref) (param $b externref) (result i32)
;; CHECK: (func $string.compare (type $14) (param $a externref) (param $b externref) (result i32)
;; CHECK-NEXT: (call $compare
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (local.get $b)
Expand All @@ -172,7 +191,7 @@
)
)

;; CHECK: (func $string.length (type $6) (param $ref externref) (result i32)
;; CHECK: (func $string.length (type $13) (param $ref externref) (result i32)
;; CHECK-NEXT: (call $length
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
Expand All @@ -183,7 +202,7 @@
)
)

;; CHECK: (func $string.get_codeunit (type $6) (param $ref externref) (result i32)
;; CHECK: (func $string.get_codeunit (type $13) (param $ref externref) (result i32)
;; CHECK-NEXT: (call $codePointAt
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 2)
Expand All @@ -196,7 +215,7 @@
)
)

;; CHECK: (func $string.slice (type $5) (param $ref externref) (result externref)
;; CHECK: (func $string.slice (type $12) (param $ref externref) (result externref)
;; CHECK-NEXT: (call $substring
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 2)
Expand All @@ -211,7 +230,7 @@
)
)

;; CHECK: (func $if.string (type $4) (param $ref externref) (result externref)
;; CHECK: (func $if.string (type $11) (param $ref externref) (result externref)
;; CHECK-NEXT: (if (result externref)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (then
Expand All @@ -234,7 +253,7 @@
)
)

;; CHECK: (func $if.string.flip (type $4) (param $ref externref) (result externref)
;; CHECK: (func $if.string.flip (type $11) (param $ref externref) (result externref)
;; CHECK-NEXT: (if (result externref)
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (then
Expand All @@ -258,7 +277,7 @@
)
)

;; CHECK: (func $exported-string-returner (type $3) (result externref)
;; CHECK: (func $exported-string-returner (type $4) (result externref)
;; CHECK-NEXT: (global.get $string.const_exported)
;; CHECK-NEXT: )
(func $exported-string-returner (export "export.1") (result stringref)
Expand All @@ -267,7 +286,7 @@
(string.const "exported")
)

;; CHECK: (func $exported-string-receiver (type $2) (param $x externref) (param $y i32) (param $z externref)
;; CHECK: (func $exported-string-receiver (type $3) (param $x externref) (param $y i32) (param $z externref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
Expand All @@ -291,4 +310,60 @@
(local.get $z)
)
)

;; CHECK: (func $use-struct-of-array (type $1)
;; CHECK-NEXT: (local $array16 (ref $0))
;; CHECK-NEXT: (local $open (ref $array16-open))
;; CHECK-NEXT: (local $child (ref $array16-child))
;; CHECK-NEXT: (local $32 (ref $array32))
;; CHECK-NEXT: (local $imm (ref $array16-imm))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $fromCharCodeArray
;; CHECK-NEXT: (struct.get $struct-of-array 0
;; CHECK-NEXT: (struct.new $struct-of-array
;; CHECK-NEXT: (array.new_fixed $0 2
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: (i32.const 20)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (i32.const 0)
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $use-struct-of-array
;; The array type here should switch to the new 16-bit array type that we
;; use for the new imports, so that it is compatible with them. Without
;; that, calling the import as we do below will fail.
(local $array16 (ref $array16))

;; In comparison, the array16-open param should remain as it is: it is an
;; open type which is different then the one we care about.
(local $open (ref $array16-open))

;; Likewise a child of that open type is also ignored.
(local $child (ref $array16-child))

;; Another array size is also ignored.
(local $32 (ref $array32))

;; An immutable array is also ignored.
(local $imm (ref $array16-imm))

(drop
(string.new_wtf16_array
(struct.get $struct-of-array 0
(struct.new $struct-of-array
(array.new_fixed $array16 2
(i32.const 10)
(i32.const 20)
)
)
)
(i32.const 0)
(i32.const 1)
)
)
)
)
Loading