Skip to content

Commit

Permalink
StringLowering: Lower all remaining important string operations (WebA…
Browse files Browse the repository at this point in the history
…ssembly#6283)

All those in the list from WebAssembly#6271 (comment)
  • Loading branch information
kripken authored and radekdoulik committed Jul 12, 2024
1 parent 48fe183 commit 0e6c012
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 31 deletions.
84 changes: 84 additions & 0 deletions src/passes/StringLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,13 +239,20 @@ struct StringLowering : public StringGathering {

// Imported string functions.
Name fromCharCodeArrayImport;
Name intoCharCodeArrayImport;
Name fromCodePointImport;
Name equalsImport;
Name compareImport;
Name lengthImport;
Name codePointAtImport;
Name substringImport;

// 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
Expand All @@ -269,6 +276,23 @@ struct StringLowering : public StringGathering {
module, "fromCharCodeArray", {nullArray16, Type::i32, Type::i32}, nnExt);
// string.fromCodePoint: codepoint -> ext
fromCodePointImport = addImport(module, "fromCodePoint", Type::i32, nnExt);
// string.intoCharCodeArray: string, array, start -> num written
intoCharCodeArrayImport = addImport(module,
"intoCharCodeArray",
{nullExt, nullArray16, Type::i32},
Type::i32);
// string.equals: string, string -> i32
equalsImport = addImport(module, "equals", {nullExt, nullExt}, Type::i32);
// string.compare: string, string -> i32
compareImport = addImport(module, "compare", {nullExt, nullExt}, Type::i32);
// string.length: string -> i32
lengthImport = addImport(module, "length", nullExt, Type::i32);
// string.codePointAt: string, offset -> i32
codePointAtImport =
addImport(module, "codePointAt", {nullExt, Type::i32}, Type::i32);
// string.substring: string, start, end -> string
substringImport =
addImport(module, "substring", {nullExt, Type::i32, Type::i32}, nnExt);

// Replace the string instructions in parallel.
struct Replacer : public WalkerPass<PostWalker<Replacer>> {
Expand Down Expand Up @@ -304,6 +328,66 @@ struct StringLowering : public StringGathering {
// strings: they are all just JS strings, so no conversion is needed.
replaceCurrent(curr->ref);
}

void visitStringEncode(StringEncode* curr) {
Builder builder(*getModule());
switch (curr->op) {
case StringEncodeWTF16Array:
replaceCurrent(builder.makeCall(lowering.intoCharCodeArrayImport,
{curr->ref, curr->ptr, curr->start},
Type::i32));
return;
default:
WASM_UNREACHABLE("TODO: all of string.encode*");
}
}

void visitStringEq(StringEq* curr) {
Builder builder(*getModule());
switch (curr->op) {
case StringEqEqual:
replaceCurrent(builder.makeCall(
lowering.equalsImport, {curr->left, curr->right}, Type::i32));
return;
case StringEqCompare:
replaceCurrent(builder.makeCall(
lowering.compareImport, {curr->left, curr->right}, Type::i32));
return;
default:
WASM_UNREACHABLE("invalid string.eq*");
}
}

void visitStringMeasure(StringMeasure* curr) {
Builder builder(*getModule());
switch (curr->op) {
case StringMeasureWTF16View:
replaceCurrent(
builder.makeCall(lowering.lengthImport, {curr->ref}, Type::i32));
return;
default:
WASM_UNREACHABLE("invalid string.measure*");
}
}

void visitStringWTF16Get(StringWTF16Get* curr) {
Builder builder(*getModule());
replaceCurrent(builder.makeCall(
lowering.codePointAtImport, {curr->ref, curr->pos}, Type::i32));
}

void visitStringSliceWTF(StringSliceWTF* curr) {
Builder builder(*getModule());
switch (curr->op) {
case StringSliceWTF16:
replaceCurrent(builder.makeCall(lowering.substringImport,
{curr->ref, curr->start, curr->end},
lowering.nnExt));
return;
default:
WASM_UNREACHABLE("TODO: all string.slice*");
}
}
};

Replacer replacer(*this);
Expand Down
64 changes: 54 additions & 10 deletions test/lit/passes/string-gathering.wast
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,43 @@
;; CHECK: (global $global2 stringref (global.get $string.const_bar))
;; LOWER: (type $0 (func))

;; LOWER: (type $1 (array (mut i16)))
;; LOWER: (type $1 (func (param externref externref) (result i32)))

;; LOWER: (type $2 (func (param (ref null $1) i32 i32) (result (ref extern))))
;; LOWER: (type $2 (array (mut i16)))

;; LOWER: (type $3 (func (param i32) (result (ref extern))))
;; LOWER: (type $3 (func (param (ref null $2) i32 i32) (result (ref extern))))

;; LOWER: (type $4 (func (param i32) (result (ref extern))))

;; LOWER: (type $5 (func (param externref (ref null $2) i32) (result i32)))

;; LOWER: (type $6 (func (param externref) (result i32)))

;; LOWER: (type $7 (func (param externref i32) (result i32)))

;; LOWER: (type $8 (func (param externref i32 i32) (result (ref extern))))

;; LOWER: (import "string.const" "0" (global $string.const_bar (ref extern)))

;; LOWER: (import "string.const" "1" (global $string.const_other (ref extern)))

;; LOWER: (import "string.const" "2" (global $global (ref extern)))

;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $2) (param (ref null $1) i32 i32) (result (ref extern))))
;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $3) (param (ref null $2) i32 i32) (result (ref extern))))

