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
43 changes: 5 additions & 38 deletions src/passes/Unsubtyping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,6 @@ struct Unsubtyping : Pass {
}
if (HeapType::isSubType(*oldSuper, super)) {
// sub <: oldSuper <: super
processDescribed(sub, *oldSuper, super);
noteSubtype(*oldSuper, super);
// We already handled sub <: oldSuper, so we're done.
return;
Expand All @@ -535,7 +534,6 @@ struct Unsubtyping : Pass {
// super will already be in the same tree when we process them below, so
// when we process casts we will know that we only need to process up to
// oldSuper.
processDescribed(sub, super, *oldSuper);
process(super, *oldSuper);
}

Expand All @@ -547,42 +545,6 @@ struct Unsubtyping : Pass {
processCasts(sub, super, oldSuper);
}

void processDescribed(HeapType sub, HeapType mid, HeapType super) {
// We are establishing sub <: mid <: super. If super describes the immediate
// supertype of the type sub describes, then once we insert mid between them
// we would have this:
//
// A -> super
// ^ ^
// | mid
// | ^
// C -> sub
//
// This violates the requirement that the descriptor of C's immediate
// supertype must be the immediate supertype of C's descriptor. To fix it,
// we have to find the type B that mid describes and insert it between A and
// C:
//
// A -> super
// ^ ^
// B -> mid
// ^ ^
// C -> sub
//
// We do this eagerly before we establish sub <: mid <: super so that if
// establishing that subtyping requires recursively establishing other
// subtypings, we can depend on the invariant that the described types are
// always set up correctly beforehand.
auto subDescribed = sub.getDescribedType();
auto superDescribed = super.getDescribedType();
if (subDescribed && superDescribed &&
types.getSupertype(*subDescribed) == superDescribed) {
auto midDescribed = mid.getDescribedType();
assert(midDescribed);
process(*subDescribed, *midDescribed);
}
}

void processDefinitions(HeapType sub, HeapType super) {
if (super.isBasic()) {
return;
Expand Down Expand Up @@ -618,6 +580,11 @@ struct Unsubtyping : Pass {
noteSubtype(*desc, *superDesc);
}
}
if (auto desc = sub.getDescribedType()) {
if (auto superDesc = super.getDescribedType()) {
noteSubtype(*desc, *superDesc);
}
}
}

void
Expand Down
78 changes: 75 additions & 3 deletions test/lit/passes/unsubtyping-desc.wast
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@
(type $A (sub (descriptor $A.desc (struct))))
;; CHECK: (type $A.desc (sub (describes $A (struct))))
(type $A.desc (sub (describes $A (struct))))
;; CHECK: (type $B (sub (descriptor $B.desc (struct))))
;; CHECK: (type $B (sub $A (descriptor $B.desc (struct))))
(type $B (sub $A (descriptor $B.desc (struct))))
;; CHECK: (type $B.desc (sub $A.desc (describes $B (struct))))
(type $B.desc (sub $A.desc (describes $B (struct))))
)

;; Now we directly require B.desc <: A.desc. This does *not* imply B <: A, so
;; we can optimize $B (but not $B.desc).
;; Now we require B.desc <: A.desc, which similarly implies B <: A.
;; CHECK: (global $B.desc (ref null $B.desc) (ref.null none))
(global $B.desc (ref null $B.desc) (ref.null none))
;; CHECK: (global $A.desc (ref null $A.desc) (global.get $B.desc))
Expand Down Expand Up @@ -134,3 +133,76 @@
;; CHECK: (global $bot-mid-desc (ref null $mid.desc) (struct.new_default $bot.desc))
(global $bot-mid-desc (ref null $mid.desc) (struct.new $bot.desc))
)

(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct))))
(type $top (sub (descriptor $top.desc (struct))))
;; CHECK: (type $mid (sub $top (descriptor $mid.desc (struct))))
(type $mid (sub $top (descriptor $mid.desc (struct))))
;; CHECK: (type $bot (sub $mid (descriptor $bot.desc (struct))))
(type $bot (sub $mid (descriptor $bot.desc (struct))))
;; CHECK: (type $top.desc (sub (describes $top (struct))))
(type $top.desc (sub (describes $top (struct))))
;; CHECK: (type $mid.desc (sub $top.desc (describes $mid (struct))))
(type $mid.desc (sub $top.desc (describes $mid (struct))))
;; CHECK: (type $bot.desc (sub $mid.desc (describes $bot (struct))))
(type $bot.desc (sub $mid.desc (describes $bot (struct))))
)

;; Now go the other direction:
;;
;; top ---> top.desc
;; ^
;; mid -> mid.desc |(2)
;; ^ (1) |
;; bot ---> bot.desc
;;
;; bot.desc <: top.desc implies bot <: top, but we already have bot <: mid, so
;; that gives us bot <: mid <: top. This is only valid if we also have
;; bot.desc <: mid.desc <: top.desc.

;; CHECK: (global $bot-mid (ref null $mid) (struct.new_default $bot
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: ))
(global $bot-mid (ref null $mid) (struct.new $bot (ref.null none)))
;; CHECK: (global $bot-top-desc (ref null $top.desc) (struct.new_default $bot.desc))
(global $bot-top-desc (ref null $top.desc) (struct.new $bot.desc))
)

(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct))))
(type $top (sub (descriptor $top.desc (struct))))
;; CHECK: (type $mid (sub $top (descriptor $mid.desc (struct))))
(type $mid (sub $top (descriptor $mid.desc (struct))))
;; CHECK: (type $bot (sub $mid (descriptor $bot.desc (struct))))
(type $bot (sub $mid (descriptor $bot.desc (struct))))
;; CHECK: (type $top.desc (sub (describes $top (struct))))
(type $top.desc (sub (describes $top (struct))))
;; CHECK: (type $mid.desc (sub $top.desc (describes $mid (struct))))
(type $mid.desc (sub $top.desc (describes $mid (struct))))
;; CHECK: (type $bot.desc (sub $mid.desc (describes $bot (struct))))
(type $bot.desc (sub $mid.desc (describes $bot (struct))))
)

;; Same as above, but the order of the initial subtypings is reversed.
;;
;; top ---> top.desc
;; ^
;; mid -> mid.desc |(1)
;; ^ (2) |
;; bot ---> bot.desc
;;
;; bot.desc <: top.desc implies bot <: top. When we add bot <: mid, that gives
;; us bot <: mid <: top. This is only valid if we also have
;; bot.desc <: mid.desc <: top.desc.
;; CHECK: (global $bot-top-desc (ref null $top.desc) (struct.new_default $bot.desc))
(global $bot-top-desc (ref null $top.desc) (struct.new $bot.desc))
;; CHECK: (global $bot-mid (ref null $mid) (struct.new_default $bot
;; CHECK-NEXT: (ref.null none)
;; CHECK-NEXT: ))
(global $bot-mid (ref null $mid) (struct.new $bot (ref.null none)))
)
Loading