Skip to content

Cranelift: Rewrite or(and(x, y), not(y)) => or(x, not(y))#5676

Merged
fitzgen merged 1 commit into
bytecodealliance:mainfrom
fitzgen:or-and-y-with-not-y
Jan 31, 2023
Merged

Cranelift: Rewrite or(and(x, y), not(y)) => or(x, not(y))#5676
fitzgen merged 1 commit into
bytecodealliance:mainfrom
fitzgen:or-and-y-with-not-y

Conversation

@fitzgen
Copy link
Copy Markdown
Member

@fitzgen fitzgen commented Jan 31, 2023

Thanks again to Souper for pointing this one out! cc @regehr

@fitzgen fitzgen added cranelift:goal:optimize-speed Focus area: the speed of the code produced by Cranelift. souper Issues and PRs related to the Souper superoptimizer labels Jan 31, 2023
@fitzgen fitzgen requested a review from cfallin January 31, 2023 20:59
@fitzgen
Copy link
Copy Markdown
Member Author

fitzgen commented Jan 31, 2023

cc @itsrainy

@fitzgen
Copy link
Copy Markdown
Member Author

fitzgen commented Jan 31, 2023

Builds on top of #5673, only the last commit is relevant to this PR

Copy link
Copy Markdown
Member

@cfallin cfallin left a comment

Choose a reason for hiding this comment

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

LGTM!

@fitzgen fitzgen force-pushed the or-and-y-with-not-y branch from e7bec6a to 9b9ded0 Compare January 31, 2023 21:28
@fitzgen fitzgen enabled auto-merge (squash) January 31, 2023 21:29
Comment thread cranelift/filetests/filetests/runtests/or-and-y-with-not-y.clif Outdated
Comment thread cranelift/filetests/filetests/egraph/algebraic.clif
Comment thread cranelift/codegen/src/opts/algebraic.isle
@fitzgen fitzgen disabled auto-merge January 31, 2023 21:50
Co-Authored-By: Rainy Sinclair <844493+itsrainy@users.noreply.github.com>
@fitzgen fitzgen force-pushed the or-and-y-with-not-y branch from 9b9ded0 to 020eb65 Compare January 31, 2023 21:58
@fitzgen fitzgen enabled auto-merge (squash) January 31, 2023 21:59
@github-actions github-actions Bot added the cranelift Issues related to the Cranelift code generator label Jan 31, 2023
@fitzgen fitzgen merged commit 8c9eb99 into bytecodealliance:main Jan 31, 2023
@fitzgen fitzgen deleted the or-and-y-with-not-y branch February 1, 2023 00:48
elliottt added a commit that referenced this pull request Feb 1, 2023
elliottt added a commit that referenced this pull request Feb 1, 2023
(band ty x (iconst ty (u64_from_imm64 y)))
z @ (iconst ty (u64_from_imm64 zk))))
(if (u64_eq zk (u64_not y)))
(bor ty x z))
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.

Our verifier prototype finds the following counterexample for this rule (lightly edited for readability):

simplify (lhs):
#x0000000000000000
#b0

bor (rhs):
#x0000000000000001
#b1

x:
#x0000000000000001
#b1

y:
#x0000000000000002
#b10

z:
#x0000000000000000
#b0

zk:
#x0000000000000000
#b0

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.

I believe that this is because the if precondition is buggy. It is just checking whether the constructor succeeds or not, it is not checking if it returned true. Basically ignoring that predicate. (Funnily enough, because u64_eq is pure, the constructor will always succeed! This seems like something the ISLE compiler can check for and reject.) This is a pretty big foot gun with if, IMHO.

Anyways, the predicate needs to be rewritten as (if-let $true ...).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That's so cool! I don't understand it but it's awesome. 😆

Here's what's confusing me: The rule should only fire if zk is equal to !y. But in this counterexample, y is 2, so u64_not y is 0xfffffffffffffffd, but zk is 0. Is the verifier missing the if condition? Or is u64_not modeled incorrectly, maybe? (It's supposed to be bitwise-not, but this counterexample would make sense if that were modeled as boolean-not, turning non-zero values into 0.)

Also, what type is this counterexample for? I'd be especially interested if you can find counterexamples for I64 specifically, because I think the rule as-written is wrong for any other width. That said, I'd expect other widths to just fail to match the rule most of the time, rather than ever rewriting incorrectly, so if you find a narrower counterexample it's probably a real issue.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ohhh, Nick's got it right. Although it's because u64_eq is not partial, not because it's pure. (Those flags are independent now.)

We also need to mask both constants down to the appropriate width if we want to make the rule fire on <64-bit types.

Copy link
Copy Markdown
Member

@avanhatt avanhatt Feb 1, 2023

Choose a reason for hiding this comment

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

To restate what @fitzgen realized:

(if (u64_eq zk (u64_not y))) desugars to (if-let _ (u64_eq zk (u64_not y))), which ISLE is perfectly happy to match on. That is, the semantics for if is whether some match is possible, not whether the match is possible and its boolean value is truthy. The verifier models these weird semantics implicitly, since it treats if-let statements as constraints setting the LHS and RHS of the if-let clause to be equal---in this case, happily setting the wildcard to false and considering the rule to have matched.

Right now, this counterexample is for BVs of width 64, and the verifier is complaining that the rule is infeasible (that is, doesn't match) for other widths (which, based on your comment, seems correct?).

fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Feb 1, 2023
This rewrite was introduced in bytecodealliance#5676 and then reverted in bytecodealliance#5682 due to a footgun
where we accidentally weren't actually checking the `y == !z` precondition. This
commit fixes the precondition check.

This reverts commit 268f6bf.
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Feb 1, 2023
This rewrite was introduced in bytecodealliance#5676 and then reverted in bytecodealliance#5682 due to a footgun
where we accidentally weren't actually checking the `y == !z` precondition. This
commit fixes the precondition check.

This reverts commit 268f6bf.
fitzgen added a commit to fitzgen/wasmtime that referenced this pull request Feb 1, 2023
This rewrite was introduced in bytecodealliance#5676 and then reverted in bytecodealliance#5682 due to a footgun
where we accidentally weren't actually checking the `y == !z` precondition. This
commit fixes the precondition check. It also fixes the arithmetic to be
correctly masked to the value type's width.

This reverts commit 268f6bf.
fitzgen added a commit that referenced this pull request Feb 1, 2023
)

This rewrite was introduced in #5676 and then reverted in #5682 due to a footgun
where we accidentally weren't actually checking the `y == !z` precondition. This
commit fixes the precondition check. It also fixes the arithmetic to be
correctly masked to the value type's width.

This reverts commit 268f6bf.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cranelift:goal:optimize-speed Focus area: the speed of the code produced by Cranelift. cranelift Issues related to the Cranelift code generator souper Issues and PRs related to the Souper superoptimizer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants