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