Skip to content
Draft
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
24 changes: 24 additions & 0 deletions src/passes/TypeSSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ struct Analyzer
news.push_back(curr);
visitExpression(curr);
}
void visitRefFunc(RefFunc* curr) {
news.push_back(curr);
visitExpression(curr);
}

// Find casts to exact types. Allocations of these types will not be able to
// be optimized.
Expand Down Expand Up @@ -298,6 +302,10 @@ struct TypeSSA : public Pass {

News newsToModify;

// Track which functions we have already queued for modification, so we only
// create one new type per function, satisfying validation rules.
std::unordered_set<Name> processedFunctions;

// As we generate new names, use a consistent index.
Index nameCounter = 0;

Expand All @@ -308,6 +316,11 @@ struct TypeSSA : public Pass {
disallowed = disallowedTypes.contains(curr->type.getHeapType());
}
if (!disallowed && isInteresting(curr)) {
if (auto* refFunc = curr->dynCast<RefFunc>()) {
if (!processedFunctions.insert(refFunc->func).second) {
continue;
}
}
newsToModify.push_back(curr);
}
}
Expand Down Expand Up @@ -347,6 +360,8 @@ struct TypeSSA : public Pass {
builder[i] = oldType.getArray();
break;
case HeapTypeKind::Func:
builder[i] = oldType.getSignature();
break;
case HeapTypeKind::Cont:
case HeapTypeKind::Basic:
WASM_UNREACHABLE("unexpected kind");
Expand Down Expand Up @@ -380,6 +395,10 @@ struct TypeSSA : public Pass {
auto newType = newTypes[i];
curr->type = Type(newType, NonNullable, Exact);

if (auto* refFunc = curr->dynCast<RefFunc>()) {
module->getFunction(refFunc->func)->type = curr->type;
}

// If the old type has a nice name, make a nice name for the new one.
if (typeNames.contains(oldType)) {
auto intendedName = typeNames[oldType].name.toString() + '_' +
Expand Down Expand Up @@ -491,6 +510,11 @@ struct TypeSSA : public Pass {
}
}
return true;
} else if (curr->is<RefFunc>()) {
// Any ref.func is interesting to type-ssa as assigning it a unique
// type lets passes distinguish references to this function from others
// with the same signature.
return true;
} else {
WASM_UNREACHABLE("unknown new");
}
Expand Down
65 changes: 65 additions & 0 deletions test/lit/passes/global-effects-typessa-vacuum.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --closed-world --generate-global-effects --vacuum -S -o - | filecheck %s
;; RUN: foreach %s %t wasm-opt -all --closed-world --type-ssa --generate-global-effects --vacuum -S -o - | filecheck %s --check-prefix=TYPESSA

(module
;; CHECK: (type $sig (sub (func)))
;; TYPESSA: (type $sig (sub (func)))
(type $sig (sub (func)))

(table 1 1 funcref)
(elem (i32.const 0) $f2)

;; CHECK: (func $f1 (type $sig)
;; CHECK-NEXT: (nop)
;; CHECK-NEXT: )
;; TYPESSA: (func $f1 (type $sig_2)
;; TYPESSA-NEXT: (nop)
;; TYPESSA-NEXT: )
(func $f1 (type $sig)
(drop (i32.const 1))
)
;; CHECK: (func $f2 (type $sig)
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; TYPESSA: (func $f2 (type $sig_1)
;; TYPESSA-NEXT: (unreachable)
;; TYPESSA-NEXT: )
(func $f2 (type $sig)
(unreachable)
)

;; CHECK: (func $test-call_ref (type $1)
;; CHECK-NEXT: (call_ref $sig
;; CHECK-NEXT: (ref.func $f1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TYPESSA: (func $test-call_ref (type $3)
;; TYPESSA-NEXT: (nop)
;; TYPESSA-NEXT: )
(func $test-call_ref
(drop (ref.func $f2))
;; Without TypeSSA, this calls $f1 which has no effects, but GlobalEffects
;; sees it has type $sig, and $f2 is also taken and has type $sig.
;; With TypeSSA, this call_ref becomes typed as a unique subtype for $f1,
;; so it is known to have no side effects and is vacuumed!
(call_ref $sig (ref.func $f1))
)

;; CHECK: (func $test-call_indirect (type $2) (param $0 i32)
;; CHECK-NEXT: (call_indirect $0 (type $sig)
;; CHECK-NEXT: (local.get $0)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; TYPESSA: (func $test-call_indirect (type $4) (param $0 i32)
;; TYPESSA-NEXT: (call_indirect $0 (type $sig)
;; TYPESSA-NEXT: (local.get $0)
;; TYPESSA-NEXT: )
;; TYPESSA-NEXT: )
(func $test-call_indirect (param i32)
;; For call_indirect, TypeSSA doesn't change the signature of the call.
;; It will still target $sig. Since both unique subtypes for $f1 and $f2
;; are subtypes of $sig, it will still see the trapping effect of $f2.
(call_indirect (type $sig) (local.get 0))
)
)
43 changes: 43 additions & 0 deletions test/lit/passes/type-ssa-func.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --type-ssa -all -S -o - | filecheck %s

(module
;; CHECK: (type $sig (sub (func)))
(type $sig (sub (func)))

;; CHECK: (rec
;; CHECK-NEXT: (type $sig_1 (sub $sig (func)))

;; CHECK: (type $sig_2 (sub $sig (func)))

;; CHECK: (type $3 (func))

;; CHECK: (elem declare func $f1 $f2)

;; CHECK: (func $f1 (type $sig_1)
;; CHECK-NEXT: )
(func $f1 (type $sig)
)

;; CHECK: (func $f2 (type $sig_2)
;; CHECK-NEXT: )
(func $f2 (type $sig)
)

;; CHECK: (func $test (type $3)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.func $f1)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.func $f2)
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.func $f1)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $test
(drop (ref.func $f1))
(drop (ref.func $f2))
(drop (ref.func $f1))
)
)
Loading