Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/passes/OptimizeInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2552,6 +2552,17 @@ struct OptimizeInstructions
bool notWeaker = Type::isSubType(curr->type, child->type);
bool safe = !child->desc || getPassOptions().trapsNeverHappen;
if (notWeaker && safe) {
if (curr->desc) {
// There is another child here, whose effects we must consider (the
// same ordering situation as in skipNonNullCast: we want to move a
// trap past later children).
auto& options = getPassOptions();
EffectAnalyzer descEffects(options, *getModule(), curr->desc);
ShallowEffectAnalyzer movingEffects(options, *getModule(), curr->ref);
if (movingEffects.orderedBefore(descEffects)) {
return;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't understand why this is correct. We're replacing the current ref argument (which is evaluated before the desc) with the child's ref argument (which is also evaluated before the desc). So what's being reordered?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The trap due to the inner cast. See the test, which is this:

(outer.cast
  (inner.cast (inner.ref))
  (descriptor with effects)
)

=>

(outer.cast
  (inner.ref)
  (descriptor with effects)
)

Now inner.cast is gone. Before, if it trapped, it trapped before the descriptor's effects. Now, the outer cast still traps - we didn't lose the trap - but it is after the descriptor.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see, it's not the expressions that are being reordered; it's the trap effect. And trapping is the only effect that matters here because we already know that curr->ref is another cast. It would be helpful to call that out slightly more explicitly in the comment.

Also, we should combine this new logic into the calculation of safe above. This is more than a nice refactoring because returning early here prevents later patterns from being evaluated.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks, comment expanded and logic reordered.

}
if (child->desc) {
// Reorder the child's reference past its dropped descriptor if
// necessary.
Expand Down
68 changes: 68 additions & 0 deletions test/lit/passes/optimize-instructions-desc.wast
Original file line number Diff line number Diff line change
Expand Up @@ -1459,4 +1459,72 @@
)
)
)

;; CHECK: (func $ref.cast_desc_eq-ref.cast (type $22) (param $x anyref)
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast_desc_eq (ref $struct)
;; CHECK-NEXT: (ref.cast (ref $struct)
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
;; CHECK-NEXT: (block (result (ref null $desc))
;; CHECK-NEXT: (return)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast_desc_eq (ref (exact $struct))
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: (struct.new_default $desc)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; NTRAP: (func $ref.cast_desc_eq-ref.cast (type $22) (param $x anyref)
;; NTRAP-NEXT: (local $1 (ref $struct))
;; NTRAP-NEXT: (local $2 (ref null $desc))
;; NTRAP-NEXT: (drop
;; NTRAP-NEXT: (block (result (ref $struct))
;; NTRAP-NEXT: (local.set $1
;; NTRAP-NEXT: (ref.cast (ref $struct)
;; NTRAP-NEXT: (local.get $x)
;; NTRAP-NEXT: )
;; NTRAP-NEXT: )
;; NTRAP-NEXT: (local.set $2
;; NTRAP-NEXT: (block (result (ref null $desc))
;; NTRAP-NEXT: (return)
;; NTRAP-NEXT: )
;; NTRAP-NEXT: )
;; NTRAP-NEXT: (local.get $1)
;; NTRAP-NEXT: )
;; NTRAP-NEXT: )
;; NTRAP-NEXT: (drop
;; NTRAP-NEXT: (ref.cast_desc_eq (ref (exact $struct))
;; NTRAP-NEXT: (local.get $x)
;; NTRAP-NEXT: (struct.new_default $desc)
;; NTRAP-NEXT: )
;; NTRAP-NEXT: )
;; NTRAP-NEXT: )
(func $ref.cast_desc_eq-ref.cast (param $x anyref)
;; As above with ref.as_non_null, removing the inner ref.cast would allow
;; reaching the return before the cast check, so we do not optimize. (In
;; NTRAP mode we end up removing the outer cast, separately.)
(drop
(ref.cast_desc_eq (ref $struct)
(ref.cast (ref $struct)
(local.get $x)
)
(block (result (ref null $desc))
(return)
)
)
)
;; Without dangerous effects we can remove the inner cast.
(drop
(ref.cast_desc_eq (ref $struct)
(ref.cast (ref $struct)
(local.get $x)
)
(struct.new $desc) ;; this has no effects
)
)
)
)
Loading