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
8 changes: 5 additions & 3 deletions src/ir/subtype-exprs.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "ir/branch-utils.h"
#include "wasm-traversal.h"
#include "wasm-type.h"
#include "wasm.h"

namespace wasm {
Expand Down Expand Up @@ -52,8 +53,9 @@ namespace wasm {
// subtype of anothers, for example,
// a block and its last child.
//
// * noteCast(HeapType, HeapType) - A fixed type is cast to another, for
// example, in a CallIndirect.
// * noteCast(HeapType, Type) - A fixed type is cast to another, for example,
// in a CallIndirect. The destination is a Type
// rather than HeapType because it may be exact.
// * noteCast(Expression, Type) - An expression's type is cast to a fixed type,
// for example, in RefTest.
// * noteCast(Expression, Expression) - An expression's type is cast to
Expand Down Expand Up @@ -165,7 +167,7 @@ struct SubtypingDiscoverer : public OverriddenVisitor<SubType> {
// this is a trivial situation that is not worth optimizing.
self()->noteSubtype(tableType, curr->heapType);
} else if (HeapType::isSubType(curr->heapType, tableType)) {
self()->noteCast(tableType, curr->heapType);
self()->noteCast(tableType, Type(curr->heapType, NonNullable, Inexact));
} else {
// The types are unrelated and the cast will fail. We can keep the types
// unrelated.
Expand Down
14 changes: 11 additions & 3 deletions src/passes/Unsubtyping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,13 +599,21 @@ struct Unsubtyping : Pass {
// Otherwise, we must take this into account.
noteSubtype(sub, super);
}
void noteCast(HeapType src, HeapType dst) {
void noteCast(HeapType src, Type dstType) {
auto dst = dstType.getHeapType();
// Casts to self and casts that must fail because they have incompatible
// types are uninteresting.
if (dst == src) {
return;
}
if (HeapType::isSubType(dst, src)) {
if (dstType.isExact()) {
// This cast only tests that the exact destination type is a subtype
// of the source type and does not impose additional requirements on
// subtypes of the destination type like a normal cast does.
info.subtypings.insert({dst, src});
return;
}
info.casts.insert({src, dst});
return;
}
Expand All @@ -617,12 +625,12 @@ struct Unsubtyping : Pass {
}
void noteCast(Expression* src, Type dst) {
if (src->type.isRef() && dst.isRef()) {
noteCast(src->type.getHeapType(), dst.getHeapType());
noteCast(src->type.getHeapType(), dst);
}
}
void noteCast(Expression* src, Expression* dst) {
if (src->type.isRef() && dst->type.isRef()) {
noteCast(src->type.getHeapType(), dst->type.getHeapType());
noteCast(src->type.getHeapType(), dst->type);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/tools/fuzzing/fuzzing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1784,7 +1784,7 @@ void TranslateToFuzzReader::mutate(Function* func) {

// TODO: Many casts can accept the top type. We may need to use visit*(), to
// handle each expression class separately.
void noteCast(HeapType src, HeapType dst) {}
void noteCast(HeapType src, Type dst) {}
void noteCast(Expression* src, Type dst) {}
void noteCast(Expression* src, Expression* dst) {}
} finder;
Expand Down
101 changes: 101 additions & 0 deletions test/lit/passes/unsubtyping-casts.wast
Original file line number Diff line number Diff line change
Expand Up @@ -563,3 +563,104 @@
)
)
)

;; Exact casts do not impose requirements on subtypes of the destination type.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $top (sub (struct)))
(type $top (sub (struct)))
;; CHECK: (type $bot (sub (struct)))
(type $bot (sub $top (struct)))
)

;; CHECK: (type $2 (func (param anyref)))

;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot))
(global $bot-sub-any anyref (struct.new $bot))

;; CHECK: (func $ref.cast-exact (type $2) (param $any anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast (ref null (exact $top))
;; CHECK-NEXT: (local.get $any)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $ref.cast-exact (param $any anyref)
(drop
(ref.cast (ref null (exact $top))
(local.get $any)
)
)
)
)

;; Same, but now with a br_on_cast.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $top (sub (struct)))
(type $top (sub (struct)))
;; CHECK: (type $bot (sub (struct)))
(type $bot (sub $top (struct)))
)

;; CHECK: (type $2 (func (param anyref)))

;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot))
(global $bot-sub-any anyref (struct.new $bot))

;; CHECK: (func $br_on_cast-exact (type $2) (param $any anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $l (result anyref)
;; CHECK-NEXT: (br_on_cast $l anyref (ref (exact $top))
;; CHECK-NEXT: (local.get $any)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast-exact (param $any anyref)
(drop
(block $l (result anyref)
(br_on_cast $l anyref (ref (exact $top))
(local.get $any)
)
)
)
)
)

;; Same, but now with a br_on_cast_fail.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $top (sub (struct)))
(type $top (sub (struct)))
;; CHECK: (type $bot (sub (struct)))
(type $bot (sub $top (struct)))
)

;; CHECK: (type $2 (func (param anyref)))

;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot))
(global $bot-sub-any anyref (struct.new $bot))

