1 /*
2 * Copyright © 2016 Broadcom
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
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include "compiler/v3d_compiler.h"
25 #include "qpu/qpu_instr.h"
26 #include "qpu/qpu_disasm.h"
27
28 static inline struct qpu_reg
qpu_reg(int index)29 qpu_reg(int index)
30 {
31 struct qpu_reg reg = {
32 .magic = false,
33 .index = index,
34 };
35 return reg;
36 }
37
38 static inline struct qpu_reg
qpu_magic(enum v3d_qpu_waddr waddr)39 qpu_magic(enum v3d_qpu_waddr waddr)
40 {
41 struct qpu_reg reg = {
42 .magic = true,
43 .index = waddr,
44 };
45 return reg;
46 }
47
48 static inline struct qpu_reg
qpu_acc(int acc)49 qpu_acc(int acc)
50 {
51 return qpu_magic(V3D_QPU_WADDR_R0 + acc);
52 }
53
54 struct v3d_qpu_instr
v3d_qpu_nop(void)55 v3d_qpu_nop(void)
56 {
57 struct v3d_qpu_instr instr = {
58 .type = V3D_QPU_INSTR_TYPE_ALU,
59 .alu = {
60 .add = {
61 .op = V3D_QPU_A_NOP,
62 .waddr = V3D_QPU_WADDR_NOP,
63 .magic_write = true,
64 },
65 .mul = {
66 .op = V3D_QPU_M_NOP,
67 .waddr = V3D_QPU_WADDR_NOP,
68 .magic_write = true,
69 },
70 }
71 };
72
73 return instr;
74 }
75
76 static struct qinst *
vir_nop(void)77 vir_nop(void)
78 {
79 struct qreg undef = { QFILE_NULL, 0 };
80 struct qinst *qinst = vir_add_inst(V3D_QPU_A_NOP, undef, undef, undef);
81
82 return qinst;
83 }
84
85 static struct qinst *
new_qpu_nop_before(struct qinst * inst)86 new_qpu_nop_before(struct qinst *inst)
87 {
88 struct qinst *q = vir_nop();
89
90 list_addtail(&q->link, &inst->link);
91
92 return q;
93 }
94
95 static void
new_ldunif_instr(struct qinst * inst,int i)96 new_ldunif_instr(struct qinst *inst, int i)
97 {
98 struct qinst *ldunif = new_qpu_nop_before(inst);
99
100 ldunif->qpu.sig.ldunif = true;
101 assert(inst->src[i].file == QFILE_UNIF);
102 ldunif->uniform = inst->src[i].index;
103 }
104
105 /**
106 * Allocates the src register (accumulator or register file) into the RADDR
107 * fields of the instruction.
108 */
109 static void
set_src(struct v3d_qpu_instr * instr,enum v3d_qpu_mux * mux,struct qpu_reg src)110 set_src(struct v3d_qpu_instr *instr, enum v3d_qpu_mux *mux, struct qpu_reg src)
111 {
112 if (src.magic) {
113 assert(src.index >= V3D_QPU_WADDR_R0 &&
114 src.index <= V3D_QPU_WADDR_R5);
115 *mux = src.index - V3D_QPU_WADDR_R0 + V3D_QPU_MUX_R0;
116 return;
117 }
118
119 if (instr->alu.add.a != V3D_QPU_MUX_A &&
120 instr->alu.add.b != V3D_QPU_MUX_A &&
121 instr->alu.mul.a != V3D_QPU_MUX_A &&
122 instr->alu.mul.b != V3D_QPU_MUX_A) {
123 instr->raddr_a = src.index;
124 *mux = V3D_QPU_MUX_A;
125 } else {
126 if (instr->raddr_a == src.index) {
127 *mux = V3D_QPU_MUX_A;
128 } else {
129 assert(!(instr->alu.add.a == V3D_QPU_MUX_B &&
130 instr->alu.add.b == V3D_QPU_MUX_B &&
131 instr->alu.mul.a == V3D_QPU_MUX_B &&
132 instr->alu.mul.b == V3D_QPU_MUX_B) ||
133 src.index == instr->raddr_b);
134
135 instr->raddr_b = src.index;
136 *mux = V3D_QPU_MUX_B;
137 }
138 }
139 }
140
141 static void
v3d_generate_code_block(struct v3d_compile * c,struct qblock * block,struct qpu_reg * temp_registers)142 v3d_generate_code_block(struct v3d_compile *c,
143 struct qblock *block,
144 struct qpu_reg *temp_registers)
145 {
146 int last_vpm_read_index = -1;
147
148 vir_for_each_inst(qinst, block) {
149 #if 0
150 fprintf(stderr, "translating qinst to qpu: ");
151 vir_dump_inst(c, qinst);
152 fprintf(stderr, "\n");
153 #endif
154
155 struct qinst *temp;
156
157 if (vir_has_implicit_uniform(qinst)) {
158 int src = vir_get_implicit_uniform_src(qinst);
159 assert(qinst->src[src].file == QFILE_UNIF);
160 qinst->uniform = qinst->src[src].index;
161 c->num_uniforms++;
162 }
163
164 int nsrc = vir_get_non_sideband_nsrc(qinst);
165 struct qpu_reg src[ARRAY_SIZE(qinst->src)];
166 bool emitted_ldunif = false;
167 for (int i = 0; i < nsrc; i++) {
168 int index = qinst->src[i].index;
169 switch (qinst->src[i].file) {
170 case QFILE_REG:
171 src[i] = qpu_reg(qinst->src[i].index);
172 break;
173 case QFILE_MAGIC:
174 src[i] = qpu_magic(qinst->src[i].index);
175 break;
176 case QFILE_NULL:
177 case QFILE_LOAD_IMM:
178 src[i] = qpu_acc(0);
179 break;
180 case QFILE_TEMP:
181 src[i] = temp_registers[index];
182 break;
183 case QFILE_UNIF:
184 if (!emitted_ldunif) {
185 new_ldunif_instr(qinst, i);
186 c->num_uniforms++;
187 emitted_ldunif = true;
188 }
189
190 src[i] = qpu_acc(5);
191 break;
192 case QFILE_SMALL_IMM:
193 abort(); /* XXX */
194 #if 0
195 src[i].mux = QPU_MUX_SMALL_IMM;
196 src[i].addr = qpu_encode_small_immediate(qinst->src[i].index);
197 /* This should only have returned a valid
198 * small immediate field, not ~0 for failure.
199 */
200 assert(src[i].addr <= 47);
201 #endif
202 break;
203
204 case QFILE_VPM:
205 assert((int)qinst->src[i].index >=
206 last_vpm_read_index);
207 (void)last_vpm_read_index;
208 last_vpm_read_index = qinst->src[i].index;
209
210 temp = new_qpu_nop_before(qinst);
211 temp->qpu.sig.ldvpm = true;
212
213 src[i] = qpu_acc(3);
214 break;
215
216 case QFILE_TLB:
217 case QFILE_TLBU:
218 unreachable("bad vir src file");
219 }
220 }
221
222 struct qpu_reg dst;
223 switch (qinst->dst.file) {
224 case QFILE_NULL:
225 dst = qpu_magic(V3D_QPU_WADDR_NOP);
226 break;
227
228 case QFILE_REG:
229 dst = qpu_reg(qinst->dst.index);
230 break;
231
232 case QFILE_MAGIC:
233 dst = qpu_magic(qinst->dst.index);
234 break;
235
236 case QFILE_TEMP:
237 dst = temp_registers[qinst->dst.index];
238 break;
239
240 case QFILE_VPM:
241 dst = qpu_magic(V3D_QPU_WADDR_VPM);
242 break;
243
244 case QFILE_TLB:
245 dst = qpu_magic(V3D_QPU_WADDR_TLB);
246 break;
247
248 case QFILE_TLBU:
249 dst = qpu_magic(V3D_QPU_WADDR_TLBU);
250 break;
251
252 case QFILE_UNIF:
253 case QFILE_SMALL_IMM:
254 case QFILE_LOAD_IMM:
255 assert(!"not reached");
256 break;
257 }
258
259 if (qinst->qpu.type == V3D_QPU_INSTR_TYPE_ALU) {
260 if (v3d_qpu_sig_writes_address(c->devinfo,
261 &qinst->qpu.sig)) {
262 assert(qinst->qpu.alu.add.op == V3D_QPU_A_NOP);
263 assert(qinst->qpu.alu.mul.op == V3D_QPU_M_NOP);
264
265 qinst->qpu.sig_addr = dst.index;
266 qinst->qpu.sig_magic = dst.magic;
267 } else if (qinst->qpu.alu.add.op != V3D_QPU_A_NOP) {
268 assert(qinst->qpu.alu.mul.op == V3D_QPU_M_NOP);
269 if (nsrc >= 1) {
270 set_src(&qinst->qpu,
271 &qinst->qpu.alu.add.a, src[0]);
272 }
273 if (nsrc >= 2) {
274 set_src(&qinst->qpu,
275 &qinst->qpu.alu.add.b, src[1]);
276 }
277
278 qinst->qpu.alu.add.waddr = dst.index;
279 qinst->qpu.alu.add.magic_write = dst.magic;
280 } else {
281 if (nsrc >= 1) {
282 set_src(&qinst->qpu,
283 &qinst->qpu.alu.mul.a, src[0]);
284 }
285 if (nsrc >= 2) {
286 set_src(&qinst->qpu,
287 &qinst->qpu.alu.mul.b, src[1]);
288 }
289
290 qinst->qpu.alu.mul.waddr = dst.index;
291 qinst->qpu.alu.mul.magic_write = dst.magic;
292 }
293 } else {
294 assert(qinst->qpu.type == V3D_QPU_INSTR_TYPE_BRANCH);
295 }
296 }
297 }
298
299
300 static void
v3d_dump_qpu(struct v3d_compile * c)301 v3d_dump_qpu(struct v3d_compile *c)
302 {
303 fprintf(stderr, "%s prog %d/%d QPU:\n",
304 vir_get_stage_name(c),
305 c->program_id, c->variant_id);
306
307 for (int i = 0; i < c->qpu_inst_count; i++) {
308 const char *str = v3d_qpu_disasm(c->devinfo, c->qpu_insts[i]);
309 fprintf(stderr, "0x%016"PRIx64" %s\n", c->qpu_insts[i], str);
310 }
311 fprintf(stderr, "\n");
312 }
313
314 void
v3d_vir_to_qpu(struct v3d_compile * c,struct qpu_reg * temp_registers)315 v3d_vir_to_qpu(struct v3d_compile *c, struct qpu_reg *temp_registers)
316 {
317 /* Reset the uniform count to how many will be actually loaded by the
318 * generated QPU code.
319 */
320 c->num_uniforms = 0;
321
322 vir_for_each_block(block, c)
323 v3d_generate_code_block(c, block, temp_registers);
324
325 uint32_t cycles = v3d_qpu_schedule_instructions(c);
326
327 c->qpu_insts = rzalloc_array(c, uint64_t, c->qpu_inst_count);
328 int i = 0;
329 vir_for_each_inst_inorder(inst, c) {
330 bool ok = v3d_qpu_instr_pack(c->devinfo, &inst->qpu,
331 &c->qpu_insts[i++]);
332 assert(ok); (void) ok;
333 }
334 assert(i == c->qpu_inst_count);
335
336 if (V3D_DEBUG & V3D_DEBUG_SHADERDB) {
337 fprintf(stderr, "SHADER-DB: %s prog %d/%d: %d estimated cycles\n",
338 vir_get_stage_name(c),
339 c->program_id, c->variant_id,
340 cycles);
341 }
342
343 if (V3D_DEBUG & (V3D_DEBUG_QPU |
344 v3d_debug_flag_for_shader_stage(c->s->info.stage))) {
345 v3d_dump_qpu(c);
346 }
347
348 qpu_validate(c);
349
350 free(temp_registers);
351 }
352