Optimize sign extension via shifts (#6220)

* Optimize sign extension via shifts

This commit adds egraph optimization patterns for left-shifting a value
and then right-shifting it as a form of sign extending its lower bits.
This matches the behavior of the WebAssembly `i32.extend8_s`
instruction, for example. Note that the lowering of that WebAssembly
instruction does not use shifts, but historical versions of LLVM that
didn't support the instruction, or versions with the instruction
disabled, will use shifts instead.

A second rule for reduction-of-extend being the same as the original
value was added to keep an existing shift-related test passing as well.

* Add reference assemblies for new opts
This commit is contained in:
Alex Crichton
2023-04-17 13:48:08 -05:00
committed by GitHub
parent 9a4bd7c6df
commit 7ebff82861
5 changed files with 407 additions and 0 deletions

View File

@@ -27,3 +27,8 @@
(uextend $I64 x @ (value_type $I32))
(iconst _ (u64_from_imm64 0))))
(iconst ty (imm64 1)))
;; A reduction-of-an-extend back to the same original type is the same as not
;; actually doing the extend in the first place.
(rule (simplify (ireduce ty (sextend _ x @ (value_type ty)))) x)
(rule (simplify (ireduce ty (uextend _ x @ (value_type ty)))) x)

View File

@@ -79,6 +79,25 @@
(if-let $true (u64_le shift_u64 (u64_sub (ty_bits_u64 wide) (ty_bits_u64 narrow))))
x)
;; (x << N) >> N == x as T_SMALL as T_LARGE
;; if N == bytesizeof(T_LARGE) - bytesizeof(T_SMALL)
;;
;; Note that the shift is required to be >0 to ensure this doesn't accidentally
;; try to `ireduce` a type to itself, which isn't a valid use of `ireduce`.
(rule (simplify (sshr ty (ishl ty x (iconst _ shift)) (iconst _ shift)))
(if-let (u64_from_imm64 (u64_nonzero shift_u64)) shift)
(if-let ty_small (shift_amt_to_type (u64_sub (ty_bits ty) shift_u64)))
(sextend ty (ireduce ty_small x)))
(rule (simplify (ushr ty (ishl ty x (iconst _ shift)) (iconst _ shift)))
(if-let (u64_from_imm64 (u64_nonzero shift_u64)) shift)
(if-let ty_small (shift_amt_to_type (u64_sub (ty_bits ty) shift_u64)))
(uextend ty (ireduce ty_small x)))
(decl pure partial shift_amt_to_type (u64) Type)
(rule (shift_amt_to_type 8) $I8)
(rule (shift_amt_to_type 16) $I16)
(rule (shift_amt_to_type 32) $I32)
;; ineg(ushr(x, k)) == sshr(x, k) when k == ty_bits - 1.
(rule (simplify (ineg ty (ushr ty x sconst @ (iconst ty (u64_from_imm64 shift_amt)))))
(if-let $true (u64_eq shift_amt (u64_sub (ty_bits ty) 1)))