;; CHECK: (func $br_on_cast_fail-exact (type $2) (param $any anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $l (result anyref)
;; CHECK-NEXT: (br_on_cast_fail $l anyref (ref (exact $top))
;; CHECK-NEXT: (local.get $any)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
(func $br_on_cast_fail-exact (param $any anyref)
(drop
(block $l (result anyref)
(br_on_cast_fail $l anyref (ref (exact $top))
(local.get $any)
)
)
)
)
)
39 changes: 15 additions & 24 deletions test/lit/passes/unsubtyping-desc.wast
Original file line number Diff line number Diff line change
Expand Up @@ -427,28 +427,25 @@

;; If the ref.cast_desc is exact, then it doesn't need to transitively require
;; any subtypings except that the cast destination is a subtype of the cast
;; source. TODO.
;; source.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct))))
(type $top (sub (descriptor $top.desc (struct))))
;; CHECK: (type $bot (sub $top (descriptor $bot.desc (struct))))
;; CHECK: (type $bot (sub (struct)))
(type $bot (sub $top (descriptor $bot.desc (struct))))
;; CHECK: (type $top.desc (sub (describes $top (struct))))
(type $top.desc (sub (describes $top (struct))))
;; CHECK: (type $bot.desc (sub $top.desc (describes $bot (struct))))
(type $bot.desc (sub $top.desc (describes $bot (struct))))
)

;; CHECK: (type $4 (func (param anyref (ref (exact $top.desc)))))
;; CHECK: (type $3 (func (param anyref (ref (exact $top.desc)))))

;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot
;; CHECK-NEXT: (struct.new_default $bot.desc)
;; CHECK-NEXT: ))
;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot))
(global $bot-sub-any anyref (struct.new $bot (struct.new $bot.desc)))

;; CHECK: (func $ref.cast_desc (type $4) (param $any anyref) (param $top.desc (ref (exact $top.desc)))
;; CHECK: (func $ref.cast_desc (type $3) (param $any anyref) (param $top.desc (ref (exact $top.desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast_desc (ref null (exact $top))
;; CHECK-NEXT: (local.get $any)
Expand Down Expand Up @@ -513,28 +510,25 @@

;; If the br_on_cast_desc is exact, then it doesn't need to transitively require
;; any subtypings except that the cast destination is a subtype of the cast
;; source. TODO.
;; source.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct))))
(type $top (sub (descriptor $top.desc (struct))))
;; CHECK: (type $bot (sub $top (descriptor $bot.desc (struct))))
;; CHECK: (type $bot (sub (struct)))
(type $bot (sub $top (descriptor $bot.desc (struct))))
;; CHECK: (type $top.desc (sub (describes $top (struct))))
(type $top.desc (sub (describes $top (struct))))
;; CHECK: (type $bot.desc (sub $top.desc (describes $bot (struct))))
(type $bot.desc (sub $top.desc (describes $bot (struct))))
)

;; CHECK: (type $4 (func (param anyref (ref (exact $top.desc)))))
;; CHECK: (type $3 (func (param anyref (ref (exact $top.desc)))))

;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot
;; CHECK-NEXT: (struct.new_default $bot.desc)
;; CHECK-NEXT: ))
;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot))
(global $bot-sub-any anyref (struct.new $bot (struct.new $bot.desc)))

;; CHECK: (func $br_on_cast_desc (type $4) (param $any anyref) (param $top.desc (ref (exact $top.desc)))
;; CHECK: (func $br_on_cast_desc (type $3) (param $any anyref) (param $top.desc (ref (exact $top.desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $l (result anyref)
;; CHECK-NEXT: (br_on_cast_desc $l anyref (ref null (exact $top))
Expand Down Expand Up @@ -603,28 +597,25 @@

;; If the br_on_cast_desc_fail is exact, then it doesn't need to transitively
;; require any subtypings except that the cast destination is a subtype of the
;; cast source. TODO.
;; cast source.
(module
(rec
;; CHECK: (rec
;; CHECK-NEXT: (type $top (sub (descriptor $top.desc (struct))))
(type $top (sub (descriptor $top.desc (struct))))
;; CHECK: (type $bot (sub $top (descriptor $bot.desc (struct))))
;; CHECK: (type $bot (sub (struct)))
(type $bot (sub $top (descriptor $bot.desc (struct))))
;; CHECK: (type $top.desc (sub (describes $top (struct))))
(type $top.desc (sub (describes $top (struct))))
;; CHECK: (type $bot.desc (sub $top.desc (describes $bot (struct))))
(type $bot.desc (sub $top.desc (describes $bot (struct))))
)

;; CHECK: (type $4 (func (param anyref (ref (exact $top.desc)))))
;; CHECK: (type $3 (func (param anyref (ref (exact $top.desc)))))

;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot
;; CHECK-NEXT: (struct.new_default $bot.desc)
;; CHECK-NEXT: ))
;; CHECK: (global $bot-sub-any anyref (struct.new_default $bot))
(global $bot-sub-any anyref (struct.new $bot (struct.new $bot.desc)))

;; CHECK: (func $br_on_cast_desc_fail (type $4) (param $any anyref) (param $top.desc (ref (exact $top.desc)))
;; CHECK: (func $br_on_cast_desc_fail (type $3) (param $any anyref) (param $top.desc (ref (exact $top.desc)))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $l (result anyref)
;; CHECK-NEXT: (br_on_cast_desc_fail $l anyref (ref null (exact $top))
Expand Down
Loading