Skip to content

Conversation

osa1
Copy link
Contributor

@osa1 osa1 commented Oct 2, 2025

Fixes #6989.

With this we're able to use -O4 to optimize dart2wasm programs. I tried this on a few benchmarks, largest one being a 37M .wasm, and it worked fine.

Flattening transformations done:

(br_on_null $label <flat expr>)

==>

Locals:
  (local $nullableTemp <flat expr type>)
  (local $isNull i32)

Preludes:
  (local.set $nullableTemp <flat expr>)
  (local.set $isNull (ref_is_null (local.get $nullableTemp)
  (br_if $label (local.get $isNull))

Expr:
  (ref_as_non_null (local.get $nullableTemp)
(br_on_non_null $label <flat expr>)

==>

Locals:
  (local $nullableTemp <flat expr type>)
  (local $isNull i32)
  (local $isNotNull i32)

Preludes:
  (local.set $nullableTemp <flat expr>)
  (local.set $isNull (ref_is_null (local.get $nullableTemp)
  (local.set $isNotNull (i32.eqz (local.get $isNull)))

Expr:
  (if (local.get $isNotNull)
    (local.set $labelLocal (ref_as_non_null (local.get $nullableTemp)))
    (br $label))
(br_on_cast $label $s $t <flat expr>)

==> 

Locals:
  (local $source $s)
  (local $typeTest i32)
  (local $failTemp ($s \ $t))

Preludes:
  (local.set $source <flat expr>)
  (local.set $typeTest (ref_test (local.get $source) $t))

  (if (local.get $typeTest)
    (local.set $labelLocal (ref_cast (local.get $source) $s $t))
    (br $label))

  ;; If `$s \ $t` is non-nullable:
  (local.set $failTemp (ref_as_non_null (local.get $source)))
  ;; Otherwise:
  (local.set $failTemp (local.get $source))

Expr:
  (local.get $failTemp)
(br_on_cast_failure $label $s $t <flat expr>)

==>

Locals:
  (local $source $s)
  (local $target $t)
  (local $typeTest i32)
  (local $typeTestFail i32)
  (local $failTemp ($s \ $t))

Preludes:
  (local.set $source <flat expr>)
  (local.set $typeTest (ref_test (local.get $source) $t))
  (local.set $typeTestFail (i32.eqz (local.get $typeTest)))

  (if (local.get $typeTestFail)
    ;; If `$s \ $t` is non-nullable:
    (local.set $labelLocal (ref_as_non_null (local.get $source)))
    ;; Otherwise
    (local.set $labelLocal (local.get $source))
  )

  (local.set $target (ref_cast (local.get $source) $s $t))

Expr:
  (local.get $target)

;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; NOTE: This test was ported using port_passes_tests_to_lit.py and could be cleaned up.

;; RUN: foreach %s %t wasm-opt --flatten --all-features -S -o - | filecheck %s
Copy link
Member

Choose a reason for hiding this comment

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

The foreach here lets you have multiple modules in the same test file, but since the pass just does function-local changes, you can even just have a single module with several functions in it. This would be more in line with how we usually group tests together.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I deliberately created different files as I found it to be easier to modify the tests this way.

I also get more detailed error reporting, if I have a bug in one of the instructions but others work fine, 3 out of 4 tests pass (instead of a large file with lots of instructions failing with just one error).

I can still merge these to one of the existing files if you prefer. Which file should I move these to?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, I guess with multiple modules I would get multiple errors in the same file, right? Instead of just one error for all of the tests in the file.

It's still easier to edit multiple files, but if you let me know where you want these tests I can move them to another file. There's a flatten_all-features.wast, but it's kinda large, and it mixes underscores and dashes in the same file name 😞

Copy link
Member

Choose a reason for hiding this comment

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

Yes, with multiple modules you can get multiple errors. Keeping the separate modules is fine, but I would consolidate all the tests in a single flatten-br-on.wast file. (The mixed underscores and dashes in flatten_all-features.wast is baggage from the older test system that parsed command line options from file names 🫣)

@osa1 osa1 marked this pull request as ready for review October 3, 2025 08:50
@osa1
Copy link
Contributor Author

osa1 commented Oct 3, 2025

@tlively @kripken This is ready for reviews, but the testing here may not be sufficient. Because I use ref_as_non_null and ref_cast in the lowered code, when done incorectly these instructions can make the Wasm module valid but just do ref_as_non_null on null references. This happened in a previous version of this PR and I only caught it in dart2wasm.

I'm not sure how to test for this as we would need to run the lowered program. For now I tested this on a few dart2wasm benchmarks (largest one being a 37M .wasm) and it works fine.

Any suggestions on how to improve testing here?

I suggest reviewing the transformations shown in the PR description first. If those transformations are not right then the code also won't be right. (unless I made a mistake while transcribing)

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.

Flatten does not handle BrOn.
2 participants