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
25 changes: 17 additions & 8 deletions src/ir/possible-contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,9 @@ void PossibleContents::intersect(const PossibleContents& other) {
// Note the global's information, if we started as a global. In that case, the
// code below will refine our type but we can remain a global, which we will
// accomplish by restoring our global status at the end.
std::optional<Name> globalName;
std::optional<GlobalInfo> global;
if (isGlobal()) {
globalName = getGlobal();
global = getGlobal();
}

if (hasFullCone() && other.hasFullCone()) {
Expand All @@ -230,9 +230,9 @@ void PossibleContents::intersect(const PossibleContents& other) {
value = ConeType{newType, std::min(newDepth, otherNewDepth)};
}

if (globalName) {
if (global) {
// Restore the global but keep the new and refined type.
value = GlobalInfo{*globalName, getType()};
value = GlobalInfo{global->name, global->kind, getType()};
}
}

Expand Down Expand Up @@ -641,9 +641,17 @@ struct InfoCollector
addRoot(curr);
}
void visitRefFunc(RefFunc* curr) {
addRoot(curr,
PossibleContents::literal(
Literal::makeFunc(curr->func, curr->type.getHeapType())));
if (!getModule()->getFunction(curr->func)->imported()) {
// This is not imported, so we know the exact function literal.
addRoot(curr,
PossibleContents::literal(
Literal::makeFunc(curr->func, curr->type.getHeapType())));
} else {
// This is imported, so it is effectively a global.
addRoot(curr,
PossibleContents::global(
curr->func, ExternalKind::Function, curr->type));
}

// The presence of a RefFunc indicates the function may be called
// indirectly, so add the relevant connections for this particular function.
Expand Down Expand Up @@ -2836,7 +2844,8 @@ void Flower::filterGlobalContents(PossibleContents& contents,
// a cone/exact type *and* that something is equal to a global, in some
// cases. See https://github.com/WebAssembly/binaryen/pull/5083
if (contents.isMany() || contents.isConeType()) {
contents = PossibleContents::global(global->name, global->type);
contents = PossibleContents::global(
global->name, ExternalKind::Global, global->type);

// TODO: We could do better here, to set global->init->type instead of
// global->type, or even the contents.getType() - either of those
Expand Down
36 changes: 24 additions & 12 deletions src/ir/possible-contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ namespace wasm {
//
// * Literal: One possible constant value like an i32 of 42.
//
// * Global: The name of a global whose value is here. We do not know
// the actual value at compile time, but we know it is equal
// to that global. Typically we can only infer this for
// immutable globals.
// * Global: An immutable global value, something who we can identify
// but do not know the actual value of at runtime. This can
// be either a wasm (immutable) Global, or an imported wasm
// Function (which is effectively the same: we can refer to
// it, but do not know what is being imported there).
//
// * ConeType: Any possible value of a particular type, and a possible
// "cone" of a certain depth below it. If the depth is 0
Expand Down Expand Up @@ -85,6 +86,7 @@ class PossibleContents {

struct GlobalInfo {
Name name;
ExternalKind kind;
// The type of contents. Note that this may not match the type of the
// global, if we were filtered. For example:
//
Expand All @@ -99,7 +101,7 @@ class PossibleContents {
// way. In principle, not having depth info can lead to loss of
// precision.
bool operator==(const GlobalInfo& other) const {
return name == other.name && type == other.type;
return name == other.name && kind == other.kind && type == other.type;
}
};

Expand Down Expand Up @@ -144,8 +146,8 @@ class PossibleContents {

static PossibleContents none() { return PossibleContents{None()}; }
static PossibleContents literal(Literal c) { return PossibleContents{c}; }
static PossibleContents global(Name name, Type type) {
return PossibleContents{GlobalInfo{name, type}};
static PossibleContents global(Name name, ExternalKind kind, Type type) {
return PossibleContents{GlobalInfo{name, kind, type}};
}
// Helper for a cone type with depth 0, i.e., an exact type.
static PossibleContents exactType(Type type) {
Expand Down Expand Up @@ -216,9 +218,9 @@ class PossibleContents {
return std::get<Literal>(value);
}

Name getGlobal() const {
GlobalInfo getGlobal() const {
assert(isGlobal());
return std::get<GlobalInfo>(value).name;
return std::get<GlobalInfo>(value);
}

bool isNull() const { return isLiteral() && getLiteral().isNull(); }
Expand Down Expand Up @@ -316,11 +318,18 @@ class PossibleContents {
if (isLiteral()) {
return builder.makeConstantExpression(getLiteral());
} else {
auto name = getGlobal();
auto info = getGlobal();
// Note that we load the type from the module, rather than use the type
// in the GlobalInfo, as that type may not match the global (see comment
// in the GlobalInfo declaration above).
return builder.makeGlobalGet(name, wasm.getGlobal(name)->type);
if (info.kind == ExternalKind::Global) {
return builder.makeGlobalGet(info.name,
wasm.getGlobal(info.name)->type);
} else {
assert(info.kind == ExternalKind::Function);
return builder.makeRefFunc(
info.name, wasm.getFunction(info.name)->type.getHeapType());
}
}
}

Expand Down Expand Up @@ -352,6 +361,7 @@ class PossibleContents {
rehash(ret, getLiteral());
} else if (auto* global = std::get_if<GlobalInfo>(&value)) {
rehash(ret, global->name);
rehash(ret, global->kind);
rehash(ret, global->type);
} else if (auto* coneType = std::get_if<ConeType>(&value)) {
rehash(ret, coneType->type);
Expand All @@ -374,7 +384,9 @@ class PossibleContents {
o << " HT: " << h;
}
} else if (isGlobal()) {
o << "GlobalInfo $" << getGlobal() << " T: " << getType();
auto info = getGlobal();
o << "GlobalInfo $" << info.name << " K: " << int(info.kind)
<< " T: " << getType();
} else if (auto* coneType = std::get_if<ConeType>(&value)) {
auto t = coneType->type;
o << "ConeType " << t;
Expand Down
71 changes: 61 additions & 10 deletions test/gtest/possible-contents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,22 @@ class PossibleContentsTest : public testing::Test {
PossibleContents::literal(Literal::makeNull(HeapType::i31));

PossibleContents i32Global1 =
PossibleContents::global("i32Global1", Type::i32);
PossibleContents::global("i32Global1", ExternalKind::Global, Type::i32);
PossibleContents i32Global2 =
PossibleContents::global("i32Global2", Type::i32);
PossibleContents f64Global = PossibleContents::global("f64Global", Type::f64);
PossibleContents anyGlobal = PossibleContents::global("anyGlobal", anyref);
PossibleContents funcGlobal = PossibleContents::global("funcGlobal", funcref);
PossibleContents nonNullFuncGlobal =
PossibleContents::global("funcGlobal", Type(HeapType::func, NonNullable));
PossibleContents::global("i32Global2", ExternalKind::Global, Type::i32);
PossibleContents f64Global =
PossibleContents::global("f64Global", ExternalKind::Global, Type::f64);
PossibleContents anyGlobal =
PossibleContents::global("anyGlobal", ExternalKind::Global, anyref);
PossibleContents funcGlobal =
PossibleContents::global("funcGlobal", ExternalKind::Global, funcref);
PossibleContents nonNullFuncGlobal = PossibleContents::global(
"funcGlobal", ExternalKind::Global, Type(HeapType::func, NonNullable));

PossibleContents importedFunc1 = PossibleContents::global(
"impfunc1", ExternalKind::Function, Type(HeapType::func, NonNullable));
PossibleContents importedFunc2 = PossibleContents::global(
"impfunc2", ExternalKind::Function, Type(HeapType::func, NonNullable));

PossibleContents nonNullFunc = PossibleContents::literal(
Literal::makeFunc("func", Signature(Type::none, Type::none)));
Expand All @@ -114,6 +122,8 @@ class PossibleContentsTest : public testing::Test {
PossibleContents coneAnyref = PossibleContents::coneType(anyref);
PossibleContents coneFuncref = PossibleContents::coneType(funcref);
PossibleContents coneFuncref1 = PossibleContents::coneType(funcref, 1);
PossibleContents coneNonNullFuncref =
PossibleContents::coneType(Type(HeapType::func, NonNullable));
};

TEST_F(PossibleContentsTest, TestComparisons) {
Expand All @@ -135,6 +145,9 @@ TEST_F(PossibleContentsTest, TestComparisons) {
assertNotEqualSymmetric(i32Global1, exactI32);
assertNotEqualSymmetric(i32Global1, many);

assertEqualSymmetric(importedFunc1, importedFunc1);
assertNotEqualSymmetric(importedFunc1, importedFunc2);

assertEqualSymmetric(exactI32, exactI32);
assertNotEqualSymmetric(exactI32, exactAnyref);
assertNotEqualSymmetric(exactI32, many);
Expand All @@ -151,6 +164,23 @@ TEST_F(PossibleContentsTest, TestComparisons) {
assertNotEqualSymmetric(exactNonNullAnyref, exactAnyref);
}

TEST_F(PossibleContentsTest, TestComparisonsGlobals) {
// Check if two PossibleContents::global, one a wasm Global and one a wasm
// Function, and equal in their names and types, are still understood to be
// non-equal: the |kind| field differentiates them.

PossibleContents wasmGlobal = PossibleContents::global(
"foo", ExternalKind::Global, Type(HeapType::func, NonNullable));
PossibleContents wasmFunction = PossibleContents::global(
"foo", ExternalKind::Function, Type(HeapType::func, NonNullable));

assertNotEqualSymmetric(wasmGlobal, wasmFunction);

// But they are equal to themselves, of course.
assertEqualSymmetric(wasmGlobal, wasmGlobal);
assertEqualSymmetric(wasmFunction, wasmFunction);
}

TEST_F(PossibleContentsTest, TestHash) {
// Hashes should be deterministic.
EXPECT_EQ(none.hash(), none.hash());
Expand Down Expand Up @@ -257,6 +287,10 @@ TEST_F(PossibleContentsTest, TestCombinations) {

assertCombination(anyGlobal, anyNull, coneAnyref);
assertCombination(anyGlobal, i31Null, coneAnyref);

// Imported functions.
assertCombination(importedFunc1, importedFunc1, importedFunc1);
assertCombination(importedFunc1, importedFunc2, coneNonNullFuncref);
}

static PassOptions options;
Expand Down Expand Up @@ -338,6 +372,13 @@ TEST_F(PossibleContentsTest, TestIntersection) {

// Separate hierarchies.
assertLackIntersection(funcGlobal, anyGlobal);

// Imported functions.
assertHaveIntersection(importedFunc1, importedFunc1);
assertHaveIntersection(importedFunc1, exactFuncSignatureType);
assertHaveIntersection(importedFunc1, exactNonNullFuncSignatureType);
assertHaveIntersection(importedFunc1, importedFunc2);
assertHaveIntersection(importedFunc1, funcGlobal);
}

TEST_F(PossibleContentsTest, TestIntersectWithCombinations) {
Expand Down Expand Up @@ -484,6 +525,8 @@ TEST_F(PossibleContentsTest, TestIntersectWithCombinations) {
funcGlobal,
nonNullFuncGlobal,
nonNullFunc,
importedFunc1,
importedFunc2,
exactI32,
exactAnyref,
exactFuncref,
Expand Down Expand Up @@ -785,9 +828,10 @@ TEST_F(PossibleContentsTest, TestStructCones) {
nonNullFunc, PossibleContents::coneType(signature), nonNullFunc);

// Filter a global to a more specific type.
assertIntersection(funcGlobal,
PossibleContents::coneType(signature),
PossibleContents::global("funcGlobal", signature));
assertIntersection(
funcGlobal,
PossibleContents::coneType(signature),
PossibleContents::global("funcGlobal", ExternalKind::Global, signature));

// Filter a global's nullability only.
auto nonNullFuncRef = Type(HeapType::func, NonNullable);
Expand All @@ -814,6 +858,13 @@ TEST_F(PossibleContentsTest, TestStructCones) {
assertIntersection(funcGlobal, none, none);
assertIntersection(PossibleContents::coneType(signature), none, none);

// Imported functions. TODO: These are not yet supported, and assert instead.
// assertIntersection(
// importedFunc1, importedFunc1, importedFunc1);
// assertIntersection(
// importedFunc1, PossibleContents::coneType(nonNullFuncRef),
// importedFunc1);

// Subcontents. This API only supports the case where one of the inputs is a
// full cone type.
// First, compare exact types to such a cone.
Expand Down
31 changes: 31 additions & 0 deletions test/lit/passes/gufa.wast
Original file line number Diff line number Diff line change
Expand Up @@ -1154,3 +1154,34 @@
)
)
)

;; Imported functions can be inferred.
(module
;; CHECK: (type $0 (func))

;; CHECK: (type $1 (func (result funcref)))

;; CHECK: (import "" "" (func $f (type $0)))
(import "" "" (func $f))

;; CHECK: (elem declare func $f)

;; CHECK: (export "test" (func $test))

;; CHECK: (func $test (type $1) (result funcref)
;; CHECK-NEXT: (local $temp funcref)
;; CHECK-NEXT: (local.set $temp
;; CHECK-NEXT: (ref.func $f)
;; CHECK-NEXT: )
;; CHECK-NEXT: (ref.func $f)
;; CHECK-NEXT: )
(func $test (export "test") (result funcref)
(local $temp funcref)
(local.set $temp
(ref.func $f)
)
;; This will become a ref.func $f.
(local.get $temp)
)
)

Loading