;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $3) (param i32) (result (ref extern))))
;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $4) (param i32) (result (ref extern))))

;; LOWER: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $5) (param externref (ref null $2) i32) (result i32)))

;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32)))

;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32)))

;; LOWER: (import "wasm:js-string" "length" (func $length (type $6) (param externref) (result i32)))

;; LOWER: (import "wasm:js-string" "codePointAt" (func $codePointAt (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: (global $global2 externref (global.get $string.const_bar))
(global $global2 (ref null string) (string.const "bar"))
Expand Down Expand Up @@ -121,11 +143,21 @@
;; Multiple possible reusable globals. Also test ignoring of imports.
(module
;; CHECK: (import "a" "b" (global $import (ref string)))
;; LOWER: (type $0 (array (mut i16)))
;; LOWER: (type $0 (func (param externref externref) (result i32)))

;; LOWER: (type $1 (func (param (ref null $0) i32 i32) (result (ref extern))))
;; LOWER: (type $1 (array (mut i16)))

;; LOWER: (type $2 (func (param (ref null $1) i32 i32) (result (ref extern))))

;; LOWER: (type $3 (func (param i32) (result (ref extern))))

;; LOWER: (type $4 (func (param externref (ref null $1) i32) (result i32)))

;; LOWER: (type $5 (func (param externref) (result i32)))

;; LOWER: (type $6 (func (param externref i32) (result i32)))

;; LOWER: (type $2 (func (param i32) (result (ref extern))))
;; LOWER: (type $7 (func (param externref i32 i32) (result (ref extern))))

;; LOWER: (import "a" "b" (global $import (ref extern)))
(import "a" "b" (global $import (ref string)))
Expand All @@ -138,9 +170,21 @@

;; LOWER: (import "string.const" "1" (global $global4 (ref extern)))

;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $1) (param (ref null $0) i32 i32) (result (ref extern))))
;; LOWER: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $2) (param (ref null $1) i32 i32) (result (ref extern))))

;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $3) (param i32) (result (ref extern))))

;; LOWER: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $4) (param externref (ref null $1) i32) (result i32)))

;; LOWER: (import "wasm:js-string" "equals" (func $equals (type $0) (param externref externref) (result i32)))

;; LOWER: (import "wasm:js-string" "compare" (func $compare (type $0) (param externref externref) (result i32)))

;; LOWER: (import "wasm:js-string" "length" (func $length (type $5) (param externref) (result i32)))

;; LOWER: (import "wasm:js-string" "codePointAt" (func $codePointAt (type $6) (param externref i32) (result i32)))

;; LOWER: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint (type $2) (param i32) (result (ref extern))))
;; LOWER: (import "wasm:js-string" "substring" (func $substring (type $7) (param externref i32 i32) (result (ref extern))))

;; LOWER: (global $global2 (ref extern) (global.get $global1))
(global $global2 (ref string) (string.const "foo"))
Expand Down
150 changes: 129 additions & 21 deletions test/lit/passes/string-lowering-instructions.wast
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,60 @@
;; RUN: foreach %s %t wasm-opt --string-lowering -all -S -o - | filecheck %s

