Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | serge | 1 | /* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ |
2 | |||
3 | /* |
||
4 | * Copyright (C) 2014 Rob Clark |
||
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 |
||
27 | */ |
||
28 | |||
29 | #include |
||
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, "Φ"); |
||
60 | break; |
||
61 | default: |
||
62 | /* shouldn't hit here.. just for debugging: */ |
||
63 | switch (instr->opc) { |
||
64 | case OPC_META_INPUT: fprintf(ctx->f, "_meta:in"); break; |
||
65 | case OPC_META_OUTPUT: fprintf(ctx->f, "_meta:out"); break; |
||
66 | case OPC_META_FO: fprintf(ctx->f, "_meta:fo"); break; |
||
67 | case OPC_META_FI: fprintf(ctx->f, "_meta:fi"); break; |
||
68 | case OPC_META_FLOW: fprintf(ctx->f, "_meta:flow"); break; |
||
69 | |||
70 | default: fprintf(ctx->f, "_meta:%d", instr->opc); break; |
||
71 | } |
||
72 | break; |
||
73 | } |
||
74 | } else if (instr->category == 1) { |
||
75 | static const char *type[] = { |
||
76 | [TYPE_F16] = "f16", |
||
77 | [TYPE_F32] = "f32", |
||
78 | [TYPE_U16] = "u16", |
||
79 | [TYPE_U32] = "u32", |
||
80 | [TYPE_S16] = "s16", |
||
81 | [TYPE_S32] = "s32", |
||
82 | [TYPE_U8] = "u8", |
||
83 | [TYPE_S8] = "s8", |
||
84 | }; |
||
85 | if (instr->cat1.src_type == instr->cat1.dst_type) |
||
86 | fprintf(ctx->f, "mov"); |
||
87 | else |
||
88 | fprintf(ctx->f, "cov"); |
||
89 | fprintf(ctx->f, ".%s%s", type[instr->cat1.src_type], type[instr->cat1.dst_type]); |
||
90 | } else { |
||
91 | fprintf(ctx->f, "%s", ir3_instr_name(instr)); |
||
92 | if (instr->flags & IR3_INSTR_3D) |
||
93 | fprintf(ctx->f, ".3d"); |
||
94 | if (instr->flags & IR3_INSTR_A) |
||
95 | fprintf(ctx->f, ".a"); |
||
96 | if (instr->flags & IR3_INSTR_O) |
||
97 | fprintf(ctx->f, ".o"); |
||
98 | if (instr->flags & IR3_INSTR_P) |
||
99 | fprintf(ctx->f, ".p"); |
||
100 | if (instr->flags & IR3_INSTR_S) |
||
101 | fprintf(ctx->f, ".s"); |
||
102 | if (instr->flags & IR3_INSTR_S2EN) |
||
103 | fprintf(ctx->f, ".s2en"); |
||
104 | } |
||
105 | } |
||
106 | |||
107 | static void dump_reg_name(struct ir3_dump_ctx *ctx, |
||
108 | struct ir3_register *reg, bool followssa) |
||
109 | { |
||
110 | if ((reg->flags & (IR3_REG_FABS | IR3_REG_SABS)) && |
||
111 | (reg->flags & (IR3_REG_FNEG | IR3_REG_SNEG | IR3_REG_BNOT))) |
||
112 | fprintf(ctx->f, "(absneg)"); |
||
113 | else if (reg->flags & (IR3_REG_FNEG | IR3_REG_SNEG | IR3_REG_BNOT)) |
||
114 | fprintf(ctx->f, "(neg)"); |
||
115 | else if (reg->flags & (IR3_REG_FABS | IR3_REG_SABS)) |
||
116 | fprintf(ctx->f, "(abs)"); |
||
117 | |||
118 | if (reg->flags & IR3_REG_IMMED) { |
||
119 | fprintf(ctx->f, "imm[%f,%d,0x%x]", reg->fim_val, reg->iim_val, reg->iim_val); |
||
120 | } else if (reg->flags & IR3_REG_SSA) { |
||
121 | if (ctx->verbose) { |
||
122 | fprintf(ctx->f, "_"); |
||
123 | if (followssa) { |
||
124 | fprintf(ctx->f, "["); |
||
125 | dump_instr_name(ctx, reg->instr); |
||
126 | fprintf(ctx->f, "]"); |
||
127 | } |
||
128 | } |
||
129 | } else if (reg->flags & IR3_REG_RELATIV) { |
||
130 | if (reg->flags & IR3_REG_HALF) |
||
131 | fprintf(ctx->f, "h"); |
||
132 | if (reg->flags & IR3_REG_CONST) |
||
133 | fprintf(ctx->f, "c |
||
134 | else |
||
135 | fprintf(ctx->f, "\x1b[0;31mr |
||
136 | } else { |
||
137 | if (reg->flags & IR3_REG_HALF) |
||
138 | fprintf(ctx->f, "h"); |
||
139 | if (reg->flags & IR3_REG_CONST) |
||
140 | fprintf(ctx->f, "c%u.%c", reg_num(reg), "xyzw"[reg_comp(reg)]); |
||
141 | else |
||
142 | fprintf(ctx->f, "\x1b[0;31mr%u.%c\x1b[0m", reg_num(reg), "xyzw"[reg_comp(reg)]); |
||
143 | } |
||
144 | } |
||
145 | |||
146 | static void ir3_instr_dump(struct ir3_dump_ctx *ctx, |
||
147 | struct ir3_instruction *instr); |
||
148 | static void ir3_block_dump(struct ir3_dump_ctx *ctx, |
||
149 | struct ir3_block *block, const char *name); |
||
150 | |||
151 | static void dump_instr(struct ir3_dump_ctx *ctx, |
||
152 | struct ir3_instruction *instr) |
||
153 | { |
||
154 | /* if we've already visited this instruction, bail now: */ |
||
155 | if (ir3_instr_check_mark(instr)) |
||
156 | return; |
||
157 | |||
158 | /* some meta-instructions need to be handled specially: */ |
||
159 | if (is_meta(instr)) { |
||
160 | if ((instr->opc == OPC_META_FO) || |
||
161 | (instr->opc == OPC_META_FI)) { |
||
162 | struct ir3_instruction *src; |
||
163 | foreach_ssa_src(src, instr) |
||
164 | dump_instr(ctx, src); |
||
165 | } else if (instr->opc == OPC_META_FLOW) { |
||
166 | struct ir3_register *reg = instr->regs[1]; |
||
167 | ir3_block_dump(ctx, instr->flow.if_block, "if"); |
||
168 | if (instr->flow.else_block) |
||
169 | ir3_block_dump(ctx, instr->flow.else_block, "else"); |
||
170 | if (reg->flags & IR3_REG_SSA) |
||
171 | dump_instr(ctx, reg->instr); |
||
172 | } else if (instr->opc == OPC_META_PHI) { |
||
173 | /* treat like a normal instruction: */ |
||
174 | ir3_instr_dump(ctx, instr); |
||
175 | } |
||
176 | } else { |
||
177 | ir3_instr_dump(ctx, instr); |
||
178 | } |
||
179 | } |
||
180 | |||
181 | /* arrarraggh! if link is to something outside of the current block, we |
||
182 | * need to defer emitting the link until the end of the block, since the |
||
183 | * edge triggers pre-creation of the node it links to inside the cluster, |
||
184 | * even though it is meant to be outside.. |
||
185 | */ |
||
186 | static struct { |
||
187 | char buf[40960]; |
||
188 | unsigned n; |
||
189 | } edge_buf; |
||
190 | |||
191 | /* helper to print or defer: */ |
||
192 | static void printdef(struct ir3_dump_ctx *ctx, |
||
193 | bool defer, const char *fmt, ...) |
||
194 | { |
||
195 | va_list ap; |
||
196 | va_start(ap, fmt); |
||
197 | if (defer) { |
||
198 | unsigned n = edge_buf.n; |
||
199 | n += vsnprintf(&edge_buf.buf[n], sizeof(edge_buf.buf) - n, |
||
200 | fmt, ap); |
||
201 | edge_buf.n = n; |
||
202 | } else { |
||
203 | vfprintf(ctx->f, fmt, ap); |
||
204 | } |
||
205 | va_end(ap); |
||
206 | } |
||
207 | |||
208 | static void dump_link2(struct ir3_dump_ctx *ctx, |
||
209 | struct ir3_instruction *instr, const char *target, bool defer) |
||
210 | { |
||
211 | /* some meta-instructions need to be handled specially: */ |
||
212 | if (is_meta(instr)) { |
||
213 | if (instr->opc == OPC_META_INPUT) { |
||
214 | printdef(ctx, defer, "input%lx: |
||
215 | PTRID(instr->inout.block), |
||
216 | instr->regs[0]->num, target); |
||
217 | } else if (instr->opc == OPC_META_FO) { |
||
218 | struct ir3_register *reg = instr->regs[1]; |
||
219 | dump_link2(ctx, reg->instr, target, defer); |
||
220 | printdef(ctx, defer, "[label=\".%c\"]", |
||
221 | "xyzw"[instr->fo.off & 0x3]); |
||
222 | } else if (instr->opc == OPC_META_FI) { |
||
223 | struct ir3_instruction *src; |
||
224 | |||
225 | foreach_ssa_src_n(src, i, instr) { |
||
226 | dump_link2(ctx, src, target, defer); |
||
227 | printdef(ctx, defer, "[label=\".%c\"]", |
||
228 | "xyzw"[i & 0x3]); |
||
229 | } |
||
230 | } else if (instr->opc == OPC_META_OUTPUT) { |
||
231 | printdef(ctx, defer, "output%lx: |
||
232 | PTRID(instr->inout.block), |
||
233 | instr->regs[0]->num, target); |
||
234 | } else if (instr->opc == OPC_META_PHI) { |
||
235 | /* treat like a normal instruction: */ |
||
236 | printdef(ctx, defer, "instr%lx: |
||
237 | } |
||
238 | } else { |
||
239 | printdef(ctx, defer, "instr%lx: |
||
240 | } |
||
241 | } |
||
242 | |||
243 | static void dump_link(struct ir3_dump_ctx *ctx, |
||
244 | struct ir3_instruction *instr, |
||
245 | struct ir3_block *block, const char *target) |
||
246 | { |
||
247 | bool defer = instr->block != block; |
||
248 | dump_link2(ctx, instr, target, defer); |
||
249 | printdef(ctx, defer, "\n"); |
||
250 | } |
||
251 | |||
252 | static struct ir3_register *follow_flow(struct ir3_register *reg) |
||
253 | { |
||
254 | if (reg->flags & IR3_REG_SSA) { |
||
255 | struct ir3_instruction *instr = reg->instr; |
||
256 | /* go with the flow.. */ |
||
257 | if (is_meta(instr) && (instr->opc == OPC_META_FLOW)) |
||
258 | return instr->regs[1]; |
||
259 | } |
||
260 | return reg; |
||
261 | } |
||
262 | |||
263 | static void ir3_instr_dump(struct ir3_dump_ctx *ctx, |
||
264 | struct ir3_instruction *instr) |
||
265 | { |
||
266 | struct ir3_register *src; |
||
267 | |||
268 | fprintf(ctx->f, "instr%lx [shape=record,style=filled,fillcolor=lightgrey,label=\"{", |
||
269 | PTRID(instr)); |
||
270 | dump_instr_name(ctx, instr); |
||
271 | |||
272 | /* destination register: */ |
||
273 | fprintf(ctx->f, "| |
||
274 | |||
275 | /* source register(s): */ |
||
276 | foreach_src_n(src, i, instr) { |
||
277 | struct ir3_register *reg = follow_flow(src); |
||
278 | |||
279 | fprintf(ctx->f, "|"); |
||
280 | |||
281 | if (reg->flags & IR3_REG_SSA) |
||
282 | fprintf(ctx->f, " |
||
283 | |||
284 | dump_reg_name(ctx, reg, true); |
||
285 | } |
||
286 | |||
287 | fprintf(ctx->f, "}\"];\n"); |
||
288 | |||
289 | /* and recursively dump dependent instructions: */ |
||
290 | foreach_src_n(src, i, instr) { |
||
291 | struct ir3_register *reg = follow_flow(src); |
||
292 | char target[32]; /* link target */ |
||
293 | |||
294 | if (!(reg->flags & IR3_REG_SSA)) |
||
295 | continue; |
||
296 | |||
297 | snprintf(target, sizeof(target), "instr%lx: |
||
298 | PTRID(instr), i); |
||
299 | |||
300 | dump_instr(ctx, reg->instr); |
||
301 | dump_link(ctx, reg->instr, instr->block, target); |
||
302 | } |
||
303 | } |
||
304 | |||
305 | static void ir3_block_dump(struct ir3_dump_ctx *ctx, |
||
306 | struct ir3_block *block, const char *name) |
||
307 | { |
||
308 | unsigned i, n; |
||
309 | |||
310 | n = edge_buf.n; |
||
311 | |||
312 | fprintf(ctx->f, "subgraph cluster%lx {\n", PTRID(block)); |
||
313 | fprintf(ctx->f, "label=\"%s\";\n", name); |
||
314 | |||
315 | /* draw inputs: */ |
||
316 | fprintf(ctx->f, "input%lx [shape=record,label=\"inputs", PTRID(block)); |
||
317 | for (i = 0; i < block->ninputs; i++) |
||
318 | if (block->inputs[i]) |
||
319 | fprintf(ctx->f, "| |
||
320 | fprintf(ctx->f, "\"];\n"); |
||
321 | |||
322 | /* draw instruction graph: */ |
||
323 | for (i = 0; i < block->noutputs; i++) |
||
324 | if (block->outputs[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, "| |
||
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: |
||
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: |
||
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, !!i); |
||
409 | } |
||
410 | |||
411 | if (instr->address) { |
||
412 | fprintf(ctx.f, ", address=_"); |
||
413 | fprintf(ctx.f, "["); |
||
414 | dump_instr_name(&ctx, instr->address); |
||
415 | fprintf(ctx.f, "]"); |
||
416 | } |
||
417 | |||
418 | if (instr->fanin) { |
||
419 | fprintf(ctx.f, ", fanin=_"); |
||
420 | fprintf(ctx.f, "["); |
||
421 | dump_instr_name(&ctx, instr->fanin); |
||
422 | fprintf(ctx.f, "]"); |
||
423 | } |
||
424 | |||
425 | if (is_meta(instr)) { |
||
426 | if (instr->opc == OPC_META_FO) { |
||
427 | printf(", off=%d", instr->fo.off); |
||
428 | } else if ((instr->opc == OPC_META_FI) && instr->fi.aid) { |
||
429 | printf(", aid=%d", instr->fi.aid); |
||
430 | } |
||
431 | } |
||
432 | |||
433 | printf("\n"); |
||
434 | } |
||
435 | |||
436 | void |
||
437 | ir3_dump_instr_list(struct ir3_instruction *instr) |
||
438 | { |
||
439 | struct ir3_block *block = instr->block; |
||
440 | unsigned n = 0; |
||
441 | |||
442 | while (instr) { |
||
443 | ir3_dump_instr_single(instr); |
||
444 | if (!is_meta(instr)) |
||
445 | n++; |
||
446 | instr = instr->next; |
||
447 | } |
||
448 | printf("%u instructions\n", n); |
||
449 | |||
450 | for (n = 0; n < block->noutputs; n++) { |
||
451 | if (!block->outputs[n]) |
||
452 | continue; |
||
453 | printf("out%d: ", n); |
||
454 | ir3_dump_instr_single(block->outputs[n]); |
||
455 | } |
||
456 | }>>>>>>> |