summaryrefslogtreecommitdiffstats
path: root/third_party/rust/cranelift-codegen/src/preopt.peepmatic
blob: cd988ff7d95651e4318c0b355edf1d89e3cbd12d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
;; 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))