Skip to content
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
7 changes: 6 additions & 1 deletion src/ir/type-updating.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ GlobalTypeRewriter::GlobalTypeRewriter(Module& wasm) : wasm(wasm) {}

void GlobalTypeRewriter::update() { mapTypes(rebuildTypes()); }

GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes() {
GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes(
const std::vector<HeapType>& additionalPrivateTypes) {
// Find the heap types that are not publicly observable. Even in a closed
// world scenario, don't modify public types because we assume that they may
// be reflected on or used for linking. Figure out where each private type
Expand All @@ -39,6 +40,10 @@ GlobalTypeRewriter::TypeMap GlobalTypeRewriter::rebuildTypes() {
Index i = 0;
auto privateTypes = ModuleUtils::getPrivateHeapTypes(wasm);

for (auto t : additionalPrivateTypes) {
privateTypes.push_back(t);
}

// Topological sort to have supertypes first, but we have to account for the
// fact that we may be replacing the supertypes to get the order correct.
struct SupertypesFirst
Expand Down
13 changes: 10 additions & 3 deletions src/ir/type-updating.h
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,12 @@ class GlobalTypeRewriter {
// Builds new types after updating their contents using the hooks below and
// returns a map from the old types to the modified types. Used internally in
// update().
TypeMap rebuildTypes();
//
// This only operates on private types (so as not to modify the module's
// external ABI). It takes as a parameter a list of public types to consider
// private, which allows more flexibility.
TypeMap
rebuildTypes(const std::vector<HeapType>& additionalPrivateTypes = {});

private:
TypeBuilder typeBuilder;
Expand All @@ -446,10 +451,12 @@ class TypeMapper : public GlobalTypeRewriter {
TypeMapper(Module& wasm, const TypeUpdates& mapping)
: GlobalTypeRewriter(wasm), mapping(mapping) {}

void map() {
// As rebuildTypes, this can take an optional set of additional types to
// consider private (and therefore to modify).
void map(const std::vector<HeapType>& additionalPrivateTypes = {}) {
// Update the internals of types (struct fields, signatures, etc.) to
// refer to the merged types.
auto newMapping = rebuildTypes();
auto newMapping = rebuildTypes(additionalPrivateTypes);

// Compose the user-provided mapping from old types to other old types with
// the new mapping from old types to new types. `newMapping` will become
Expand Down
16 changes: 15 additions & 1 deletion src/passes/StringLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,27 @@ struct StringLowering : public StringGathering {

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

// There is no difference between strings and views with imported strings:
// they are all just JS strings, so they all turn into externref.
updates[HeapType::string] = HeapType::ext;
updates[HeapType::stringview_wtf8] = HeapType::ext;
updates[HeapType::stringview_wtf16] = HeapType::ext;
updates[HeapType::stringview_iter] = HeapType::ext;
TypeMapper(*module, updates).map();

// We consider all types that use strings as modifiable, which means we
// mark them as non-private. 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);
std::vector<HeapType> stringUsers;
for (auto t : publicTypes) {
if (Type(t, Nullable).getFeatures().hasStrings()) {
stringUsers.push_back(t);
}
}

TypeMapper(*module, updates).map(stringUsers);
}

// Imported string functions.
Expand Down
104 changes: 73 additions & 31 deletions test/lit/passes/string-lowering-instructions.wast
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,62 @@
;; CHECK: (type $1 (func (param externref externref) (result i32)))

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

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

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

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

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

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

;; CHECK: (type $8 (func (param externref externref externref externref)))
;; CHECK: (type $8 (func (param (ref $array16))))

;; CHECK: (type $9 (func))
;; CHECK: (type $9 (func (param externref externref externref externref)))

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

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

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

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

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

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

;; CHECK: (import "colliding" "name" (func $fromCodePoint (type $9)))
;; CHECK: (type $16 (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 $10)))
(import "colliding" "name" (func $fromCodePoint))

;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $10) (param (ref null $array16) i32 i32) (result (ref extern))))
;; CHECK: (import "wasm:js-string" "fromCharCodeArray" (func $fromCharCodeArray (type $11) (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" "fromCodePoint" (func $fromCodePoint_13 (type $12) (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" "intoCharCodeArray" (func $intoCharCodeArray (type $13) (param externref (ref null $array16) i32) (result i32)))

;; CHECK: (import "wasm:js-string" "equals" (func $equals (type $1) (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" "length" (func $length (type $13) (param externref) (result i32)))
;; CHECK: (import "wasm:js-string" "length" (func $length (type $14) (param externref) (result i32)))

;; CHECK: (import "wasm:js-string" "codePointAt" (func $codePointAt (type $15) (param externref i32) (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 $16) (param externref i32 i32) (result (ref extern))))

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

;; CHECK: (func $string.as (type $8) (param $a externref) (param $b externref) (param $c externref) (param $d externref)
;; CHECK: (export "export.2" (func $exported-string-receiver))

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

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

;; CHECK: (func $string.from_code_point (type $6) (result externref)
;; CHECK-NEXT: (call $fromCodePoint_11
;; CHECK: (func $string.from_code_point (type $3) (result externref)
;; CHECK-NEXT: (call $fromCodePoint_13
;; CHECK-NEXT: (i32.const 1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
Expand All @@ -121,7 +129,7 @@
)
)

;; CHECK: (func $string.encode (type $5) (param $ref externref) (param $array16 (ref $array16)) (result i32)
;; CHECK: (func $string.encode (type $7) (param $ref externref) (param $array16 (ref $array16)) (result i32)
;; CHECK-NEXT: (call $intoCharCodeArray
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (local.get $array16)
Expand All @@ -136,7 +144,7 @@
)
)

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

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

;; CHECK: (func $string.length (type $3) (param $ref externref) (result i32)
;; CHECK: (func $string.length (type $5) (param $ref externref) (result i32)
;; CHECK-NEXT: (call $length
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: )
Expand All @@ -173,7 +181,7 @@
)
)

;; CHECK: (func $string.get_codeunit (type $3) (param $ref externref) (result i32)
;; CHECK: (func $string.get_codeunit (type $5) (param $ref externref) (result i32)
;; CHECK-NEXT: (call $codePointAt
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 2)
Expand All @@ -186,7 +194,7 @@
)
)

;; CHECK: (func $string.slice (type $2) (param $ref externref) (result externref)
;; CHECK: (func $string.slice (type $4) (param $ref externref) (result externref)
;; CHECK-NEXT: (call $substring
;; CHECK-NEXT: (local.get $ref)
;; CHECK-NEXT: (i32.const 2)
Expand All @@ -200,4 +208,38 @@
(i32.const 3)
)
)

;; CHECK: (func $exported-string-returner (type $3) (result externref)
;; CHECK-NEXT: (global.get $string.const_exported)
;; CHECK-NEXT: )
(func $exported-string-returner (export "export.1") (result stringref)
;; We should update the signature of this function even though it is public
;; (exported).
(string.const "exported")
)

;; CHECK: (func $exported-string-receiver (type $2) (param $x externref) (param $y i32) (param $z externref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $y)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $z)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $exported-string-receiver (export "export.2") (param $x stringref) (param $y i32) (param $z stringref)
;; We should update the signature of this function even though it is public
;; (exported).
(drop
(local.get $x)
)
(drop
(local.get $y)
)
(drop
(local.get $z)
)
)
)