xref: /aosp_15_r20/external/mesa3d/src/panfrost/midgard/midgard_address.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright (C) 2019 Collabora, Ltd.
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 FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  * Authors (Collabora):
24  *    Alyssa Rosenzweig <[email protected]>
25  */
26 
27 #include "compiler.h"
28 
29 /* Midgard's generic load/store instructions, particularly to implement SSBOs
30  * and globals, have support for address arithmetic natively. In particularly,
31  * they take two indirect arguments A, B and two immediates #s, #c, calculating
32  * the address:
33  *
34  *      A + (zext?(B) << #s) + #c
35  *
36  * This allows for fast indexing into arrays. This file tries to pattern match
37  * the offset in NIR with this form to reduce pressure on the ALU pipe.
38  */
39 
40 struct mir_address {
41    nir_scalar A;
42    nir_scalar B;
43 
44    midgard_index_address_format type;
45    unsigned shift;
46    unsigned bias;
47 };
48 
49 static bool
mir_args_ssa(nir_scalar s,unsigned count)50 mir_args_ssa(nir_scalar s, unsigned count)
51 {
52    nir_alu_instr *alu = nir_instr_as_alu(s.def->parent_instr);
53 
54    if (count > nir_op_infos[alu->op].num_inputs)
55       return false;
56 
57    return true;
58 }
59 
60 /* Matches a constant in either slot and moves it to the bias */
61 
62 static void
mir_match_constant(struct mir_address * address)63 mir_match_constant(struct mir_address *address)
64 {
65    if (address->A.def && nir_scalar_is_const(address->A)) {
66       address->bias += nir_scalar_as_uint(address->A);
67       address->A.def = NULL;
68    }
69 
70    if (address->B.def && nir_scalar_is_const(address->B)) {
71       address->bias += nir_scalar_as_uint(address->B);
72       address->B.def = NULL;
73    }
74 }
75 
76 /* Matches an iadd when there is a free slot or constant */
77 
78 /* The offset field is a 18-bit signed integer */
79 #define MAX_POSITIVE_OFFSET ((1 << 17) - 1)
80 
81 static void
mir_match_iadd(struct mir_address * address,bool first_free)82 mir_match_iadd(struct mir_address *address, bool first_free)
83 {
84    if (!address->B.def || !nir_scalar_is_alu(address->B))
85       return;
86 
87    if (!mir_args_ssa(address->B, 2))
88       return;
89 
90    nir_op op = nir_scalar_alu_op(address->B);
91 
92    if (op != nir_op_iadd)
93       return;
94 
95    nir_scalar op1 = nir_scalar_chase_alu_src(address->B, 0);
96    nir_scalar op2 = nir_scalar_chase_alu_src(address->B, 1);
97 
98    if (nir_scalar_is_const(op1) &&
99        nir_scalar_as_uint(op1) <= MAX_POSITIVE_OFFSET) {
100       address->bias += nir_scalar_as_uint(op1);
101       address->B = op2;
102    } else if (nir_scalar_is_const(op2) &&
103               nir_scalar_as_uint(op2) <= MAX_POSITIVE_OFFSET) {
104       address->bias += nir_scalar_as_uint(op2);
105       address->B = op1;
106    } else if (!nir_scalar_is_const(op1) && !nir_scalar_is_const(op2) &&
107               first_free && !address->A.def) {
108       address->A = op1;
109       address->B = op2;
110    }
111 }
112 
113 /* Matches u2u64 and sets type */
114 
115 static void
mir_match_u2u64(struct mir_address * address)116 mir_match_u2u64(struct mir_address *address)
117 {
118    if (!address->B.def || !nir_scalar_is_alu(address->B))
119       return;
120 
121    if (!mir_args_ssa(address->B, 1))
122       return;
123 
124    nir_op op = nir_scalar_alu_op(address->B);
125    if (op != nir_op_u2u64)
126       return;
127    nir_scalar arg = nir_scalar_chase_alu_src(address->B, 0);
128 
129    address->B = arg;
130    address->type = midgard_index_address_u32;
131 }
132 
133 /* Matches i2i64 and sets type */
134 
135 static void
mir_match_i2i64(struct mir_address * address)136 mir_match_i2i64(struct mir_address *address)
137 {
138    if (!address->B.def || !nir_scalar_is_alu(address->B))
139       return;
140 
141    if (!mir_args_ssa(address->B, 1))
142       return;
143 
144    nir_op op = nir_scalar_alu_op(address->B);
145    if (op != nir_op_i2i64)
146       return;
147    nir_scalar arg = nir_scalar_chase_alu_src(address->B, 0);
148 
149    address->B = arg;
150    address->type = midgard_index_address_s32;
151 }
152 
153 /* Matches ishl to shift */
154 
155 static void
mir_match_ishl(struct mir_address * address)156 mir_match_ishl(struct mir_address *address)
157 {
158    if (!address->B.def || !nir_scalar_is_alu(address->B))
159       return;
160 
161    if (!mir_args_ssa(address->B, 2))
162       return;
163 
164    nir_op op = nir_scalar_alu_op(address->B);
165    if (op != nir_op_ishl)
166       return;
167    nir_scalar op1 = nir_scalar_chase_alu_src(address->B, 0);
168    nir_scalar op2 = nir_scalar_chase_alu_src(address->B, 1);
169 
170    if (!nir_scalar_is_const(op2))
171       return;
172 
173    unsigned shift = nir_scalar_as_uint(op2);
174    if (shift > 0x7)
175       return;
176 
177    address->B = op1;
178    address->shift = shift;
179 }
180 
181 /* Strings through mov which can happen from NIR vectorization */
182 
183 static void
mir_match_mov(struct mir_address * address)184 mir_match_mov(struct mir_address *address)
185 {
186    if (address->A.def && nir_scalar_is_alu(address->A)) {
187       nir_op op = nir_scalar_alu_op(address->A);
188 
189       if (op == nir_op_mov && mir_args_ssa(address->A, 1))
190          address->A = nir_scalar_chase_alu_src(address->A, 0);
191    }
192 
193    if (address->B.def && nir_scalar_is_alu(address->B)) {
194       nir_op op = nir_scalar_alu_op(address->B);
195 
196       if (op == nir_op_mov && mir_args_ssa(address->B, 1))
197          address->B = nir_scalar_chase_alu_src(address->B, 0);
198    }
199 }
200 
201 /* Tries to pattern match into mir_address */
202 
203 static struct mir_address
mir_match_offset(nir_def * offset,bool first_free,bool extend)204 mir_match_offset(nir_def *offset, bool first_free, bool extend)
205 {
206    struct mir_address address = {
207       .B = {.def = offset},
208       .type = extend ? midgard_index_address_u64 : midgard_index_address_u32,
209    };
210 
211    mir_match_mov(&address);
212    mir_match_constant(&address);
213    mir_match_mov(&address);
214    mir_match_iadd(&address, first_free);
215    mir_match_mov(&address);
216 
217    if (extend) {
218       mir_match_u2u64(&address);
219       mir_match_i2i64(&address);
220       mir_match_mov(&address);
221    }
222 
223    mir_match_ishl(&address);
224 
225    return address;
226 }
227 
228 void
mir_set_offset(compiler_context * ctx,midgard_instruction * ins,nir_src * offset,unsigned seg)229 mir_set_offset(compiler_context *ctx, midgard_instruction *ins, nir_src *offset,
230                unsigned seg)
231 {
232    for (unsigned i = 0; i < 16; ++i) {
233       ins->swizzle[1][i] = 0;
234       ins->swizzle[2][i] = 0;
235    }
236 
237    /* Sign extend instead of zero extend in case the address is something
238     * like `base + offset + 20`, where offset could be negative. */
239    bool force_sext = (nir_src_bit_size(*offset) < 64);
240    bool first_free = (seg == LDST_GLOBAL);
241 
242    struct mir_address match = mir_match_offset(offset->ssa, first_free, true);
243 
244    if (match.A.def) {
245       unsigned bitsize = match.A.def->bit_size;
246       assert(bitsize == 32 || bitsize == 64);
247 
248       ins->src[1] = nir_ssa_index(match.A.def);
249       ins->swizzle[1][0] = match.A.comp;
250       ins->src_types[1] = nir_type_uint | bitsize;
251       ins->load_store.bitsize_toggle = (bitsize == 64);
252    } else {
253       ins->load_store.bitsize_toggle = true;
254       ins->load_store.arg_comp = seg & 0x3;
255       ins->load_store.arg_reg = (seg >> 2) & 0x7;
256    }
257 
258    if (match.B.def) {
259       ins->src[2] = nir_ssa_index(match.B.def);
260       ins->swizzle[2][0] = match.B.comp;
261       ins->src_types[2] = nir_type_uint | match.B.def->bit_size;
262    } else
263       ins->load_store.index_reg = REGISTER_LDST_ZERO;
264 
265    if (force_sext)
266       match.type = midgard_index_address_s32;
267 
268    ins->load_store.index_format = match.type;
269 
270    assert(match.shift <= 7);
271    ins->load_store.index_shift = match.shift;
272 
273    ins->constants.u32[0] = match.bias;
274 }
275 
276 void
mir_set_ubo_offset(midgard_instruction * ins,nir_src * src,unsigned bias)277 mir_set_ubo_offset(midgard_instruction *ins, nir_src *src, unsigned bias)
278 {
279    struct mir_address match = mir_match_offset(src->ssa, false, false);
280 
281    if (match.B.def) {
282       ins->src[2] = nir_ssa_index(match.B.def);
283 
284       for (unsigned i = 0; i < ARRAY_SIZE(ins->swizzle[2]); ++i)
285          ins->swizzle[2][i] = match.B.comp;
286    }
287 
288    ins->load_store.index_shift = match.shift;
289    ins->constants.u32[0] = match.bias + bias;
290 }
291