Skip to content

Use exactness to optimize GUFA cone depth #7538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
8 changes: 6 additions & 2 deletions src/ir/possible-contents.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ class PossibleContents {

// Internal convenience for creating a cone type of unbounded depth, i.e., the
// full cone of all subtypes for that type.
static ConeType FullConeType(Type type) { return ConeType{type, FullDepth}; }
static ConeType FullConeType(Type type) {
return type.isExact() ? ExactType(type) : ConeType{type, FullDepth};
}

template<typename T> PossibleContents(T value) : value(value) {}

Expand Down Expand Up @@ -265,7 +267,9 @@ class PossibleContents {

// Returns whether the relevant cone for this, as computed by getCone(), is of
// full size, that is, includes all subtypes.
bool hasFullCone() const { return getCone().depth == FullDepth; }
bool hasFullCone() const {
return getCone().depth == (getType().isExact() ? 0 : FullDepth);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to change? It seems odd to consider a code of depth 0 as "full".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, otherwise a bunch of assertions expecting full cone types started failing. I think it makes sense to make cone creation and inspection consistent in treating depth-0 cones for exact types as "full," so I think this is a better solution than going and changing those assertions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to understand this better - which assertions were those?

Copy link
Member Author

@tlively tlively Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion at the top of PossibleContents::intersect.

// This does not yet handle all possible content.
assert(other.isFullConeType() || other.isLiteral() || other.isNone());

Looking deeper into PossibleContents::intersect and the other methods it calls, it's a little difficult to tell whether they do the right thing with exact types. I propose 1) moving forward with this change as-is to see if further fuzzing with exact types turns up any issues and 2) adding more possible-contents gtests covering exact types in a follow-up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that assert looks suspicious to me... I'd rather investigate that first, but I don't feel strongly.

If you want to land this quickly to unblock other stuff, how about doing the reverse: modify the assert to allow either a full cone or an exact type, so we don't need to modify hasFullCone? I can also take a look at the PossibleContents code if you want.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No rush to land this. I can reorder things to avoid getting blocked. I do think it would be weird if a value created by fullConeType did not satisfy hasFullCone or isFullCone, though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we should rename fullConeType probably. Really what it does now is create a cone based on the type. Just coneType might be enough.


// Returns whether this is a cone type and also is of full size. This differs
// from hasFullCone() in that the former can return true for a global, for
Expand Down
71 changes: 71 additions & 0 deletions test/lit/passes/gufa-cast-all-exact.wast
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,77 @@
)
)

(module
;; CHECK: (type $super (sub (struct)))
;; NO_CD: (type $super (sub (struct)))
(type $super (sub (struct)))
(type $sub (sub $super (struct)))

;; CHECK: (import "" "a" (global $exact-a (ref (exact $super))))
;; NO_CD: (import "" "a" (global $exact-a (ref (exact $super))))
(import "" "a" (global $exact-a (ref (exact $super))))
;; CHECK: (import "" "b" (global $exact-b (ref (exact $super))))
;; NO_CD: (import "" "b" (global $exact-b (ref (exact $super))))
(import "" "b" (global $exact-b (ref (exact $super))))
;; CHECK: (import "" "x" (global $x i32))
;; NO_CD: (import "" "x" (global $x i32))
(import "" "x" (global $x i32))

;; CHECK: (func $get-exact (type $1) (result (ref $super))
;; CHECK-NEXT: (select (result (ref (exact $super)))
;; CHECK-NEXT: (global.get $exact-a)
;; CHECK-NEXT: (global.get $exact-b)
;; CHECK-NEXT: (global.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NO_CD: (func $get-exact (type $1) (result (ref $super))
;; NO_CD-NEXT: (select (result (ref (exact $super)))
;; NO_CD-NEXT: (global.get $exact-a)
;; NO_CD-NEXT: (global.get $exact-b)
;; NO_CD-NEXT: (global.get $x)
;; NO_CD-NEXT: )
;; NO_CD-NEXT: )
(func $get-exact (result (ref $super))
(select (result (ref (exact $super)))
(global.get $exact-a)
(global.get $exact-b)
(global.get $x)
)
)

;; CHECK: (func $cast-sub (type $2)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast (ref (exact $super))
;; CHECK-NEXT: (call $get-exact)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NO_CD: (func $cast-sub (type $2)
;; NO_CD-NEXT: (drop
;; NO_CD-NEXT: (block
;; NO_CD-NEXT: (drop
;; NO_CD-NEXT: (call $get-exact)
;; NO_CD-NEXT: )
;; NO_CD-NEXT: (unreachable)
;; NO_CD-NEXT: )
;; NO_CD-NEXT: )
;; NO_CD-NEXT: )
(func $cast-sub
(drop
;; We should be able to infer that this cast will not succeed and insert an
;; unreachable after it.
(ref.cast (ref $sub)
(call $get-exact)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, why doesn't this work without exact types? GUFA's depth zero cone should have had enough information already.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the sources are imports, not allocations, so without the new changes, GUFA doesn't know that it can use 0-depth cones for those imports.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, thanks!

)
)
)
)

(module
;; CHECK: (type $foo (sub (struct (field i32))))
;; NO_CD: (type $foo (sub (struct (field i32))))
Expand Down
Loading