vc4: Pack the unorm-packing bits into a src MUL instruction when possible.
[mesa.git] / src / gallium / drivers / vc4 / vc4_opt_algebraic.c
1 /*
2 * Copyright © 2014 Broadcom
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
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 /**
25 * @file vc4_opt_algebraic.c
26 *
27 * This is the optimization pass for miscellaneous changes to instructions
28 * where we can simplify the operation by some knowledge about the specific
29 * operations.
30 *
31 * Mostly this will be a matter of turning things into MOVs so that they can
32 * later be copy-propagated out.
33 */
34
35 #include "vc4_qir.h"
36 #include "util/u_math.h"
37
38 static bool debug;
39
40 static void
41 dump_from(struct vc4_compile *c, struct qinst *inst)
42 {
43 if (!debug)
44 return;
45
46 fprintf(stderr, "optimizing: ");
47 qir_dump_inst(c, inst);
48 fprintf(stderr, "\n");
49 }
50
51 static void
52 dump_to(struct vc4_compile *c, struct qinst *inst)
53 {
54 if (!debug)
55 return;
56
57 fprintf(stderr, "to: ");
58 qir_dump_inst(c, inst);
59 fprintf(stderr, "\n");
60 }
61
62 static bool
63 is_constant_value(struct vc4_compile *c, struct qreg reg,
64 uint32_t val)
65 {
66 if (reg.file == QFILE_UNIF &&
67 c->uniform_contents[reg.index] == QUNIFORM_CONSTANT &&
68 c->uniform_data[reg.index] == val) {
69 return true;
70 }
71
72 if (reg.file == QFILE_SMALL_IMM && reg.index == val)
73 return true;
74
75 return false;
76 }
77
78 static bool
79 is_zero(struct vc4_compile *c, struct qreg reg)
80 {
81 reg = qir_follow_movs(c, reg);
82 return is_constant_value(c, reg, 0);
83 }
84
85 static bool
86 is_1f(struct vc4_compile *c, struct qreg reg)
87 {
88 reg = qir_follow_movs(c, reg);
89 return is_constant_value(c, reg, fui(1.0));
90 }
91
92 static void
93 replace_with_mov(struct vc4_compile *c, struct qinst *inst, struct qreg arg)
94 {
95 dump_from(c, inst);
96 inst->op = QOP_MOV;
97 inst->src[0] = arg;
98 inst->src[1] = c->undef;
99 dump_to(c, inst);
100 }
101
102 static bool
103 replace_x_0_with_x(struct vc4_compile *c,
104 struct qinst *inst,
105 int arg)
106 {
107 if (!is_zero(c, inst->src[arg]))
108 return false;
109 replace_with_mov(c, inst, inst->src[1 - arg]);
110 return true;
111 }
112
113 static bool
114 replace_x_0_with_0(struct vc4_compile *c,
115 struct qinst *inst,
116 int arg)
117 {
118 if (!is_zero(c, inst->src[arg]))
119 return false;
120 replace_with_mov(c, inst, inst->src[arg]);
121 return true;
122 }
123
124 static bool
125 fmul_replace_one(struct vc4_compile *c,
126 struct qinst *inst,
127 int arg)
128 {
129 if (!is_1f(c, inst->src[arg]))
130 return false;
131 replace_with_mov(c, inst, inst->src[1 - arg]);
132 return true;
133 }
134
135 bool
136 qir_opt_algebraic(struct vc4_compile *c)
137 {
138 bool progress = false;
139
140 list_for_each_entry(struct qinst, inst, &c->instructions, link) {
141 switch (inst->op) {
142 case QOP_SEL_X_Y_ZS:
143 case QOP_SEL_X_Y_ZC:
144 case QOP_SEL_X_Y_NS:
145 case QOP_SEL_X_Y_NC:
146 if (is_zero(c, inst->src[1])) {
147 /* Replace references to a 0 uniform value
148 * with the SEL_X_0 equivalent.
149 */
150 dump_from(c, inst);
151 inst->op -= (QOP_SEL_X_Y_ZS - QOP_SEL_X_0_ZS);
152 inst->src[1] = c->undef;
153 progress = true;
154 dump_to(c, inst);
155 break;
156 }
157
158 if (is_zero(c, inst->src[0])) {
159 /* Replace references to a 0 uniform value
160 * with the SEL_X_0 equivalent, flipping the
161 * condition being evaluated since the operand
162 * order is flipped.
163 */
164 dump_from(c, inst);
165 inst->op -= QOP_SEL_X_Y_ZS;
166 inst->op ^= 1;
167 inst->op += QOP_SEL_X_0_ZS;
168 inst->src[0] = inst->src[1];
169 inst->src[1] = c->undef;
170 progress = true;
171 dump_to(c, inst);
172 break;
173 }
174
175 break;
176
177 case QOP_FSUB:
178 case QOP_SUB:
179 if (is_zero(c, inst->src[1])) {
180 replace_with_mov(c, inst, inst->src[0]);
181 }
182 break;
183
184 case QOP_ADD:
185 if (replace_x_0_with_x(c, inst, 0) ||
186 replace_x_0_with_x(c, inst, 1)) {
187 progress = true;
188 break;
189 }
190 break;
191
192 case QOP_FADD:
193 if (replace_x_0_with_x(c, inst, 0) ||
194 replace_x_0_with_x(c, inst, 1)) {
195 progress = true;
196 break;
197 }
198
199 /* FADD(a, FSUB(0, b)) -> FSUB(a, b) */
200 if (inst->src[1].file == QFILE_TEMP &&
201 c->defs[inst->src[1].index] &&
202 c->defs[inst->src[1].index]->op == QOP_FSUB) {
203 struct qinst *fsub = c->defs[inst->src[1].index];
204 if (is_zero(c, fsub->src[0])) {
205 dump_from(c, inst);
206 inst->op = QOP_FSUB;
207 inst->src[1] = fsub->src[1];
208 progress = true;
209 dump_to(c, inst);
210 break;
211 }
212 }
213
214 /* FADD(FSUB(0, b), a) -> FSUB(a, b) */
215 if (inst->src[0].file == QFILE_TEMP &&
216 c->defs[inst->src[0].index] &&
217 c->defs[inst->src[0].index]->op == QOP_FSUB) {
218 struct qinst *fsub = c->defs[inst->src[0].index];
219 if (is_zero(c, fsub->src[0])) {
220 dump_from(c, inst);
221 inst->op = QOP_FSUB;
222 inst->src[0] = inst->src[1];
223 inst->src[1] = fsub->src[1];
224 dump_to(c, inst);
225 progress = true;
226 break;
227 }
228 }
229 break;
230
231 case QOP_FMUL:
232 if (!inst->dst.pack &&
233 (replace_x_0_with_0(c, inst, 0) ||
234 replace_x_0_with_0(c, inst, 1) ||
235 fmul_replace_one(c, inst, 0) ||
236 fmul_replace_one(c, inst, 1))) {
237 progress = true;
238 break;
239 }
240 break;
241
242 case QOP_MUL24:
243 if (!inst->dst.pack &&
244 (replace_x_0_with_0(c, inst, 0) ||
245 replace_x_0_with_0(c, inst, 1))) {
246 progress = true;
247 break;
248 }
249 break;
250
251 case QOP_AND:
252 if (replace_x_0_with_0(c, inst, 0) ||
253 replace_x_0_with_0(c, inst, 1)) {
254 progress = true;
255 break;
256 }
257
258 if (is_constant_value(c, inst->src[0], ~0)) {
259 replace_with_mov(c, inst, inst->src[1]);
260 progress = true;
261 break;
262 }
263 if (is_constant_value(c, inst->src[1], ~0)) {
264 replace_with_mov(c, inst, inst->src[0]);
265 progress = true;
266 break;
267 }
268 break;
269
270 case QOP_OR:
271 if (replace_x_0_with_x(c, inst, 0) ||
272 replace_x_0_with_x(c, inst, 1)) {
273 progress = true;
274 break;
275 }
276 break;
277
278 default:
279 break;
280 }
281 }
282
283 return progress;
284 }