1a6f49d51cd7f2c315277e82986c9d55c4227b20
[mesa.git] / src / gallium / drivers / freedreno / a3xx / ir3_dump.c
1 /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
2
3 /*
4 * Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 *
25 * Authors:
26 * Rob Clark <robclark@freedesktop.org>
27 */
28
29 #include <stdarg.h>
30
31 #include "ir3.h"
32
33 #define PTRID(x) ((unsigned long)(x))
34
35 struct ir3_dump_ctx {
36 FILE *f;
37 bool verbose;
38 };
39
40 static void dump_instr_name(struct ir3_dump_ctx *ctx,
41 struct ir3_instruction *instr)
42 {
43 /* for debugging: */
44 if (ctx->verbose) {
45 #ifdef DEBUG
46 fprintf(ctx->f, "%04u:", instr->serialno);
47 #endif
48 fprintf(ctx->f, "%03u: ", instr->depth);
49 }
50
51 if (instr->flags & IR3_INSTR_SY)
52 fprintf(ctx->f, "(sy)");
53 if (instr->flags & IR3_INSTR_SS)
54 fprintf(ctx->f, "(ss)");
55
56 if (is_meta(instr)) {
57 switch(instr->opc) {
58 case OPC_META_PHI:
59 fprintf(ctx->f, "&#934;");
60 break;
61 case OPC_META_DEREF:
62 fprintf(ctx->f, "(*)");
63 break;
64 default:
65 /* shouldn't hit here.. just for debugging: */
66 switch (instr->opc) {
67 case OPC_META_INPUT: fprintf(ctx->f, "_meta:in"); break;
68 case OPC_META_OUTPUT: fprintf(ctx->f, "_meta:out"); break;
69 case OPC_META_FO: fprintf(ctx->f, "_meta:fo"); break;
70 case OPC_META_FI: fprintf(ctx->f, "_meta:fi"); break;
71 case OPC_META_FLOW: fprintf(ctx->f, "_meta:flow"); break;
72
73 default: fprintf(ctx->f, "_meta:%d", instr->opc); break;
74 }
75 break;
76 }
77 } else if (instr->category == 1) {
78 static const char *type[] = {
79 [TYPE_F16] = "f16",
80 [TYPE_F32] = "f32",
81 [TYPE_U16] = "u16",
82 [TYPE_U32] = "u32",
83 [TYPE_S16] = "s16",
84 [TYPE_S32] = "s32",
85 [TYPE_U8] = "u8",
86 [TYPE_S8] = "s8",
87 };
88 if (instr->cat1.src_type == instr->cat1.dst_type)
89 fprintf(ctx->f, "mov");
90 else
91 fprintf(ctx->f, "cov");
92 fprintf(ctx->f, ".%s%s", type[instr->cat1.src_type], type[instr->cat1.dst_type]);
93 } else {
94 fprintf(ctx->f, "%s", ir3_instr_name(instr));
95 if (instr->flags & IR3_INSTR_3D)
96 fprintf(ctx->f, ".3d");
97 if (instr->flags & IR3_INSTR_A)
98 fprintf(ctx->f, ".a");
99 if (instr->flags & IR3_INSTR_O)
100 fprintf(ctx->f, ".o");
101 if (instr->flags & IR3_INSTR_P)
102 fprintf(ctx->f, ".p");
103 if (instr->flags & IR3_INSTR_S)
104 fprintf(ctx->f, ".s");
105 if (instr->flags & IR3_INSTR_S2EN)
106 fprintf(ctx->f, ".s2en");
107 }
108 }
109
110 static void dump_reg_name(struct ir3_dump_ctx *ctx,
111 struct ir3_register *reg)
112 {
113 if ((reg->flags & IR3_REG_ABS) && (reg->flags & IR3_REG_NEGATE))
114 fprintf(ctx->f, "(absneg)");
115 else if (reg->flags & IR3_REG_NEGATE)
116 fprintf(ctx->f, "(neg)");
117 else if (reg->flags & IR3_REG_ABS)
118 fprintf(ctx->f, "(abs)");
119
120 if (reg->flags & IR3_REG_IMMED) {
121 fprintf(ctx->f, "imm[%f,%d,0x%x]", reg->fim_val, reg->iim_val, reg->iim_val);
122 } else if (reg->flags & IR3_REG_SSA) {
123 if (ctx->verbose) {
124 fprintf(ctx->f, "_[");
125 dump_instr_name(ctx, reg->instr);
126 fprintf(ctx->f, "]");
127 }
128 } else {
129 if (reg->flags & IR3_REG_HALF)
130 fprintf(ctx->f, "h");
131 if (reg->flags & IR3_REG_CONST)
132 fprintf(ctx->f, "c%u.%c", reg_num(reg), "xyzw"[reg_comp(reg)]);
133 else
134 fprintf(ctx->f, "r%u.%c", reg_num(reg), "xyzw"[reg_comp(reg)]);
135 }
136 }
137
138 static void ir3_instr_dump(struct ir3_dump_ctx *ctx,
139 struct ir3_instruction *instr);
140 static void ir3_block_dump(struct ir3_dump_ctx *ctx,
141 struct ir3_block *block, const char *name);
142
143 static void dump_instr(struct ir3_dump_ctx *ctx,
144 struct ir3_instruction *instr)
145 {
146 /* if we've already visited this instruction, bail now: */
147 if (ir3_instr_check_mark(instr))
148 return;
149
150 /* some meta-instructions need to be handled specially: */
151 if (is_meta(instr)) {
152 if ((instr->opc == OPC_META_FO) ||
153 (instr->opc == OPC_META_FI)) {
154 unsigned i;
155 for (i = 1; i < instr->regs_count; i++) {
156 struct ir3_register *reg = instr->regs[i];
157 if (reg->flags & IR3_REG_SSA)
158 dump_instr(ctx, reg->instr);
159 }
160 } else if (instr->opc == OPC_META_FLOW) {
161 struct ir3_register *reg = instr->regs[1];
162 ir3_block_dump(ctx, instr->flow.if_block, "if");
163 if (instr->flow.else_block)
164 ir3_block_dump(ctx, instr->flow.else_block, "else");
165 if (reg->flags & IR3_REG_SSA)
166 dump_instr(ctx, reg->instr);
167 } else if ((instr->opc == OPC_META_PHI) ||
168 (instr->opc == OPC_META_DEREF)) {
169 /* treat like a normal instruction: */
170 ir3_instr_dump(ctx, instr);
171 }
172 } else {
173 ir3_instr_dump(ctx, instr);
174 }
175 }
176
177 /* arrarraggh! if link is to something outside of the current block, we
178 * need to defer emitting the link until the end of the block, since the
179 * edge triggers pre-creation of the node it links to inside the cluster,
180 * even though it is meant to be outside..
181 */
182 static struct {
183 char buf[40960];
184 unsigned n;
185 } edge_buf;
186
187 /* helper to print or defer: */
188 static void printdef(struct ir3_dump_ctx *ctx,
189 bool defer, const char *fmt, ...)
190 {
191 va_list ap;
192 va_start(ap, fmt);
193 if (defer) {
194 unsigned n = edge_buf.n;
195 n += vsnprintf(&edge_buf.buf[n], sizeof(edge_buf.buf) - n,
196 fmt, ap);
197 edge_buf.n = n;
198 } else {
199 vfprintf(ctx->f, fmt, ap);
200 }
201 va_end(ap);
202 }
203
204 static void dump_link2(struct ir3_dump_ctx *ctx,
205 struct ir3_instruction *instr, const char *target, bool defer)
206 {
207 /* some meta-instructions need to be handled specially: */
208 if (is_meta(instr)) {
209 if (instr->opc == OPC_META_INPUT) {
210 printdef(ctx, defer, "input%lx:<in%u>:w -> %s",
211 PTRID(instr->inout.block),
212 instr->regs[0]->num, target);
213 } else if (instr->opc == OPC_META_FO) {
214 struct ir3_register *reg = instr->regs[1];
215 dump_link2(ctx, reg->instr, target, defer);
216 printdef(ctx, defer, "[label=\".%c\"]",
217 "xyzw"[instr->fo.off & 0x3]);
218 } else if (instr->opc == OPC_META_FI) {
219 unsigned i;
220
221 /* recursively dump all parents and links */
222 for (i = 1; i < instr->regs_count; i++) {
223 struct ir3_register *reg = instr->regs[i];
224 if (reg->flags & IR3_REG_SSA) {
225 dump_link2(ctx, reg->instr, target, defer);
226 printdef(ctx, defer, "[label=\".%c\"]",
227 "xyzw"[(i - 1) & 0x3]);
228 }
229 }
230 } else if (instr->opc == OPC_META_OUTPUT) {
231 printdef(ctx, defer, "output%lx:<out%u>:w -> %s",
232 PTRID(instr->inout.block),
233 instr->regs[0]->num, target);
234 } else if ((instr->opc == OPC_META_PHI) ||
235 (instr->opc == OPC_META_DEREF)) {
236 /* treat like a normal instruction: */
237 printdef(ctx, defer, "instr%lx:<dst0> -> %s", PTRID(instr), target);
238 }
239 } else {
240 printdef(ctx, defer, "instr%lx:<dst0> -> %s", PTRID(instr), target);
241 }
242 }
243
244 static void dump_link(struct ir3_dump_ctx *ctx,
245 struct ir3_instruction *instr,
246 struct ir3_block *block, const char *target)
247 {
248 bool defer = instr->block != block;
249 dump_link2(ctx, instr, target, defer);
250 printdef(ctx, defer, "\n");
251 }
252
253 static struct ir3_register *follow_flow(struct ir3_register *reg)
254 {
255 if (reg->flags & IR3_REG_SSA) {
256 struct ir3_instruction *instr = reg->instr;
257 /* go with the flow.. */
258 if (is_meta(instr) && (instr->opc == OPC_META_FLOW))
259 return instr->regs[1];
260 }
261 return reg;
262 }
263
264 static void ir3_instr_dump(struct ir3_dump_ctx *ctx,
265 struct ir3_instruction *instr)
266 {
267 unsigned i;
268
269 fprintf(ctx->f, "instr%lx [shape=record,style=filled,fillcolor=lightgrey,label=\"{",
270 PTRID(instr));
271 dump_instr_name(ctx, instr);
272
273 /* destination register: */
274 fprintf(ctx->f, "|<dst0>");
275
276 /* source register(s): */
277 for (i = 1; i < instr->regs_count; i++) {
278 struct ir3_register *reg = follow_flow(instr->regs[i]);
279
280 fprintf(ctx->f, "|");
281
282 if (reg->flags & IR3_REG_SSA)
283 fprintf(ctx->f, "<src%u> ", (i - 1));
284
285 dump_reg_name(ctx, reg);
286 }
287
288 fprintf(ctx->f, "}\"];\n");
289
290 /* and recursively dump dependent instructions: */
291 for (i = 1; i < instr->regs_count; i++) {
292 struct ir3_register *reg = instr->regs[i];
293 char target[32]; /* link target */
294
295 if (!(reg->flags & IR3_REG_SSA))
296 continue;
297
298 snprintf(target, sizeof(target), "instr%lx:<src%u>",
299 PTRID(instr), (i - 1));
300
301 dump_instr(ctx, reg->instr);
302 dump_link(ctx, follow_flow(reg)->instr, instr->block, target);
303 }
304 }
305
306 static void ir3_block_dump(struct ir3_dump_ctx *ctx,
307 struct ir3_block *block, const char *name)
308 {
309 unsigned i, n;
310
311 n = edge_buf.n;
312
313 fprintf(ctx->f, "subgraph cluster%lx {\n", PTRID(block));
314 fprintf(ctx->f, "label=\"%s\";\n", name);
315
316 /* draw inputs: */
317 fprintf(ctx->f, "input%lx [shape=record,label=\"inputs", PTRID(block));
318 for (i = 0; i < block->ninputs; i++)
319 if (block->inputs[i])
320 fprintf(ctx->f, "|<in%u> i%u.%c", i, (i >> 2), "xyzw"[i & 0x3]);
321 fprintf(ctx->f, "\"];\n");
322
323 /* draw instruction graph: */
324 for (i = 0; i < block->noutputs; i++)
325 dump_instr(ctx, block->outputs[i]);
326
327 /* draw outputs: */
328 fprintf(ctx->f, "output%lx [shape=record,label=\"outputs", PTRID(block));
329 for (i = 0; i < block->noutputs; i++)
330 fprintf(ctx->f, "|<out%u> o%u.%c", i, (i >> 2), "xyzw"[i & 0x3]);
331 fprintf(ctx->f, "\"];\n");
332
333 /* and links to outputs: */
334 for (i = 0; i < block->noutputs; i++) {
335 char target[32]; /* link target */
336
337 /* NOTE: there could be outputs that are never assigned,
338 * so skip them
339 */
340 if (!block->outputs[i])
341 continue;
342
343 snprintf(target, sizeof(target), "output%lx:<out%u>:e",
344 PTRID(block), i);
345
346 dump_link(ctx, block->outputs[i], block, target);
347 }
348
349 fprintf(ctx->f, "}\n");
350
351 /* and links to inputs: */
352 if (block->parent) {
353 for (i = 0; i < block->ninputs; i++) {
354 char target[32]; /* link target */
355
356 if (!block->inputs[i])
357 continue;
358
359 dump_instr(ctx, block->inputs[i]);
360
361 snprintf(target, sizeof(target), "input%lx:<in%u>:e",
362 PTRID(block), i);
363
364 dump_link(ctx, block->inputs[i], block, target);
365 }
366 }
367
368 /* dump deferred edges: */
369 if (edge_buf.n > n) {
370 fprintf(ctx->f, "%*s", edge_buf.n - n, &edge_buf.buf[n]);
371 edge_buf.n = n;
372 }
373 }
374
375 void ir3_dump(struct ir3 *shader, const char *name,
376 struct ir3_block *block /* XXX maybe 'block' ptr should move to ir3? */,
377 FILE *f)
378 {
379 struct ir3_dump_ctx ctx = {
380 .f = f,
381 };
382 ir3_clear_mark(shader);
383 fprintf(ctx.f, "digraph G {\n");
384 fprintf(ctx.f, "rankdir=RL;\n");
385 fprintf(ctx.f, "nodesep=0.25;\n");
386 fprintf(ctx.f, "ranksep=1.5;\n");
387 ir3_block_dump(&ctx, block, name);
388 fprintf(ctx.f, "}\n");
389 }
390
391 /*
392 * For Debugging:
393 */
394
395 void
396 ir3_dump_instr_single(struct ir3_instruction *instr)
397 {
398 struct ir3_dump_ctx ctx = {
399 .f = stdout,
400 .verbose = true,
401 };
402 unsigned i;
403
404 dump_instr_name(&ctx, instr);
405 for (i = 0; i < instr->regs_count; i++) {
406 struct ir3_register *reg = instr->regs[i];
407 printf(i ? ", " : " ");
408 dump_reg_name(&ctx, reg);
409 }
410 printf("\n");
411 }
412
413 void
414 ir3_dump_instr_list(struct ir3_instruction *instr)
415 {
416 unsigned n = 0;
417
418 while (instr) {
419 ir3_dump_instr_single(instr);
420 if (!is_meta(instr))
421 n++;
422 instr = instr->next;
423 }
424 printf("%u instructions\n", n);
425 }