(module
;; CHECK: (type $0 (func))

;; CHECK: (type $array16 (array (mut i16)))
(type $array16 (array (mut i16)))

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

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

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

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

;; CHECK: (type $5 (func (param externref (ref $array16)) (result i32)))

;; CHECK: (type $6 (func (result externref)))

;; CHECK: (type $7 (func (param (ref $array16))))

;; CHECK: (type $3 (func (param externref externref externref externref)))
;; CHECK: (type $8 (func (param externref externref externref externref)))

;; CHECK: (type $4 (func (param (ref null $array16) i32 i32) (result (ref extern))))
;; CHECK: (type $9 (func))

;; CHECK: (type $5 (func (param i32) (result (ref extern))))
;; CHECK: (type $10 (func (param (ref null $array16) i32 i32) (result (ref extern))))

;; CHECK: (import "colliding" "name" (func $fromCodePoint (type $0)))
;; CHECK: (type $11 (func (param i32) (result (ref extern))))

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

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

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

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

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

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

;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint_11 (type $11) (param i32) (result (ref extern))))

;; CHECK: (import "wasm:js-string" "intoCharCodeArray" (func $intoCharCodeArray (type $12) (param externref (ref null $array16) i32) (result i32)))

;; CHECK: (import "wasm:js-string" "fromCodePoint" (func $fromCodePoint_5 (type $5) (param i32) (result (ref extern))))
;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $1) (param externref externref) (result i32)))

;; CHECK: (func $string.as (type $3) (param $a externref) (param $b externref) (param $c externref) (param $d externref)
;; CHECK: (import "wasm:js-string" "compare" (func $compare (type $1) (param externref externref) (result i32)))

;; CHECK: (import "wasm:js-string" "length" (func $length (type $13) (param externref) (result i32)))

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

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

;; CHECK: (func $string.as (type $8) (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 @@ -59,7 +91,7 @@
)
)

;; CHECK: (func $string.new.gc (type $2) (param $array16 (ref $array16))
;; CHECK: (func $string.new.gc (type $7) (param $array16 (ref $array16))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $fromCharCodeArray
;; CHECK-NEXT: (local.get $array16)
Expand All @@ -78,18 +110,94 @@
)
)

;; CHECK: (func $string.from_code_point (type $0)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (call $fromCodePoint_5
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK: (func $string.from_code_point (type $6) (result externref)
;; CHECK-NEXT: (call $fromCodePoint_11
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $string.from_code_point
(drop
(string.from_code_point
(i32.const 1)
)
(func $string.from_code_point (result stringref)
(string.from_code_point
(i32.const 1)
)
)

;; CHECK: (func $string.encode (type $5) (param $ref externref) (param $array16 (ref $array16)) (result i32)
;; CHECK-NEXT: (call $intoCharCodeArray
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (local.get $array16)
;; CHECK-NEXT: (i32.const 10)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $string.encode (param $ref stringref) (param $array16 (ref $array16)) (result i32)
(string.encode_wtf16_array
(local.get $ref)
(local.get $array16)
(i32.const 10)
)
)

;; CHECK: (func $string.eq (type $4) (param $a externref) (param $b externref) (result i32)
;; CHECK-NEXT: (call $equals
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (local.get $b)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $string.eq (param $a stringref) (param $b stringref) (result i32)
(string.eq
(local.get $a)
(local.get $b)
)
)

;; CHECK: (func $string.compare (type $4) (param $a externref) (param $b externref) (result i32)
;; CHECK-NEXT: (call $compare
;; CHECK-NEXT: (local.get $a)
;; CHECK-NEXT: (local.get $b)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $string.compare (param $a stringref) (param $b stringref) (result i32)
(string.compare
(local.get $a)
(local.get $b)
)
)

;; CHECK: (func $string.length (type $3) (param $ref externref) (result i32)
;; CHECK-NEXT: (call $length
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $string.length (param $ref stringview_wtf16) (result i32)
(stringview_wtf16.length
(local.get $ref)
)
)

;; CHECK: (func $string.get_codeunit (type $3) (param $ref externref) (result i32)
;; CHECK-NEXT: (call $codePointAt
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $string.get_codeunit (param $ref stringview_wtf16) (result i32)
(stringview_wtf16.get_codeunit
(local.get $ref)
(i32.const 2)
)
)

;; CHECK: (func $string.slice (type $2) (param $ref externref) (result externref)
;; CHECK-NEXT: (call $substring
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 2)
;; CHECK-NEXT: (i32.const 3)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $string.slice (param $ref stringview_wtf16) (result stringref)
(stringview_wtf16.slice
(local.get $ref)
(i32.const 2)
(i32.const 3)
)
)
)

0 comments on commit 0e6c012

Please sign in to comment.