xref: /aosp_15_r20/external/mesa3d/src/panfrost/midgard/midgard_opt_prop.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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