Skip to content

Conversation

@kripken
Copy link
Member

@kripken kripken commented Dec 2, 2025

Rather than recompute the entire map of Call instructions to each target, keep it
around between iterations, and just update the changed part. This requires us to
track the origin of each call, so we know which data is old and stale.

This makes the pass 15% faster on some Dart and C++ testcases, and
9% faster on Kotlin and Java. On Dart this is also the slowest pass in -O3, making
us overall 3% faster.

@kripken kripken requested a review from tlively December 2, 2025 23:15
@kripken kripken marked this pull request as draft December 3, 2025 16:35
@kripken
Copy link
Member Author

kripken commented Dec 3, 2025

Fuzzer found an issue, so converting to draft. It turns out that we Refinalize internally, and that can remove (unreachable) calls. That makes this hang on dae-optimizing:

(module
 (rec
  (type $0 (func))
  (type $1 (func (param (ref $2)) (result f32)))
  (type $2 (struct (field (mut externref))))
  (type $3 (func (result v128)))
 )
 (func $0 (type $3) (result v128)
  (drop
   (call_ref $1
    (struct.new $2
     (ref.as_non_null
      (ref.null noextern)
     )
    )
    (ref.func $1)
   )
  )
  (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
 )
 (func $1 (type $1) (param $0 (ref $2)) (result f32)
  (unreachable)
 )
 (func $2 (type $0)
  (drop
   (call $0)
  )
 )
)

We are not aware that Refinalize causes changes, so we miss the call going away, and end up confused.

Tracking Refinalize may be enough, perhaps in a separate PR.

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.

1 participant