;; Apply basic simplifications. ;; ;; This folds constants with arithmetic to form `_imm` instructions, and other ;; minor simplifications. ;; ;; Doesn't apply some simplifications if the native word width (in bytes) is ;; smaller than the controlling type's width of the instruction. This would ;; result in an illegal instruction that would likely be expanded back into an ;; instruction on smaller types with the same initial opcode, creating ;; unnecessary churn. ;; Binary instructions whose second argument is constant. (=> (when (iadd $x $C) (fits-in-native-word $C)) (iadd_imm $C $x)) (=> (when (imul $x $C) (fits-in-native-word $C)) (imul_imm $C $x)) (=> (when (sdiv $x $C) (fits-in-native-word $C)) (sdiv_imm $C $x)) (=> (when (udiv $x $C) (fits-in-native-word $C)) (udiv_imm $C $x)) (=> (when (srem $x $C) (fits-in-native-word $C)) (srem_imm $C $x)) (=> (when (urem $x $C) (fits-in-native-word $C)) (urem_imm $C $x)) (=> (when (band $x $C) (fits-in-native-word $C)) (band_imm $C $x)) (=> (when (bor $x $C) (fits-in-native-word $C)) (bor_imm $C $x)) (=> (when (bxor $x $C) (fits-in-native-word $C)) (bxor_imm $C $x)) (=> (when (rotl $x $C) (fits-in-native-word $C)) (rotl_imm $C $x)) (=> (when (rotr $x $C) (fits-in-native-word $C)) (rotr_imm $C $x)) (=> (when (ishl $x $C) (fits-in-native-word $C)) (ishl_imm $C $x)) (=> (when (ushr $x $C) (fits-in-native-word $C)) (ushr_imm $C $x)) (=> (when (sshr $x $C) (fits-in-native-word $C)) (sshr_imm $C $x)) (=> (when (isub $x $C) (fits-in-native-word $C)) (iadd_imm $(neg $C) $x)) (=> (when (ifcmp $x $C) (fits-in-native-word $C)) (ifcmp_imm $C $x)) (=> (when (icmp $cond $x $C) (fits-in-native-word $C)) (icmp_imm $cond $C $x)) ;; Binary instructions whose first operand is constant. (=> (when (iadd $C $x) (fits-in-native-word $C)) (iadd_imm $C $x)) (=> (when (imul $C $x) (fits-in-native-word $C)) (imul_imm $C $x)) (=> (when (band $C $x) (fits-in-native-word $C)) (band_imm $C $x)) (=> (when (bor $C $x) (fits-in-native-word $C)) (bor_imm $C $x)) (=> (when (bxor $C $x) (fits-in-native-word $C)) (bxor_imm $C $x)) (=> (when (isub $C $x) (fits-in-native-word $C)) (irsub_imm $C $x)) ;; Unary instructions whose operand is constant. (=> (adjust_sp_down $C) (adjust_sp_down_imm $C)) ;; Fold `(binop_imm $C1 (binop_imm $C2 $x))` into `(binop_imm $(binop $C2 $C1) $x)`. (=> (iadd_imm $C1 (iadd_imm $C2 $x)) (iadd_imm $(iadd $C1 $C2) $x)) (=> (imul_imm $C1 (imul_imm $C2 $x)) (imul_imm $(imul $C1 $C2) $x)) (=> (bor_imm $C1 (bor_imm $C2 $x)) (bor_imm $(bor $C1 $C2) $x)) (=> (band_imm $C1 (band_imm $C2 $x)) (band_imm $(band $C1 $C2) $x)) (=> (bxor_imm $C1 (bxor_imm $C2 $x)) (bxor_imm $(bxor $C1 $C2) $x)) ;; Remove operations that are no-ops. (=> (iadd_imm 0 $x) $x) (=> (imul_imm 1 $x) $x) (=> (sdiv_imm 1 $x) $x) (=> (udiv_imm 1 $x) $x) (=> (bor_imm 0 $x) $x) (=> (band_imm -1 $x) $x) (=> (bxor_imm 0 $x) $x) (=> (rotl_imm 0 $x) $x) (=> (rotr_imm 0 $x) $x) (=> (ishl_imm 0 $x) $x) (=> (ushr_imm 0 $x) $x) (=> (sshr_imm 0 $x) $x) ;; Replace with zero. (=> (imul_imm 0 $x) 0) (=> (band_imm 0 $x) 0) ;; Replace with negative 1. (=> (bor_imm -1 $x) -1) ;; Transform `[(x << N) >> N]` into a (un)signed-extending move. ;; ;; i16 -> i8 -> i16 (=> (when (ushr_imm 8 (ishl_imm 8 $x)) (bit-width $x 16)) (uextend{i16} (ireduce{i8} $x))) (=> (when (sshr_imm 8 (ishl_imm 8 $x)) (bit-width $x 16)) (sextend{i16} (ireduce{i8} $x))) ;; i32 -> i8 -> i32 (=> (when (ushr_imm 24 (ishl_imm 24 $x)) (bit-width $x 32)) (uextend{i32} (ireduce{i8} $x))) (=> (when (sshr_imm 24 (ishl_imm 24 $x)) (bit-width $x 32)) (sextend{i32} (ireduce{i8} $x))) ;; i32 -> i16 -> i32 (=> (when (ushr_imm 16 (ishl_imm 16 $x)) (bit-width $x 32)) (uextend{i32} (ireduce{i16} $x))) (=> (when (sshr_imm 16 (ishl_imm 16 $x)) (bit-width $x 32)) (sextend{i32} (ireduce{i16} $x))) ;; i64 -> i8 -> i64 (=> (when (ushr_imm 56 (ishl_imm 56 $x)) (bit-width $x 64)) (uextend{i64} (ireduce{i8} $x))) (=> (when (sshr_imm 56 (ishl_imm 56 $x)) (bit-width $x 64)) (sextend{i64} (ireduce{i8} $x))) ;; i64 -> i16 -> i64 (=> (when (ushr_imm 48 (ishl_imm 48 $x)) (bit-width $x 64)) (uextend{i64} (ireduce{i16} $x))) (=> (when (sshr_imm 48 (ishl_imm 48 $x)) (bit-width $x 64)) (sextend{i64} (ireduce{i16} $x))) ;; i64 -> i32 -> i64 (=> (when (ushr_imm 32 (ishl_imm 32 $x)) (bit-width $x 64)) (uextend{i64} (ireduce{i32} $x))) (=> (when (sshr_imm 32 (ishl_imm 32 $x)) (bit-width $x 64)) (sextend{i64} (ireduce{i32} $x))) ;; Fold away redundant `bint` instructions that accept both integer and boolean ;; arguments. (=> (select (bint $x) $y $z) (select $x $y $z)) (=> (brz (bint $x)) (brz $x)) (=> (brnz (bint $x)) (brnz $x)) (=> (trapz (bint $x)) (trapz $x)) (=> (trapnz (bint $x)) (trapnz $x)) ;; Fold comparisons into branch operations when possible. ;; ;; This matches against operations which compare against zero, then use the ;; result in a `brz` or `brnz` branch. It folds those two operations into a ;; single `brz` or `brnz`. (=> (brnz (icmp_imm ne 0 $x)) (brnz $x)) (=> (brz (icmp_imm ne 0 $x)) (brz $x)) (=> (brnz (icmp_imm eq 0 $x)) (brz $x)) (=> (brz (icmp_imm eq 0 $x)) (brnz $x)) ;; Division and remainder by constants. ;; ;; TODO: this section is incomplete, and a bunch of related optimizations are ;; still hand-coded in `simple_preopt.rs`. ;; (Division by one is handled above.) ;; Remainder by one is zero. (=> (urem_imm 1 $x) 0) (=> (srem_imm 1 $x) 0) ;; Division by a power of two -> shift right. (=> (when (udiv_imm $C $x) (is-power-of-two $C)) (ushr_imm $(log2 $C) $x)) ;; Remainder by a power of two -> bitwise and with decreased by one constant. (=> (when (urem_imm $C $x) (is-power-of-two $C) (fits-in-native-word $C)) (band_imm $(isub $C 1) $x))