blob: a834aa49e4cf6fd17a6ddf3fd22ee273baabd55b [file] [log] [blame]
;; Simplifications for C++20's `<=>` "spaceship" operator, aka Rust's `Ord::cmp`.
;;
;; There's no cranelift instruction for this, nor usually a machine instruction.
;; Inspired by <https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign>
;; we canonicalize the various implementations of `x <=> y` to `(x > y) - (x < y)`.
;; Unfortunately, there's at least 32 reasonable ways to write this as nested
;; selects, and no broad agreement which is the best -- notably Rust 1.74 and
;; Clang 17 use different sequences -- so we just match all of them.
;; x < y ? -1 : x == y ? 0 : +1
;; x < y ? -1 : x != y ? +1 : 0
(rule (simplify (select ty (ult rty x y)
(iconst_s ty -1)
(uextend_maybe ty (ne rty x y))))
(sextend_maybe ty (spaceship_u rty x y)))
;; x < y ? -1 : x <= y ? 0 : +1
;; x < y ? -1 : x > y ? +1 : 0
(rule (simplify (select ty (ult rty x y)
(iconst_s ty -1)
(uextend_maybe ty (ugt rty x y))))
(sextend_maybe ty (spaceship_u rty x y)))
;; x == y ? 0 : x < y ? -1 : +1
(rule (simplify (select ty (eq rty x y)
(iconst_s ty 0)
(select ty (ult rty x y)
(iconst_s ty -1)
(iconst_s ty 1))))
(sextend_maybe ty (spaceship_u rty x y)))
;; x == y ? 0 : x <= y ? -1 : +1
(rule (simplify (select ty (eq rty x y)
(iconst_s ty 0)
(select ty (ule rty x y)
(iconst_s ty -1)
(iconst_s ty 1))))
(sextend_maybe ty (spaceship_u rty x y)))
;; x == y ? 0 : x > y ? +1 : -1
(rule (simplify (select ty (eq rty x y)
(iconst_s ty 0)
(select ty (ugt rty x y)
(iconst_s ty 1)
(iconst_s ty -1))))
(sextend_maybe ty (spaceship_u rty x y)))
;; x == y ? 0 : x >= y ? +1 : -1
(rule (simplify (select ty (eq rty x y)
(iconst_s ty 0)
(select ty (uge rty x y)
(iconst_s ty 1)
(iconst_s ty -1))))
(sextend_maybe ty (spaceship_u rty x y)))
;; x > y ? 1 : x < y ? -1 : 0
;; x > y ? 1 : x >= y ? 0 : -1
(rule (simplify (select ty (ugt rty x y)
(iconst_s ty 1)
(ineg rty (ult rty x y))))
(sextend_maybe ty (spaceship_u rty x y)))
(rule (simplify (select ty (ugt rty x y)
(iconst_s ty 1)
(bmask ty (ult rty x y))))
(sextend_maybe ty (spaceship_u rty x y)))
;; x > y ? 1 : x != y ? -1 : 0
;; x > y ? 1 : x == y ? 0 : -1
(rule (simplify (select ty (ugt rty x y)
(iconst_s ty 1)
(ineg rty (ne rty x y))))
(sextend_maybe ty (spaceship_u rty x y)))
(rule (simplify (select ty (ugt rty x y)
(iconst_s ty 1)
(bmask ty (ne rty x y))))
(sextend_maybe ty (spaceship_u rty x y)))
;; Same, but for signed comparisons this time
;; x < y ? -1 : x == y ? 0 : +1
;; x < y ? -1 : x != y ? +1 : 0
(rule (simplify (select ty (slt rty x y)
(iconst_s ty -1)
(uextend_maybe ty (ne rty x y))))
(sextend_maybe ty (spaceship_s rty x y)))
;; x < y ? -1 : x <= y ? 0 : +1
;; x < y ? -1 : x > y ? +1 : 0
(rule (simplify (select ty (slt rty x y)
(iconst_s ty -1)
(uextend_maybe ty (sgt rty x y))))
(sextend_maybe ty (spaceship_s rty x y)))
;; x == y ? 0 : x < y ? -1 : +1
(rule (simplify (select ty (eq rty x y)
(iconst_s ty 0)
(select ty (slt rty x y)
(iconst_s ty -1)
(iconst_s ty 1))))
(sextend_maybe ty (spaceship_s rty x y)))
;; x == y ? 0 : x <= y ? -1 : +1
(rule (simplify (select ty (eq rty x y)
(iconst_s ty 0)
(select ty (sle rty x y)
(iconst_s ty -1)
(iconst_s ty 1))))
(sextend_maybe ty (spaceship_s rty x y)))
;; x == y ? 0 : x > y ? +1 : -1
(rule (simplify (select ty (eq rty x y)
(iconst_s ty 0)
(select ty (sgt rty x y)
(iconst_s ty 1)
(iconst_s ty -1))))
(sextend_maybe ty (spaceship_s rty x y)))
;; x == y ? 0 : x >= y ? +1 : -1
(rule (simplify (select ty (eq rty x y)
(iconst_s ty 0)
(select ty (sge rty x y)
(iconst_s ty 1)
(iconst_s ty -1))))
(sextend_maybe ty (spaceship_s rty x y)))
;; x > y ? 1 : x < y ? -1 : 0
;; x > y ? 1 : x >= y ? 0 : -1
(rule (simplify (select ty (sgt rty x y)
(iconst_s ty 1)
(ineg rty (slt rty x y))))
(sextend_maybe ty (spaceship_s rty x y)))
(rule (simplify (select ty (sgt rty x y)
(iconst_s ty 1)
(bmask ty (slt rty x y))))
(sextend_maybe ty (spaceship_s rty x y)))
;; x > y ? 1 : x != y ? -1 : 0
;; x > y ? 1 : x == y ? 0 : -1
(rule (simplify (select ty (sgt rty x y)
(iconst_s ty 1)
(ineg rty (ne rty x y))))
(sextend_maybe ty (spaceship_s rty x y)))
(rule (simplify (select ty (sgt rty x y)
(iconst_s ty 1)
(bmask ty (ne rty x y))))
(sextend_maybe ty (spaceship_s rty x y)))
;; Then once we have it normalized, we can apply some basic simplifications.
;; For example, a derived `PartialOrd::lt` on a newtype in Rust will essentially
;; emit `(a <=> b) < 0`, and replacing that with `a < b` can really help.
;; `icmp.isle` prefers comparing against zero so we don't need to worry about
;; also matching things like `(a <=> b) < 1` or `(a <=> b) <= -1`.
;; (a <=> b) == 0 --> a == b
(rule (simplify (eq _ (spaceship_s ty x y) (iconst_s _ 0)))
(eq ty x y))
(rule (simplify (eq _ (spaceship_u ty x y) (iconst_s _ 0)))
(eq ty x y))
;; (a <=> b) != 0 --> a != b
(rule (simplify (ne _ (spaceship_s ty x y) (iconst_s _ 0)))
(ne ty x y))
(rule (simplify (ne _ (spaceship_u ty x y) (iconst_s _ 0)))
(ne ty x y))
;; (a <=> b) < 0 --> a < b
(rule (simplify (slt _ (spaceship_s ty x y) (iconst_s _ 0)))
(slt ty x y))
(rule (simplify (slt _ (spaceship_u ty x y) (iconst_s _ 0)))
(ult ty x y))
;; (a <=> b) <= 0 --> a <= b
(rule (simplify (sle _ (spaceship_s ty x y) (iconst_s _ 0)))
(sle ty x y))
(rule (simplify (sle _ (spaceship_u ty x y) (iconst_s _ 0)))
(ule ty x y))
;; (a <=> b) > 0 --> a > b
(rule (simplify (sgt _ (spaceship_s ty x y) (iconst_s _ 0)))
(sgt ty x y))
(rule (simplify (sgt _ (spaceship_u ty x y) (iconst_s _ 0)))
(ugt ty x y))
;; (a <=> b) >= 0 --> a >= b
(rule (simplify (sge _ (spaceship_s ty x y) (iconst_s _ 0)))
(sge ty x y))
(rule (simplify (sge _ (spaceship_u ty x y) (iconst_s _ 0)))
(uge ty x y))
;; Rust's `sort_by` uses `compare(a, b) == Less`, which the general icmp rules
;; can't simplify to a comparison against zero, so catch things like that too.
(rule (simplify (eq _ (spaceship_s ty x y) (iconst_s _ -1)))
(slt ty x y))
(rule (simplify (eq _ (spaceship_u ty x y) (iconst_s _ -1)))
(ult ty x y))
(rule (simplify (ne _ (spaceship_s ty x y) (iconst_s _ -1)))
(sge ty x y))
(rule (simplify (ne _ (spaceship_u ty x y) (iconst_s _ -1)))
(uge ty x y))
(rule (simplify (eq _ (spaceship_s ty x y) (iconst_s _ 1)))
(sgt ty x y))
(rule (simplify (eq _ (spaceship_u ty x y) (iconst_s _ 1)))
(ugt ty x y))
(rule (simplify (ne _ (spaceship_s ty x y) (iconst_s _ 1)))
(sle ty x y))
(rule (simplify (ne _ (spaceship_u ty x y) (iconst_s _ 1)))
(ule ty x y))