1a6f49d51cd7f2c315277e82986c9d55c4227b20
1 /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */
4 * Copyright (C) 2014 Rob Clark <robclark@freedesktop.org>
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:
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
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
26 * Rob Clark <robclark@freedesktop.org>
33 #define PTRID(x) ((unsigned long)(x))
40 static void dump_instr_name(struct ir3_dump_ctx
*ctx
,
41 struct ir3_instruction
*instr
)
46 fprintf(ctx
->f
, "%04u:", instr
->serialno
);
48 fprintf(ctx
->f
, "%03u: ", instr
->depth
);
51 if (instr
->flags
& IR3_INSTR_SY
)
52 fprintf(ctx
->f
, "(sy)");
53 if (instr
->flags
& IR3_INSTR_SS
)
54 fprintf(ctx
->f
, "(ss)");
59 fprintf(ctx
->f
, "Φ");
62 fprintf(ctx
->f
, "(*)");
65 /* shouldn't hit here.. just for debugging: */
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;
73 default: fprintf(ctx
->f
, "_meta:%d", instr
->opc
); break;
77 } else if (instr
->category
== 1) {
78 static const char *type
[] = {
88 if (instr
->cat1
.src_type
== instr
->cat1
.dst_type
)
89 fprintf(ctx
->f
, "mov");
91 fprintf(ctx
->f
, "cov");
92 fprintf(ctx
->f
, ".%s%s", type
[instr
->cat1
.src_type
], type
[instr
->cat1
.dst_type
]);
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");
110 static void dump_reg_name(struct ir3_dump_ctx
*ctx
,
111 struct ir3_register
*reg
)
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)");
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
) {
124 fprintf(ctx
->f
, "_[");
125 dump_instr_name(ctx
, reg
->instr
);
126 fprintf(ctx
->f
, "]");
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
)]);
134 fprintf(ctx
->f
, "r%u.%c", reg_num(reg
), "xyzw"[reg_comp(reg
)]);
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
);
143 static void dump_instr(struct ir3_dump_ctx
*ctx
,
144 struct ir3_instruction
*instr
)
146 /* if we've already visited this instruction, bail now: */
147 if (ir3_instr_check_mark(instr
))
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
)) {
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
);
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
);
173 ir3_instr_dump(ctx
, instr
);
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..
187 /* helper to print or defer: */
188 static void printdef(struct ir3_dump_ctx
*ctx
,
189 bool defer
, const char *fmt
, ...)
194 unsigned n
= edge_buf
.n
;
195 n
+= vsnprintf(&edge_buf
.buf
[n
], sizeof(edge_buf
.buf
) - n
,
199 vfprintf(ctx
->f
, fmt
, ap
);
204 static void dump_link2(struct ir3_dump_ctx
*ctx
,
205 struct ir3_instruction
*instr
, const char *target
, bool defer
)
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
) {
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]);
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
);
240 printdef(ctx
, defer
, "instr%lx:<dst0> -> %s", PTRID(instr
), target
);
244 static void dump_link(struct ir3_dump_ctx
*ctx
,
245 struct ir3_instruction
*instr
,
246 struct ir3_block
*block
, const char *target
)
248 bool defer
= instr
->block
!= block
;
249 dump_link2(ctx
, instr
, target
, defer
);
250 printdef(ctx
, defer
, "\n");
253 static struct ir3_register
*follow_flow(struct ir3_register
*reg
)
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];
264 static void ir3_instr_dump(struct ir3_dump_ctx
*ctx
,
265 struct ir3_instruction
*instr
)
269 fprintf(ctx
->f
, "instr%lx [shape=record,style=filled,fillcolor=lightgrey,label=\"{",
271 dump_instr_name(ctx
, instr
);
273 /* destination register: */
274 fprintf(ctx
->f
, "|<dst0>");
276 /* source register(s): */
277 for (i
= 1; i
< instr
->regs_count
; i
++) {
278 struct ir3_register
*reg
= follow_flow(instr
->regs
[i
]);
280 fprintf(ctx
->f
, "|");
282 if (reg
->flags
& IR3_REG_SSA
)
283 fprintf(ctx
->f
, "<src%u> ", (i
- 1));
285 dump_reg_name(ctx
, reg
);
288 fprintf(ctx
->f
, "}\"];\n");
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 */
295 if (!(reg
->flags
& IR3_REG_SSA
))
298 snprintf(target
, sizeof(target
), "instr%lx:<src%u>",
299 PTRID(instr
), (i
- 1));
301 dump_instr(ctx
, reg
->instr
);
302 dump_link(ctx
, follow_flow(reg
)->instr
, instr
->block
, target
);
306 static void ir3_block_dump(struct ir3_dump_ctx
*ctx
,
307 struct ir3_block
*block
, const char *name
)
313 fprintf(ctx
->f
, "subgraph cluster%lx {\n", PTRID(block
));
314 fprintf(ctx
->f
, "label=\"%s\";\n", name
);
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");
323 /* draw instruction graph: */
324 for (i
= 0; i
< block
->noutputs
; i
++)
325 dump_instr(ctx
, block
->outputs
[i
]);
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");
333 /* and links to outputs: */
334 for (i
= 0; i
< block
->noutputs
; i
++) {
335 char target
[32]; /* link target */
337 /* NOTE: there could be outputs that are never assigned,
340 if (!block
->outputs
[i
])
343 snprintf(target
, sizeof(target
), "output%lx:<out%u>:e",
346 dump_link(ctx
, block
->outputs
[i
], block
, target
);
349 fprintf(ctx
->f
, "}\n");
351 /* and links to inputs: */
353 for (i
= 0; i
< block
->ninputs
; i
++) {
354 char target
[32]; /* link target */
356 if (!block
->inputs
[i
])
359 dump_instr(ctx
, block
->inputs
[i
]);
361 snprintf(target
, sizeof(target
), "input%lx:<in%u>:e",
364 dump_link(ctx
, block
->inputs
[i
], block
, target
);
368 /* dump deferred edges: */
369 if (edge_buf
.n
> n
) {
370 fprintf(ctx
->f
, "%*s", edge_buf
.n
- n
, &edge_buf
.buf
[n
]);
375 void ir3_dump(struct ir3
*shader
, const char *name
,
376 struct ir3_block
*block
/* XXX maybe 'block' ptr should move to ir3? */,
379 struct ir3_dump_ctx ctx
= {
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");
396 ir3_dump_instr_single(struct ir3_instruction
*instr
)
398 struct ir3_dump_ctx ctx
= {
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
);
414 ir3_dump_instr_list(struct ir3_instruction
*instr
)
419 ir3_dump_instr_single(instr
);
424 printf("%u instructions\n", n
);