1 /*
2 * Copyright 2023 Valve Corporation
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "compiler.h"
7 #include "midgard.h"
8 #include "midgard_ops.h"
9 #include "nir.h"
10
11 static bool
is_inot(midgard_instruction * I)12 is_inot(midgard_instruction *I)
13 {
14 return I->type == TAG_ALU_4 && I->op == midgard_alu_op_inor &&
15 I->has_inline_constant && I->inline_constant == 0;
16 }
17
18 static bool
try_fold_fmov_src(midgard_instruction * use,unsigned src_idx,midgard_instruction * fmov)19 try_fold_fmov_src(midgard_instruction *use, unsigned src_idx,
20 midgard_instruction *fmov)
21 {
22 if (use->type != TAG_ALU_4)
23 return false;
24 if (fmov->has_constants || fmov->has_inline_constant)
25 return false;
26 if (mir_nontrivial_outmod(fmov))
27 return false;
28
29 /* Don't propagate into non-float instructions */
30 if (nir_alu_type_get_base_type(use->src_types[src_idx]) != nir_type_float)
31 return false;
32
33 /* TODO: Size conversions not handled yet */
34 if (use->src_types[src_idx] != fmov->src_types[1])
35 return false;
36
37 if (use->src_abs[src_idx]) {
38 /* abs(abs(x)) = abs(x) and abs(-x) = abs(x) */
39 } else {
40 /* -(-(abs(x))) = abs(x) */
41 use->src_abs[src_idx] = fmov->src_abs[1];
42 use->src_neg[src_idx] ^= fmov->src_neg[1];
43 }
44
45 use->src[src_idx] = fmov->src[1];
46 mir_compose_swizzle(use->swizzle[src_idx], fmov->swizzle[1],
47 use->swizzle[src_idx]);
48 return true;
49 }
50
51 static bool
try_fold_inot(midgard_instruction * use,unsigned src_idx,midgard_instruction * inot)52 try_fold_inot(midgard_instruction *use, unsigned src_idx,
53 midgard_instruction *inot)
54 {
55 /* TODO: Size conversions not handled yet */
56 if (nir_alu_type_get_type_size(use->src_types[src_idx]) !=
57 nir_alu_type_get_type_size(inot->src_types[0]))
58 return false;
59
60 if (use->compact_branch) {
61 use->branch.invert_conditional ^= true;
62 } else if (use->type == TAG_ALU_4) {
63 switch (use->op) {
64 case midgard_alu_op_iand:
65 case midgard_alu_op_ior:
66 case midgard_alu_op_ixor:
67 break;
68 default:
69 return false;
70 }
71
72 use->src_invert[src_idx] ^= true;
73 mir_compose_swizzle(use->swizzle[src_idx], inot->swizzle[0],
74 use->swizzle[src_idx]);
75 } else {
76 return false;
77 }
78
79 use->src[src_idx] = inot->src[0];
80 return true;
81 }
82
83 static bool
midgard_opt_prop_forward(compiler_context * ctx)84 midgard_opt_prop_forward(compiler_context *ctx)
85 {
86 bool progress = false;
87
88 midgard_instruction **defs =
89 calloc(ctx->temp_count, sizeof(midgard_instruction *));
90
91 mir_foreach_block(ctx, block_) {
92 midgard_block *block = (midgard_block *)block_;
93
94 mir_foreach_instr_in_block(block, I) {
95 /* Record SSA defs */
96 if (mir_is_ssa(I->dest)) {
97 assert(I->dest < ctx->temp_count);
98 defs[I->dest] = I;
99 }
100
101 mir_foreach_src(I, s) {
102 unsigned src = I->src[s];
103 if (!mir_is_ssa(src))
104 continue;
105
106 /* Try to fold a source mod in */
107 assert(src < ctx->temp_count);
108 midgard_instruction *def = defs[src];
109 if (def == NULL)
110 continue;
111
112 if (def->type == TAG_ALU_4 && def->op == midgard_alu_op_fmov) {
113 progress |= try_fold_fmov_src(I, s, def);
114 } else if (is_inot(def)) {
115 progress |= try_fold_inot(I, s, def);
116 }
117 }
118 }
119 }
120
121 free(defs);
122 return progress;
123 }
124
125 enum outmod_state {
126 outmod_unknown = 0,
127 outmod_clamp_0_1,
128 outmod_clamp_m1_1,
129 outmod_clamp_0_inf,
130 outmod_incompatible,
131 };
132
133 static enum outmod_state
outmod_to_state(unsigned outmod)134 outmod_to_state(unsigned outmod)
135 {
136 switch (outmod) {
137 case midgard_outmod_clamp_0_1:
138 return outmod_clamp_0_1;
139 case midgard_outmod_clamp_m1_1:
140 return outmod_clamp_m1_1;
141 case midgard_outmod_clamp_0_inf:
142 return outmod_clamp_0_inf;
143 default:
144 return outmod_incompatible;
145 }
146 }
147
148 static enum outmod_state
union_outmod_state(enum outmod_state a,enum outmod_state b)149 union_outmod_state(enum outmod_state a, enum outmod_state b)
150 {
151 if (a == outmod_unknown)
152 return b;
153 else if (b == outmod_unknown)
154 return a;
155 else if (a == b)
156 return a /* b */;
157 else
158 return outmod_incompatible;
159 }
160
161 static bool
midgard_opt_prop_backward(compiler_context * ctx)162 midgard_opt_prop_backward(compiler_context *ctx)
163 {
164 bool progress = false;
165 enum outmod_state *state = calloc(ctx->temp_count, sizeof(*state));
166 BITSET_WORD *folded = calloc(BITSET_WORDS(ctx->temp_count), sizeof(*folded));
167
168 /* Scan for outmod states */
169 mir_foreach_instr_global(ctx, I) {
170 if (I->type == TAG_ALU_4 && I->op == midgard_alu_op_fmov &&
171 !I->src_neg[1] && !I->src_abs[1] && mir_is_ssa(I->src[1])) {
172
173 enum outmod_state outmod = outmod_to_state(I->outmod);
174 state[I->src[1]] = union_outmod_state(state[I->src[1]], outmod);
175 } else {
176 /* Anything used as any other source cannot have an outmod folded in */
177 mir_foreach_src(I, s) {
178 if (mir_is_ssa(I->src[s]))
179 state[I->src[s]] = outmod_incompatible;
180 }
181 }
182 }
183
184 /* Apply outmods */
185 mir_foreach_instr_global(ctx, I) {
186 if (!mir_is_ssa(I->dest))
187 continue;
188
189 if (I->type != TAG_ALU_4 && I->type != TAG_TEXTURE_4)
190 continue;
191
192 if (nir_alu_type_get_base_type(I->dest_type) != nir_type_float)
193 continue;
194
195 if (I->outmod != midgard_outmod_none)
196 continue;
197
198 switch (state[I->dest]) {
199 case outmod_clamp_0_1:
200 I->outmod = midgard_outmod_clamp_0_1;
201 break;
202 case outmod_clamp_m1_1:
203 I->outmod = midgard_outmod_clamp_m1_1;
204 break;
205 case outmod_clamp_0_inf:
206 I->outmod = midgard_outmod_clamp_0_inf;
207 break;
208 default:
209 break;
210 }
211
212 if (I->outmod != midgard_outmod_none) {
213 BITSET_SET(folded, I->dest);
214 }
215 }
216
217 /* Strip outmods from FMOVs to let copyprop go ahead */
218 mir_foreach_instr_global(ctx, I) {
219 if (I->type == TAG_ALU_4 && I->op == midgard_alu_op_fmov &&
220 mir_is_ssa(I->src[1]) && BITSET_TEST(folded, I->src[1])) {
221
222 I->outmod = midgard_outmod_none;
223 }
224 }
225
226 free(state);
227 free(folded);
228 return progress;
229 }
230
231 bool
midgard_opt_prop(compiler_context * ctx)232 midgard_opt_prop(compiler_context *ctx)
233 {
234 bool progress = false;
235 mir_compute_temp_count(ctx);
236 progress |= midgard_opt_prop_forward(ctx);
237 progress |= midgard_opt_prop_backward(ctx);
238 return progress;
239 }
240