xref: /aosp_15_r20/external/mesa3d/src/compiler/nir/nir_opt_generate_bfi.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright 2024 Intel Corporation
3  * SPDX-License-Identifier: MIT
4  */
5 
6 /**
7  * \file
8  * Identify sequences of logical operations to convert to bfi
9  *
10  * It is difficult for opt_algebraic to match general expressions like
11  *
12  *    (a & some_constant) | (b & ~some_constant)
13  *
14  * Common cases like some_constant = 0x7fffffff can be added, but this may
15  * miss other opportunities. This pass implements that general pattern
16  * matching.
17  *
18  * Either nir_op_bfi or nir_op_bitfield_select may be generated by this pass.
19  *
20  * Future work may also detect cases like:
21  *
22  *    (a & some_constant) | ~(b | some_constant)
23  *    ~((a | some_constant) & (b | ~some_constant))
24  *    etc.
25  */
26 
27 #include "nir_builder.h"
28 
29 static bool
parse_iand(nir_scalar alu,nir_scalar * value,uint32_t * mask)30 parse_iand(nir_scalar alu, nir_scalar *value, uint32_t *mask)
31 {
32    if (nir_scalar_alu_op(alu) == nir_op_iand) {
33       /* If both source are constants, do not perform the conversion. There
34        * are lowerings in opt_algebraic that can generate this pattern on
35        * platforms that set has_bfi and avoid_ternary_with_two_constants.
36        * Undoing that lowering would result in infinite optimization loops.
37        */
38       nir_scalar left = nir_scalar_chase_alu_src(alu, 0);
39       nir_scalar right = nir_scalar_chase_alu_src(alu, 1);
40       if (nir_scalar_is_const(left) && nir_scalar_is_const(right))
41          return false;
42 
43       if (nir_scalar_is_const(left)) {
44          *mask = nir_scalar_as_uint(left);
45          *value = right;
46          return true;
47       } else if (nir_scalar_is_const(right)) {
48          *mask = nir_scalar_as_uint(right);
49          *value = left;
50          return true;
51       }
52    } else if (nir_scalar_alu_op(alu) == nir_op_extract_u16 ||
53               nir_scalar_alu_op(alu) == nir_op_extract_u8) {
54       /* There may be leftovers from opt_algebraic that haven't been constant
55        * folded yet.
56        */
57       nir_scalar left = nir_scalar_chase_alu_src(alu, 0);
58       if (nir_scalar_is_const(left))
59          return false;
60 
61       if (nir_scalar_as_uint(nir_scalar_chase_alu_src(alu, 1)) == 0) {
62          *mask = nir_scalar_alu_op(alu) == nir_op_extract_u16 ? 0x0000ffff : 0x000000ff;
63          *value = left;
64          return true;
65       }
66    }
67 
68    return false;
69 }
70 
71 static bool
nir_opt_generate_bfi_instr(nir_builder * b,nir_alu_instr * alu,UNUSED void * cb_data)72 nir_opt_generate_bfi_instr(nir_builder *b,
73                            nir_alu_instr *alu,
74                            UNUSED void *cb_data)
75 {
76    /* Since none of the source bits will overlap, these are equvalent. */
77    if ((alu->op != nir_op_ior &&
78         alu->op != nir_op_ixor &&
79         alu->op != nir_op_iadd) ||
80        alu->def.num_components != 1 || alu->def.bit_size != 32)
81       return false;
82 
83    nir_scalar alu_scalar = nir_get_scalar(&alu->def, 0);
84    nir_scalar left = nir_scalar_chase_alu_src(alu_scalar, 0);
85    nir_scalar right = nir_scalar_chase_alu_src(alu_scalar, 1);
86 
87    if (!nir_scalar_is_alu(left) || !nir_scalar_is_alu(right))
88       return false;
89 
90    nir_scalar src1;
91    nir_scalar src2;
92    uint32_t mask1;
93    uint32_t mask2;
94 
95    if (!parse_iand(left, &src1, &mask1))
96       return false;
97 
98    if (!parse_iand(right, &src2, &mask2))
99       return false;
100 
101    if (mask1 != ~mask2)
102       return false;
103 
104    nir_scalar insert;
105    nir_scalar base;
106    uint32_t mask;
107 
108    /* The mask used by the bfi instruction must be odd. When the mask is odd,
109     * the implict shift applied by the bfi is by zero bits. Since one of the
110     * masks must be odd, the rule can always be applied.
111     *
112     * bitfield_select does not have this restriction, but it doesn't hurt.
113     */
114    if ((mask1 & 1) != 0) {
115       /* Because mask1 == ~mask2. */
116       assert((mask2 & 1) == 0);
117 
118       mask = mask1;
119       insert = src1;
120       base = src2;
121    } else {
122       /* Because mask1 == ~mask2. */
123       assert((mask2 & 1) != 0);
124 
125       mask = mask2;
126       insert = src2;
127       base = src1;
128    }
129 
130    b->cursor = nir_before_instr(&alu->instr);
131 
132    nir_def *bfi;
133 
134    if (b->shader->options->has_bfi) {
135       bfi = nir_bfi(b,
136                     nir_imm_int(b, mask),
137                     nir_channel(b, insert.def, insert.comp),
138                     nir_channel(b, base.def, base.comp));
139    } else {
140       assert(b->shader->options->has_bitfield_select);
141 
142       bfi = nir_bitfield_select(b,
143                                 nir_imm_int(b, mask),
144                                 nir_channel(b, insert.def, insert.comp),
145                                 nir_channel(b, base.def, base.comp));
146    }
147 
148    nir_def_replace(&alu->def, bfi);
149    return true;
150 }
151 
152 bool
nir_opt_generate_bfi(nir_shader * shader)153 nir_opt_generate_bfi(nir_shader *shader)
154 {
155    if (!shader->options->has_bfi && !shader->options->has_bitfield_select)
156       return false;
157 
158    return nir_shader_alu_pass(shader, nir_opt_generate_bfi_instr,
159                               nir_metadata_control_flow, NULL);
160 }
161