blob: 0f44ba47d2ec5df298e7efe1b8dfbe2b9512cb23 [file] [log] [blame]
;; Rewrites for `band`, `bnot`, `bor`, `bxor`
;; x | 0 == x | x == x.
(rule (simplify (bor ty
x
(iconst_u ty 0)))
(subsume x))
(rule (simplify (bor ty x x))
(subsume x))
;; x ^ 0 == x.
(rule (simplify (bxor ty
x
(iconst_u ty 0)))
(subsume x))
;; x ^ x == 0.
(rule (simplify (bxor (ty_int ty) x x))
(subsume (iconst_u ty 0)))
;; x ^ not(x) == not(x) ^ x == x | not(x) == not(x) | x == -1.
;; This identity also holds for non-integer types, vectors, and wider types.
(rule (simplify (bxor (ty_int ty) x (bnot ty x))) (subsume (iconst_s ty -1)))
(rule (simplify (bxor (ty_int ty) (bnot ty x) x)) (subsume (iconst_s ty -1)))
(rule (simplify (bor (ty_int ty) x (bnot ty x))) (subsume (iconst_s ty -1)))
(rule (simplify (bor (ty_int ty) (bnot ty x) x)) (subsume (iconst_s ty -1)))
;; x & x == x & -1 == x.
(rule (simplify (band ty x x)) (subsume x))
(rule (simplify (band ty x (iconst_s ty -1)))
(subsume x))
;; x & 0 == x & not(x) == not(x) & x == 0.
(rule (simplify (band ty _ zero @ (iconst_u ty 0))) (subsume zero))
(rule (simplify (band (ty_int ty) x (bnot ty x))) (subsume (iconst_u ty 0)))
(rule (simplify (band (ty_int ty) (bnot ty x) x)) (subsume (iconst_u ty 0)))
;; not(not(x)) == x.
(rule (simplify (bnot ty (bnot ty x))) (subsume x))
;; DeMorgan's rule (two versions):
;; bnot(bor(x, y)) == band(bnot(x), bnot(y))
(rule (simplify (bnot ty (bor ty x y)))
(band ty (bnot ty x) (bnot ty y)))
;; bnot(band(x, y)) == bor(bnot(x), bnot(y))
(rule (simplify (bnot ty (band t x y)))
(bor ty (bnot ty x) (bnot ty y)))
;; `or(and(x, y), not(y)) == or(x, not(y))`
(rule (simplify (bor ty
(band ty x y)
z @ (bnot ty y)))
(bor ty x z))
;; Duplicate the rule but swap the `bor` operands because `bor` is
;; commutative. We could, of course, add a `simplify` rule to do the commutative
;; swap for all `bor`s but this will bloat the e-graph with many e-nodes. It is
;; cheaper to have additional rules, rather than additional e-nodes, because we
;; amortize their cost via ISLE's smart codegen.
(rule (simplify (bor ty
z @ (bnot ty y)
(band ty x y)))
(bor ty x z))
;; `or(and(x, y), not(y)) == or(x, not(y))` specialized for constants, since
;; otherwise we may not know that `z == not(y)` since we don't generally expand
;; constants in the e-graph.
;;
;; (No need to duplicate for commutative `bor` for this constant version because
;; we move constants to the right.)
(rule (simplify (bor ty
(band ty x (iconst_u ty y))
z @ (iconst_u ty zk)))
(if-let $true (u64_eq (u64_and (ty_mask ty) zk)
(u64_and (ty_mask ty) (u64_not y))))
(bor ty x z))
;; (x ^ -1) can be replaced with the `bnot` instruction
(rule (simplify (bxor ty x (iconst_s ty -1)))
(bnot ty x))
;; sshr((x | -x), N) == bmask(x) where N = ty_bits(ty) - 1.
;;
;; (x | -x) sets the sign bit to 1 if x is nonzero, and 0 if x is zero. sshr propagates
;; the sign bit to the rest of the value.
(rule (simplify (sshr ty (bor ty x (ineg ty x)) (iconst_u ty shift_amt)))
(if-let $true (u64_eq shift_amt (ty_shift_mask ty)))
(bmask ty x))
(rule (simplify (sshr ty (bor ty (ineg ty x) x) (iconst_u ty shift_amt)))
(if-let $true (u64_eq shift_amt (ty_shift_mask ty)))
(bmask ty x))
;; Since icmp is always 0 or 1, bmask is just a negation.
;; TODO: Explore whether this makes sense for things needing extension too.
(rule (simplify (bmask $I8 cmp@(icmp $I8 _ _ _)))
(ineg $I8 cmp))
;; Matches any expressions that preserve "truthiness".
;; i.e. If the input is zero it remains zero, and if it is nonzero it can have
;; a different value as long as it is still nonzero.
(decl pure multi truthy (Value) Value)
(rule (truthy (sextend _ x)) x)
(rule (truthy (uextend _ x)) x)
(rule (truthy (bmask _ x)) x)
(rule (truthy (ineg _ x)) x)
(rule (truthy (bswap _ x)) x)
(rule (truthy (bitrev _ x)) x)
(rule (truthy (popcnt _ x)) x)
(rule (truthy (rotl _ x _)) x)
(rule (truthy (rotr _ x _)) x)
(rule (truthy (select _ x (iconst_u _ (u64_nonzero _)) (iconst_u _ 0))) x)
;; (ne ty (iconst 0) v) is also canonicalized into this form via another rule
(rule (truthy (ne _ x (iconst_u _ 0))) x)
;; All of these expressions don't care about their input as long as it is truthy.
;; so we can remove expressions that preserve that property from the input.
(rule (simplify (bmask ty v)) (if-let x (truthy v)) (bmask ty x))
(rule (simplify (select ty v t f)) (if-let c (truthy v)) (select ty c t f))
;; (ne ty (iconst 0) v) is also canonicalized into this form via another rule
(rule (simplify (ne cty v (iconst_u _ 0)))
(if-let c (truthy v))
(if-let (value_type (ty_int_ref_scalar_64 ty)) c)
(ne cty c (iconst_u ty 0)))
;; (sextend (bmask x)) can be replaced with (bmask x) since bmask
;; supports any size of output type, regardless of input.
;; Same with `ireduce`
(rule (simplify (sextend ty (bmask _ x))) (bmask ty x))
(rule (simplify (ireduce ty (bmask _ x))) (bmask ty x))
;; (bswap (bswap x)) == x
(rule (simplify (bswap ty (bswap ty x))) (subsume x))
;; (bitrev (bitrev x)) == x
(rule (simplify (bitrev ty (bitrev ty x))) (subsume x))
;; WebAssembly doesn't have a native byte-swapping instruction at this time so
;; languages which have a byte-swapping operation will compile it down to bit
;; shifting and twiddling. This attempts to pattern match what LLVM currently
;; generates today for the Rust code `a.swap_bytes()`. This might be a bit
;; brittle over time and/or with other possible LLVM backend optimizations, but
;; it's at least one way to generate a byte swap.
;;
;; Technically this could be permuted quite a few ways and currently there's no
;; easy way to match all of them, so only one is matched here.
(rule (simplify (bor ty @ $I32
(bor ty
(ishl ty x (iconst_u ty 24))
(ishl ty
(band ty x (iconst_u ty 0xff00))
(iconst_u ty 8)))
(bor ty
(band ty
(ushr ty x (iconst_u ty 8))
(iconst_u ty 0xff00))
(ushr ty x (iconst_u ty 24)))))
(bswap ty x))
(rule (simplify (bor ty @ $I64
(bor ty
(bor ty
(ishl ty x (iconst_u ty 56))
(ishl ty
(band ty x (iconst_u ty 0xff00))
(iconst_u ty 40)))
(bor ty
(ishl ty
(band ty x (iconst_u ty 0xff_0000))
(iconst_u ty 24))
(ishl ty
(band ty x (iconst_u ty 0xff00_0000))
(iconst_u ty 8))))
(bor ty
(bor ty
(band ty
(ushr ty x (iconst_u ty 8))
(iconst_u ty 0xff00_0000))
(band ty
(ushr ty x (iconst_u ty 24))
(iconst_u ty 0xff_0000)))
(bor ty
(band ty
(ushr ty x (iconst_u ty 40))
(iconst_u ty 0xff00))
(ushr ty x (iconst_u ty 56))))))
(bswap ty x))