pan/midgard: Add csel invert optimization
[mesa.git] / src / panfrost / midgard / midgard_opt_invert.c
1 /*
2 * Copyright (C) 2019 Collabora, Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include "compiler.h"
25 #include "midgard_ops.h"
26
27 /* Lowers the invert field on instructions to a dedicated inot (inor)
28 * instruction instead, as invert is not always supported natively by the
29 * hardware */
30
31 void
32 midgard_lower_invert(compiler_context *ctx, midgard_block *block)
33 {
34 mir_foreach_instr_in_block_safe(block, ins) {
35 if (ins->type != TAG_ALU_4) continue;
36 if (!ins->invert) continue;
37
38 unsigned temp = make_compiler_temp(ctx);
39
40 midgard_instruction not = {
41 .type = TAG_ALU_4,
42 .mask = ins->mask,
43 .src = { temp, ~0, ~0 },
44 .dest = ins->dest,
45 .has_inline_constant = true,
46 .alu = {
47 .op = midgard_alu_op_inor,
48 /* TODO: i16 */
49 .reg_mode = midgard_reg_mode_32,
50 .dest_override = midgard_dest_override_none,
51 .outmod = midgard_outmod_int_wrap,
52 .src1 = vector_alu_srco_unsigned(blank_alu_src),
53 .src2 = vector_alu_srco_unsigned(zero_alu_src)
54 },
55 };
56
57 ins->dest = temp;
58 ins->invert = false;
59 mir_insert_instruction_before(ctx, mir_next_op(ins), not);
60 }
61 }
62
63 /* Propagate the .not up to the source */
64
65 bool
66 midgard_opt_not_propagate(compiler_context *ctx, midgard_block *block)
67 {
68 bool progress = false;
69
70 mir_foreach_instr_in_block_safe(block, ins) {
71 if (ins->type != TAG_ALU_4) continue;
72 if (ins->alu.op != midgard_alu_op_imov) continue;
73 if (!ins->invert) continue;
74 if (mir_nontrivial_source2_mod_simple(ins)) continue;
75 if (ins->src[1] & IS_REG) continue;
76
77 /* Is it beneficial to propagate? */
78 if (!mir_single_use(ctx, ins->src[1])) continue;
79
80 /* We found an imov.not, propagate the invert back */
81
82 mir_foreach_instr_in_block_from_rev(block, v, mir_prev_op(ins)) {
83 if (v->dest != ins->src[1]) continue;
84 if (v->type != TAG_ALU_4) break;
85
86 v->invert = !v->invert;
87 ins->invert = false;
88 progress |= true;
89 break;
90 }
91 }
92
93 return progress;
94 }
95
96 /* With that lowering out of the way, we can focus on more interesting
97 * optimizations. One easy one is fusing inverts into bitwise operations:
98 *
99 * ~iand = inand
100 * ~ior = inor
101 * ~ixor = inxor
102 */
103
104 static bool
105 mir_is_bitwise(midgard_instruction *ins)
106 {
107 switch (ins->alu.op) {
108 case midgard_alu_op_iand:
109 case midgard_alu_op_ior:
110 case midgard_alu_op_ixor:
111 return true;
112 default:
113 return false;
114 }
115 }
116
117 static midgard_alu_op
118 mir_invert_op(midgard_alu_op op)
119 {
120 switch (op) {
121 case midgard_alu_op_iand:
122 return midgard_alu_op_inand;
123 case midgard_alu_op_ior:
124 return midgard_alu_op_inor;
125 case midgard_alu_op_ixor:
126 return midgard_alu_op_inxor;
127 default:
128 unreachable("Op not invertible");
129 }
130 }
131
132 static midgard_alu_op
133 mir_demorgan_op(midgard_alu_op op)
134 {
135 switch (op) {
136 case midgard_alu_op_iand:
137 return midgard_alu_op_inor;
138 case midgard_alu_op_ior:
139 return midgard_alu_op_inand;
140 default:
141 unreachable("Op not De Morgan-able");
142 }
143 }
144
145 static midgard_alu_op
146 mir_notright_op(midgard_alu_op op)
147 {
148 switch (op) {
149 case midgard_alu_op_iand:
150 return midgard_alu_op_iandnot;
151 case midgard_alu_op_ior:
152 return midgard_alu_op_iornot;
153 default:
154 unreachable("Op not right able");
155 }
156 }
157
158 bool
159 midgard_opt_fuse_dest_invert(compiler_context *ctx, midgard_block *block)
160 {
161 bool progress = false;
162
163 mir_foreach_instr_in_block_safe(block, ins) {
164 /* Search for inverted bitwise */
165 if (ins->type != TAG_ALU_4) continue;
166 if (!mir_is_bitwise(ins)) continue;
167 if (!ins->invert) continue;
168
169 ins->alu.op = mir_invert_op(ins->alu.op);
170 ins->invert = false;
171 progress |= true;
172 }
173
174 return progress;
175 }
176
177 /* Next up, we can fuse inverts into the sources of bitwise ops:
178 *
179 * ~a & b = b & ~a = iandnot(b, a)
180 * a & ~b = iandnot(a, b)
181 * ~a & ~b = ~(a | b) = inor(a, b)
182 *
183 * ~a | b = b | ~a = iornot(b, a)
184 * a | ~b = iornot(a, b)
185 * ~a | ~b = ~(a & b) = inand(a, b)
186 *
187 * ~a ^ b = ~(a ^ b) = inxor(a, b)
188 * a ^ ~b = ~(a ^ b) + inxor(a, b)
189 * ~a ^ ~b = a ^ b
190 * ~(a ^ b) = inxor(a, b)
191 */
192
193 static bool
194 mir_strip_inverted(compiler_context *ctx, unsigned node)
195 {
196 if (node >= SSA_FIXED_MINIMUM)
197 return false;
198
199 /* Strips and returns the invert off a node */
200 mir_foreach_instr_global(ctx, ins) {
201 if (ins->compact_branch) continue;
202 if (ins->dest != node) continue;
203
204 bool status = ins->invert;
205 ins->invert = false;
206 return status;
207 }
208
209 unreachable("Invalid node stripped");
210 }
211
212 static bool
213 is_ssa_or_constant(unsigned node)
214 {
215 return !(node & IS_REG) || (node == SSA_FIXED_REGISTER(26));
216 }
217
218 bool
219 midgard_opt_fuse_src_invert(compiler_context *ctx, midgard_block *block)
220 {
221 bool progress = false;
222
223 mir_foreach_instr_in_block_safe(block, ins) {
224 /* Search for inverted bitwise */
225 if (ins->type != TAG_ALU_4) continue;
226 if (!mir_is_bitwise(ins)) continue;
227 if (ins->invert) continue;
228
229 if (!is_ssa_or_constant(ins->src[0])) continue;
230 if (!is_ssa_or_constant(ins->src[1])) continue;
231 if (!mir_single_use(ctx, ins->src[0])) continue;
232 if (!ins->has_inline_constant && !mir_single_use(ctx, ins->src[1])) continue;
233
234 bool not_a = mir_strip_inverted(ctx, ins->src[0]);
235 bool not_b =
236 ins->has_inline_constant ? false :
237 mir_strip_inverted(ctx, ins->src[1]);
238
239 /* Edge case: if src0 == src1, it'll've been stripped */
240 if ((ins->src[0] == ins->src[1]) && !ins->has_inline_constant)
241 not_b = not_a;
242
243 progress |= (not_a || not_b);
244
245 /* No point */
246 if (!(not_a || not_b)) continue;
247
248 bool both = not_a && not_b;
249 bool left = not_a && !not_b;
250 bool right = !not_a && not_b;
251
252 /* No-op, but we got to strip the inverts */
253 if (both && ins->alu.op == midgard_alu_op_ixor)
254 continue;
255
256 if (both) {
257 ins->alu.op = mir_demorgan_op(ins->alu.op);
258 } else if (right || (left && !ins->has_inline_constant)) {
259 /* Commute arguments */
260 if (left)
261 mir_flip(ins);
262
263 ins->alu.op = mir_notright_op(ins->alu.op);
264 } else if (left && ins->has_inline_constant) {
265 /* Some special transformations:
266 *
267 * ~A & c = ~(~(~A) | (~c)) = ~(A | ~c) = inor(A, ~c)
268 * ~A | c = ~(~(~A) & (~c)) = ~(A & ~c) = inand(A, ~c)
269 */
270
271 ins->alu.op = mir_demorgan_op(ins->alu.op);
272 ins->inline_constant = ~ins->inline_constant;
273 }
274 }
275
276 return progress;
277 }
278
279 /* Optimizes a .not away when used as the source of a conditional select:
280 *
281 * csel(a, b, c) = { b if a, c if !a }
282 * csel(!a, b, c) = { b if !a, c if !(!a) } = { c if a, b if !a } = csel(a, c, b)
283 * csel(!a, b, c) = csel(a, c, b)
284 */
285
286 bool
287 midgard_opt_csel_invert(compiler_context *ctx, midgard_block *block)
288 {
289 bool progress = false;
290
291 mir_foreach_instr_in_block_safe(block, ins) {
292 if (ins->type != TAG_ALU_4) continue;
293 if (!OP_IS_CSEL(ins->alu.op)) continue;
294 if (!mir_single_use(ctx, ins->src[2])) continue;
295 if (!mir_strip_inverted(ctx, ins->src[2])) continue;
296
297 mir_flip(ins);
298 progress |= true;
299 }
300
301 return progress;
302 }