[Custom Descriptors] Use placeholder describees in GTO#7888
Merged
Conversation
GTO tries to optimize out unused descriptor types, but sometimes cannot because the descriptor type must remain a descriptor to be a valid supertype of a subtype that will remain a descriptor. To optimize the original described type to not have a descriptor while simultaneously keeping the descriptor a valid supertype, insert a new, trivial placeholder type for the descriptor to describe. Since this new type is not otherwise used in the module, it should be able to be optimized out after subsequent rounds of unsubtyping and other optimizations.
kripken
approved these changes
Sep 8, 2025
| HeapType placeholder = Struct{}; | ||
| types.insert( | ||
| types.begin(), parent.descriptorsOfPlaceholders.size(), placeholder); | ||
| return types; |
Member
There was a problem hiding this comment.
I was thinking we need a new hook for this, but I guess using getSortedTypes works... it has to run before the rebuilding stage.
Co-authored-by: Alon Zakai <azakai@google.com>
tlively
commented
Sep 10, 2025
Member
|
Fuzzer found a bug: (module
(rec
(type $0 (sub (func)))
(type $1 (sub (descriptor $2 (struct (field f64) (field i32) (field (ref null $2)) (field (mut i8))))))
(type $2 (sub (describes $1 (struct (field (mut f32)) (field (ref null $0)) (field (mut f64))))))
(type $3 (sub (struct (field eqref))))
(type $4 (sub (array (mut f32))))
)
(type $5 (sub (struct (field f64))))
(rec
(type $6 (sub (array (mut (ref $11)))))
(type $7 (sub (func (param i32 i64 (ref $9) v128) (result (ref null $9)))))
(type $8 (sub final $7 (func (param i32 i64 arrayref v128) (result (ref null $9)))))
(type $9 (array (mut i31ref)))
(type $10 (sub $3 (struct (field (ref null $2)) (field (mut nullref)) (field f32) (field (mut f64)) (field (mut (ref null $12))) (field i64))))
(type $11 (sub final $5 (struct (field f64) (field (mut f32)) (field i32) (field i64) (field (mut i64)) (field i16))))
(type $12 (sub (func (param (ref null $6) v128) (result (ref $0)))))
)
(rec
(type $13 (sub $1 (descriptor $15 (struct (field f64) (field i32) (field (ref $15)) (field (mut i8)) (field (ref null $18))))))
(type $14 (sub final $13 (descriptor $16 (struct (field f64) (field i32) (field (ref $16)) (field (mut i8)) (field nullfuncref) (field (mut (ref eq)))))))
(type $15 (sub $2 (describes $13 (struct (field (mut f32)) (field nullfuncref) (field (mut f64))))))
(type $16 (sub $15 (describes $14 (descriptor $17 (struct (field (mut f32)) (field nullfuncref) (field (mut f64)) (field (mut f64)))))))
(type $17 (sub final $2 (describes $16 (descriptor $19 (struct (field (mut f32)) (field (ref null $20)) (field (mut f64)))))))
(type $18 (sub (func (param (ref $11)) (result anyref))))
(type $19 (sub (describes $17 (struct (field (mut i32)) (field (mut (ref null $0))) (field (mut i64)) (field (mut f32)) (field (mut i16))))))
(type $20 (sub final $0 (func)))
)
(type $21 (func (result f32 (ref null $12) i32 i64 exnref arrayref)))
(global $global$0 (ref (exact $16)) (struct.new_default $16
(struct.new_default $17
(ref.null none)
)
))
(export "func_24" (func $0))
(func $0 (result f32 (ref null $12) i32 i64 exnref arrayref)
(unreachable)
)
) |
Member
|
Fuzzer failure I bisected to here, that still fails on |
Member
Author
|
Thanks, taking a look now. |
Member
|
Another that fails on current (module
(type $7 (struct))
(type $8 (sub (array (mut (ref $7)))))
(rec
(type $1 (sub (descriptor $2 (struct))))
(type $5 (sub $1 (descriptor $6 (struct))))
(type $2 (sub (describes $1 (struct))))
(type $6 (sub $2 (describes $5 (struct))))
(type $63 (func (result structref)))
)
(rec
(type $10 (func (param (ref $8)) (result (ref null $14))))
(type $11 (func (result f32)))
(type $12 (sub (func (param (ref $8) v128) (result i64))))
(type $13 (func (param (ref null $11)) (result f64)))
(type $14 (struct (field (mut i8)) (field (mut i32)) (field (ref $11)) (field (ref i31))))
)
(type $69 (func (param externref externref) (result (ref $11))))
(elem declare func $8)
(export "func_20" (func $71))
(func $8 (type $11) (result f32)
(unreachable)
)
(func $58 (type $63) (result structref)
(block $block (result (ref (exact $5)))
(unreachable)
(drop
(struct.new $14
(i32.const 0)
(block (result i32)
(br $block
(struct.new_default $5
(ref.null none)
)
)
(i32.const 0)
)
(ref.func $8)
(ref.i31
(i32.const 0)
)
)
)
(return
(struct.new_default $7)
)
)
)
(func $71 (type $69) (param $0 externref) (param $1 externref) (result (ref $11))
(unreachable)
)
) |
Member
Author
|
Wow, what a bug farm. Taking a look. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GTO tries to optimize out unused descriptor types, but sometimes cannot
because the descriptor type must remain a descriptor to be a valid
supertype of a subtype that will remain a descriptor. To optimize the
original described type to not have a descriptor while simultaneously
keeping the descriptor a valid supertype, insert a new, trivial
placeholder type for the descriptor to describe. Since this new type is
not otherwise used in the module, it should be able to be optimized out
after subsequent rounds of unsubtyping and other optimizations.