Skip to content

Conversation

tlively
Copy link
Member

@tlively tlively commented Sep 6, 2025

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.

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.
@tlively tlively requested a review from kripken September 6, 2025 02:47
Copy link
Member

@kripken kripken left a comment

Choose a reason for hiding this comment

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

Nice!

HeapType placeholder = Struct{};
types.insert(
types.begin(), parent.descriptorsOfPlaceholders.size(), placeholder);
return types;
Copy link
Member

Choose a reason for hiding this comment

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

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 tlively enabled auto-merge (squash) September 8, 2025 20:08
@tlively tlively merged commit 4f25fee into main Sep 10, 2025
16 checks passed
@tlively tlively deleted the gto-placeholder-describees branch September 10, 2025 23:53
@kripken
Copy link
Member

kripken commented Sep 11, 2025

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)
 )
)
$ bin/wasm-opt a.wat --gto -all --closed-world
Fatal: Internal GlobalTypeRewriter build error: Heap type has an invalid supertype at index 3

@kripken
Copy link
Member

kripken commented Sep 15, 2025

Fuzzer failure I bisected to here, that still fails on main:

(module
 (rec
  (type $0 (sub (descriptor $2 (struct (field (mut (ref $3))) (field (mut (ref func))) (field (mut v128)) (field f64) (field (mut i64)) (field (mut f32))))))
  (type $1 (sub (array i8)))
  (type $2 (sub (describes $0 (struct (field i16) (field (ref null $3)) (field (ref null $4)) (field (mut i8))))))
  (type $3 (array anyref))
  (type $4 (sub (struct (field (ref $2)) (field (mut (ref null $4))))))
 )
 (rec
  (type $5 (descriptor $7 (struct (field (mut (ref $3))) (field (mut (ref func))) (field (mut v128)) (field f64) (field (mut i64)) (field (mut f32)))))
  (type $6 (sub (array i8)))
  (type $7 (sub $2 (describes $5 (struct (field i16) (field nullref) (field (ref (exact $8))) (field (mut i8))))))
  (type $8 (sub $4 (struct (field (ref (exact $7))) (field (mut (ref null $4))))))
  (type $9 (func (param v128)))
  (type $10 (func (param eqref) (result (ref null $6))))
 )
 (type $11 (func (result (ref $2))))
 (export "func_28" (func $1))
 (func $0 (type $10) (param $0 eqref) (result (ref null $6))
  (unreachable)
 )
 (func $1 (result (ref $2))
  (unreachable)
 )
)
$ bin/wasm-opt -all --closed-world a.wat --gto
Fatal: Internal GlobalTypeRewriter build error: Descriptor type does not describe heap type at index 0

@tlively

@tlively
Copy link
Member Author

tlively commented Sep 18, 2025

Thanks, taking a look now.

@kripken
Copy link
Member

kripken commented Sep 22, 2025

Another that fails on current main:

(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)
 )
)
$ bin/wasm-opt w.wat -all --closed-world --gto
[wasm-validator error in function 58] unexpected false: struct.new of type with descriptor requires descriptor operand, on 
(struct.new_default $7)
Fatal: error after opts

@tlively
Copy link
Member Author

tlively commented Sep 22, 2025

Wow, what a bug farm. Taking a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants