freedreno/ir3: drop instr_clone() stuff
[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 qinst **defs, 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 qinst **defs, struct qreg reg)
80 {
81 reg = qir_follow_movs(defs, reg);
82 return is_constant_value(c, defs, reg, 0);
83 }
84
85 static bool
86 is_1f(struct vc4_compile *c, struct qinst **defs, struct qreg reg)
87 {
88 reg = qir_follow_movs(defs, reg);
89 return is_constant_value(c, defs, 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 add_replace_zero(struct vc4_compile *c,
104 struct qinst **defs,
105 struct qinst *inst,
106 int arg)
107 {
108 if (!is_zero(c, defs, inst->src[arg]))
109 return false;
110 replace_with_mov(c, inst, inst->src[1 - arg]);
111 return true;
112 }
113
114 static bool
115 fmul_replace_zero(struct vc4_compile *c,
116 struct qinst **defs,
117 struct qinst *inst,
118 int arg)
119 {
120 if (!is_zero(c, defs, inst->src[arg]))
121 return false;
122 replace_with_mov(c, inst, inst->src[arg]);
123 return true;
124 }
125
126 static bool
127 fmul_replace_one(struct vc4_compile *c,
128 struct qinst **defs,
129 struct qinst *inst,
130 int arg)
131 {
132 if (!is_1f(c, defs, inst->src[arg]))
133 return false;
134 replace_with_mov(c, inst, inst->src[1 - arg]);
135 return true;
136 }
137
138 bool
139 qir_opt_algebraic(struct vc4_compile *c)
140 {
141 bool progress = false;
142 struct simple_node *node;
143 struct qinst *defs[c->num_temps];
144
145 foreach(node, &c->instructions) {
146 struct qinst *inst = (struct qinst *)node;
147
148 if (inst->dst.file == QFILE_TEMP)
149 defs[inst->dst.index] = inst;
150
151 switch (inst->op) {
152 case QOP_SF:
153 /* SF just looks at the sign bit, or whether all the
154 * bits are 0. This is preserved across an itof
155 * transformation.
156 */
157 if (inst->src[0].file == QFILE_TEMP &&
158 defs[inst->src[0].index]->op == QOP_ITOF) {
159 dump_from(c, inst);
160 inst->src[0] =
161 defs[inst->src[0].index]->src[0];
162 progress = true;
163 dump_to(c, inst);
164 break;
165 }
166 break;
167
168 case QOP_SEL_X_Y_ZS:
169 case QOP_SEL_X_Y_ZC:
170 case QOP_SEL_X_Y_NS:
171 case QOP_SEL_X_Y_NC:
172 if (qir_reg_equals(inst->src[0], inst->src[1])) {
173 /* Turn "dst = (sf == x) ? a : a)" into
174 * "dst = a"
175 */
176 replace_with_mov(c, inst, inst->src[1]);
177 progress = true;
178 break;
179 }
180
181 if (is_zero(c, defs, inst->src[1])) {
182 /* Replace references to a 0 uniform value
183 * with the SEL_X_0 equivalent.
184 */
185 dump_from(c, inst);
186 inst->op -= (QOP_SEL_X_Y_ZS - QOP_SEL_X_0_ZS);
187 inst->src[1] = c->undef;
188 progress = true;
189 dump_to(c, inst);
190 break;
191 }
192
193 if (is_zero(c, defs, inst->src[0])) {
194 /* Replace references to a 0 uniform value
195 * with the SEL_X_0 equivalent, flipping the
196 * condition being evaluated since the operand
197 * order is flipped.
198 */
199 dump_from(c, inst);
200 inst->op -= QOP_SEL_X_Y_ZS;
201 inst->op ^= 1;
202 inst->op += QOP_SEL_X_0_ZS;
203 inst->src[0] = inst->src[1];
204 inst->src[1] = c->undef;
205 progress = true;
206 dump_to(c, inst);
207 break;
208 }
209
210 break;
211
212 case QOP_FSUB:
213 case QOP_SUB:
214 if (is_zero(c, defs, inst->src[1])) {
215 replace_with_mov(c, inst, inst->src[0]);
216 }
217 break;
218
219 case QOP_ADD:
220 if (add_replace_zero(c, defs, inst, 0) ||
221 add_replace_zero(c, defs, inst, 1)) {
222 progress = true;
223 break;
224 }
225 break;
226
227 case QOP_FADD:
228 if (add_replace_zero(c, defs, inst, 0) ||
229 add_replace_zero(c, defs, inst, 1)) {
230 progress = true;
231 break;
232 }
233
234 /* FADD(a, FSUB(0, b)) -> FSUB(a, b) */
235 if (inst->src[1].file == QFILE_TEMP &&
236 defs[inst->src[1].index]->op == QOP_FSUB) {
237 struct qinst *fsub = defs[inst->src[1].index];
238 if (is_zero(c, defs, fsub->src[0])) {
239 dump_from(c, inst);
240 inst->op = QOP_FSUB;
241 inst->src[1] = fsub->src[1];
242 progress = true;
243 dump_to(c, inst);
244 break;
245 }
246 }
247
248 /* FADD(FSUB(0, b), a) -> FSUB(a, b) */
249 if (inst->src[0].file == QFILE_TEMP &&
250 defs[inst->src[0].index]->op == QOP_FSUB) {
251 struct qinst *fsub = defs[inst->src[0].index];
252 if (is_zero(c, defs, fsub->src[0])) {
253 dump_from(c, inst);
254 inst->op = QOP_FSUB;
255 inst->src[0] = inst->src[1];
256 inst->src[1] = fsub->src[1];
257 dump_to(c, inst);
258 progress = true;
259 break;
260 }
261 }
262 break;
263
264 case QOP_FMUL:
265 if (fmul_replace_zero(c, defs, inst, 0) ||
266 fmul_replace_zero(c, defs, inst, 1) ||
267 fmul_replace_one(c, defs, inst, 0) ||
268 fmul_replace_one(c, defs, inst, 1)) {
269 progress = true;
270 break;
271 }
272 break;
273
274 default:
275 break;
276 }
277 }
278
279 return progress;
280 }