1/* 2 * Copyright (C) 2014 Rob Clark <robclark@freedesktop.org> 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: 24 * Rob Clark <robclark@freedesktop.org> 25 */ 26 27#include <stdarg.h> 28#include <stdio.h> 29 30#include "util/log.h" 31#include "ir3.h" 32 33#define PTRID(x) ((unsigned long)(x)) 34 35/* ansi escape sequences: */ 36#define RESET "\x1b[0m" 37#define RED "\x1b[0;31m" 38#define GREEN "\x1b[0;32m" 39#define BLUE "\x1b[0;34m" 40#define MAGENTA "\x1b[0;35m" 41 42/* syntax coloring, mostly to make it easier to see different sorts of 43 * srcs (immediate, constant, ssa, array, ...) 44 */ 45#define SYN_REG(x) RED x RESET 46#define SYN_IMMED(x) GREEN x RESET 47#define SYN_CONST(x) GREEN x RESET 48#define SYN_SSA(x) BLUE x RESET 49#define SYN_ARRAY(x) MAGENTA x RESET 50 51static const char * 52type_name(type_t type) 53{ 54 static const char *type_names[] = { 55 /* clang-format off */ 56 [TYPE_F16] = "f16", 57 [TYPE_F32] = "f32", 58 [TYPE_U16] = "u16", 59 [TYPE_U32] = "u32", 60 [TYPE_S16] = "s16", 61 [TYPE_S32] = "s32", 62 [TYPE_U8] = "u8", 63 [TYPE_S8] = "s8", 64 /* clang-format on */ 65 }; 66 return type_names[type]; 67} 68 69static void 70print_instr_name(struct log_stream *stream, struct ir3_instruction *instr, 71 bool flags) 72{ 73 if (!instr) 74 return; 75#ifdef DEBUG 76 mesa_log_stream_printf(stream, "%04u:", instr->serialno); 77#endif 78 mesa_log_stream_printf(stream, "%04u:", instr->ip); 79 if (instr->flags & IR3_INSTR_UNUSED) { 80 mesa_log_stream_printf(stream, "XXX: "); 81 } else { 82 mesa_log_stream_printf(stream, "%03u: ", instr->use_count); 83 } 84 85 if (flags) { 86 mesa_log_stream_printf(stream, "\t"); 87 if (instr->flags & IR3_INSTR_SY) 88 mesa_log_stream_printf(stream, "(sy)"); 89 if (instr->flags & IR3_INSTR_SS) 90 mesa_log_stream_printf(stream, "(ss)"); 91 if (instr->flags & IR3_INSTR_JP) 92 mesa_log_stream_printf(stream, "(jp)"); 93 if (instr->repeat) 94 mesa_log_stream_printf(stream, "(rpt%d)", instr->repeat); 95 if (instr->nop) 96 mesa_log_stream_printf(stream, "(nop%d)", instr->nop); 97 if (instr->flags & IR3_INSTR_UL) 98 mesa_log_stream_printf(stream, "(ul)"); 99 } else { 100 mesa_log_stream_printf(stream, " "); 101 } 102 103 if (is_meta(instr)) { 104 switch (instr->opc) { 105 case OPC_META_INPUT: 106 mesa_log_stream_printf(stream, "_meta:in"); 107 break; 108 case OPC_META_SPLIT: 109 mesa_log_stream_printf(stream, "_meta:split"); 110 break; 111 case OPC_META_COLLECT: 112 mesa_log_stream_printf(stream, "_meta:collect"); 113 break; 114 case OPC_META_TEX_PREFETCH: 115 mesa_log_stream_printf(stream, "_meta:tex_prefetch"); 116 break; 117 case OPC_META_PARALLEL_COPY: 118 mesa_log_stream_printf(stream, "_meta:parallel_copy"); 119 break; 120 case OPC_META_PHI: 121 mesa_log_stream_printf(stream, "_meta:phi"); 122 break; 123 124 /* shouldn't hit here.. just for debugging: */ 125 default: 126 mesa_log_stream_printf(stream, "_meta:%d", instr->opc); 127 break; 128 } 129 } else if (opc_cat(instr->opc) == 1) { 130 if (instr->opc == OPC_MOV) { 131 if (instr->cat1.src_type == instr->cat1.dst_type) 132 mesa_log_stream_printf(stream, "mov"); 133 else 134 mesa_log_stream_printf(stream, "cov"); 135 } else { 136 mesa_log_stream_printf(stream, "%s", 137 disasm_a3xx_instr_name(instr->opc)); 138 } 139 140 if (instr->opc != OPC_MOVMSK) { 141 mesa_log_stream_printf(stream, ".%s%s", 142 type_name(instr->cat1.src_type), 143 type_name(instr->cat1.dst_type)); 144 } 145 } else if (instr->opc == OPC_B) { 146 const char *name[8] = { 147 /* clang-format off */ 148 [BRANCH_PLAIN] = "br", 149 [BRANCH_OR] = "brao", 150 [BRANCH_AND] = "braa", 151 [BRANCH_CONST] = "brac", 152 [BRANCH_ANY] = "bany", 153 [BRANCH_ALL] = "ball", 154 [BRANCH_X] = "brax", 155 /* clang-format on */ 156 }; 157 mesa_log_stream_printf(stream, "%s", name[instr->cat0.brtype]); 158 } else { 159 mesa_log_stream_printf(stream, "%s", disasm_a3xx_instr_name(instr->opc)); 160 if (instr->flags & IR3_INSTR_3D) 161 mesa_log_stream_printf(stream, ".3d"); 162 if (instr->flags & IR3_INSTR_A) 163 mesa_log_stream_printf(stream, ".a"); 164 if (instr->flags & IR3_INSTR_O) 165 mesa_log_stream_printf(stream, ".o"); 166 if (instr->flags & IR3_INSTR_P) 167 mesa_log_stream_printf(stream, ".p"); 168 if (instr->flags & IR3_INSTR_S) 169 mesa_log_stream_printf(stream, ".s"); 170 if (instr->flags & IR3_INSTR_A1EN) 171 mesa_log_stream_printf(stream, ".a1en"); 172 if (instr->opc == OPC_LDC) 173 mesa_log_stream_printf(stream, ".offset%d", instr->cat6.d); 174 if (instr->flags & IR3_INSTR_B) { 175 mesa_log_stream_printf( 176 stream, ".base%d", 177 is_tex(instr) ? instr->cat5.tex_base : instr->cat6.base); 178 } 179 if (instr->flags & IR3_INSTR_S2EN) 180 mesa_log_stream_printf(stream, ".s2en"); 181 182 static const char *cond[0x7] = { 183 "lt", "le", "gt", "ge", "eq", "ne", 184 }; 185 186 switch (instr->opc) { 187 case OPC_CMPS_F: 188 case OPC_CMPS_U: 189 case OPC_CMPS_S: 190 case OPC_CMPV_F: 191 case OPC_CMPV_U: 192 case OPC_CMPV_S: 193 mesa_log_stream_printf(stream, ".%s", 194 cond[instr->cat2.condition & 0x7]); 195 break; 196 default: 197 break; 198 } 199 } 200} 201 202static void 203print_ssa_def_name(struct log_stream *stream, struct ir3_register *reg) 204{ 205 mesa_log_stream_printf(stream, SYN_SSA("ssa_%u"), reg->instr->serialno); 206 if (reg->name != 0) 207 mesa_log_stream_printf(stream, ":%u", reg->name); 208} 209 210static void 211print_ssa_name(struct log_stream *stream, struct ir3_register *reg, bool dst) 212{ 213 if (!dst) { 214 if (!reg->def) 215 mesa_log_stream_printf(stream, SYN_SSA("undef")); 216 else 217 print_ssa_def_name(stream, reg->def); 218 } else { 219 print_ssa_def_name(stream, reg); 220 } 221 222 if (reg->num != INVALID_REG && !(reg->flags & IR3_REG_ARRAY)) 223 mesa_log_stream_printf(stream, "(" SYN_REG("r%u.%c") ")", reg_num(reg), 224 "xyzw"[reg_comp(reg)]); 225} 226 227static void 228print_reg_name(struct log_stream *stream, struct ir3_instruction *instr, 229 struct ir3_register *reg, bool dest) 230{ 231 if ((reg->flags & (IR3_REG_FABS | IR3_REG_SABS)) && 232 (reg->flags & (IR3_REG_FNEG | IR3_REG_SNEG | IR3_REG_BNOT))) 233 mesa_log_stream_printf(stream, "(absneg)"); 234 else if (reg->flags & (IR3_REG_FNEG | IR3_REG_SNEG | IR3_REG_BNOT)) 235 mesa_log_stream_printf(stream, "(neg)"); 236 else if (reg->flags & (IR3_REG_FABS | IR3_REG_SABS)) 237 mesa_log_stream_printf(stream, "(abs)"); 238 239 if (reg->flags & IR3_REG_FIRST_KILL) 240 mesa_log_stream_printf(stream, "(kill)"); 241 if (reg->flags & IR3_REG_UNUSED) 242 mesa_log_stream_printf(stream, "(unused)"); 243 244 if (reg->flags & IR3_REG_R) 245 mesa_log_stream_printf(stream, "(r)"); 246 247 /* Right now all instructions that use tied registers only have one 248 * destination register, so we can just print (tied) as if it's a flag, 249 * although it's more convenient for RA if it's a pointer. 250 */ 251 if (reg->tied) 252 mesa_log_stream_printf(stream, "(tied)"); 253 254 if (reg->flags & IR3_REG_SHARED) 255 mesa_log_stream_printf(stream, "s"); 256 if (reg->flags & IR3_REG_HALF) 257 mesa_log_stream_printf(stream, "h"); 258 259 if (reg->flags & IR3_REG_IMMED) { 260 mesa_log_stream_printf(stream, SYN_IMMED("imm[%f,%d,0x%x]"), reg->fim_val, 261 reg->iim_val, reg->iim_val); 262 } else if (reg->flags & IR3_REG_ARRAY) { 263 if (reg->flags & IR3_REG_SSA) { 264 print_ssa_name(stream, reg, dest); 265 mesa_log_stream_printf(stream, ":"); 266 } 267 mesa_log_stream_printf(stream, 268 SYN_ARRAY("arr[id=%u, offset=%d, size=%u]"), 269 reg->array.id, reg->array.offset, reg->size); 270 if (reg->array.base != INVALID_REG) 271 mesa_log_stream_printf(stream, "(" SYN_REG("r%u.%c") ")", 272 reg->array.base >> 2, 273 "xyzw"[reg->array.base & 0x3]); 274 } else if (reg->flags & IR3_REG_SSA) { 275 print_ssa_name(stream, reg, dest); 276 } else if (reg->flags & IR3_REG_RELATIV) { 277 if (reg->flags & IR3_REG_CONST) 278 mesa_log_stream_printf(stream, SYN_CONST("c<a0.x + %d>"), 279 reg->array.offset); 280 else 281 mesa_log_stream_printf(stream, SYN_REG("r<a0.x + %d>") " (%u)", 282 reg->array.offset, reg->size); 283 } else { 284 if (reg->flags & IR3_REG_CONST) 285 mesa_log_stream_printf(stream, SYN_CONST("c%u.%c"), reg_num(reg), 286 "xyzw"[reg_comp(reg)]); 287 else 288 mesa_log_stream_printf(stream, SYN_REG("r%u.%c"), reg_num(reg), 289 "xyzw"[reg_comp(reg)]); 290 } 291 292 if (reg->wrmask > 0x1) 293 mesa_log_stream_printf(stream, " (wrmask=0x%x)", reg->wrmask); 294} 295 296static void 297tab(struct log_stream *stream, int lvl) 298{ 299 for (int i = 0; i < lvl; i++) 300 mesa_log_stream_printf(stream, "\t"); 301} 302 303static void 304print_instr(struct log_stream *stream, struct ir3_instruction *instr, int lvl) 305{ 306 tab(stream, lvl); 307 308 print_instr_name(stream, instr, true); 309 310 if (is_tex(instr)) { 311 mesa_log_stream_printf(stream, " (%s)(", type_name(instr->cat5.type)); 312 for (unsigned i = 0; i < 4; i++) 313 if (instr->dsts[0]->wrmask & (1 << i)) 314 mesa_log_stream_printf(stream, "%c", "xyzw"[i]); 315 mesa_log_stream_printf(stream, ")"); 316 } else if ((instr->srcs_count > 0 || instr->dsts_count > 0) && 317 (instr->opc != OPC_B)) { 318 /* NOTE the b(ranch) instruction has a suffix, which is 319 * handled below 320 */ 321 mesa_log_stream_printf(stream, " "); 322 } 323 324 if (!is_flow(instr) || instr->opc == OPC_END || instr->opc == OPC_CHMASK) { 325 bool first = true; 326 foreach_dst (reg, instr) { 327 if (reg->wrmask == 0) 328 continue; 329 if (!first) 330 mesa_log_stream_printf(stream, ", "); 331 print_reg_name(stream, instr, reg, true); 332 first = false; 333 } 334 foreach_src (reg, instr) { 335 if (!first) 336 mesa_log_stream_printf(stream, ", "); 337 print_reg_name(stream, instr, reg, false); 338 first = false; 339 } 340 } 341 342 if (is_tex(instr) && !(instr->flags & IR3_INSTR_S2EN)) { 343 if (!!(instr->flags & IR3_INSTR_B)) { 344 if (!!(instr->flags & IR3_INSTR_A1EN)) { 345 mesa_log_stream_printf(stream, ", s#%d", instr->cat5.samp); 346 } else { 347 mesa_log_stream_printf(stream, ", s#%d, t#%d", 348 instr->cat5.samp & 0xf, 349 instr->cat5.samp >> 4); 350 } 351 } else { 352 mesa_log_stream_printf(stream, ", s#%d, t#%d", instr->cat5.samp, 353 instr->cat5.tex); 354 } 355 } 356 357 if (instr->opc == OPC_META_SPLIT) { 358 mesa_log_stream_printf(stream, ", off=%d", instr->split.off); 359 } else if (instr->opc == OPC_META_TEX_PREFETCH) { 360 mesa_log_stream_printf(stream, ", tex=%d, samp=%d, input_offset=%d", 361 instr->prefetch.tex, instr->prefetch.samp, 362 instr->prefetch.input_offset); 363 } 364 365 if (is_flow(instr) && instr->cat0.target) { 366 /* the predicate register src is implied: */ 367 if (instr->opc == OPC_B) { 368 static const struct { 369 int nsrc; 370 bool idx; 371 } brinfo[7] = { 372 /* clang-format off */ 373 [BRANCH_PLAIN] = {1, false}, 374 [BRANCH_OR] = {2, false}, 375 [BRANCH_AND] = {2, false}, 376 [BRANCH_CONST] = {0, true}, 377 [BRANCH_ANY] = {1, false}, 378 [BRANCH_ALL] = {1, false}, 379 [BRANCH_X] = {0, false}, 380 /* clang-format on */ 381 }; 382 383 if (brinfo[instr->cat0.brtype].idx) { 384 mesa_log_stream_printf(stream, ".%u", instr->cat0.idx); 385 } 386 if (brinfo[instr->cat0.brtype].nsrc >= 1) { 387 mesa_log_stream_printf(stream, " %sp0.%c (", 388 instr->cat0.inv1 ? "!" : "", 389 "xyzw"[instr->cat0.comp1 & 0x3]); 390 print_reg_name(stream, instr, instr->srcs[0], false); 391 mesa_log_stream_printf(stream, "), "); 392 } 393 if (brinfo[instr->cat0.brtype].nsrc >= 2) { 394 mesa_log_stream_printf(stream, " %sp0.%c (", 395 instr->cat0.inv2 ? "!" : "", 396 "xyzw"[instr->cat0.comp2 & 0x3]); 397 print_reg_name(stream, instr, instr->srcs[1], false); 398 mesa_log_stream_printf(stream, "), "); 399 } 400 } 401 mesa_log_stream_printf(stream, " target=block%u", 402 block_id(instr->cat0.target)); 403 } 404 405 if (instr->deps_count) { 406 mesa_log_stream_printf(stream, ", false-deps:"); 407 unsigned n = 0; 408 for (unsigned i = 0; i < instr->deps_count; i++) { 409 if (!instr->deps[i]) 410 continue; 411 if (n++ > 0) 412 mesa_log_stream_printf(stream, ", "); 413 mesa_log_stream_printf(stream, SYN_SSA("ssa_%u"), 414 instr->deps[i]->serialno); 415 } 416 } 417 418 mesa_log_stream_printf(stream, "\n"); 419} 420 421void 422ir3_print_instr_stream(struct log_stream *stream, struct ir3_instruction *instr) 423{ 424 print_instr(stream, instr, 0); 425} 426 427void 428ir3_print_instr(struct ir3_instruction *instr) 429{ 430 struct log_stream *stream = mesa_log_streami(); 431 print_instr(stream, instr, 0); 432 mesa_log_stream_destroy(stream); 433} 434 435static void 436print_block(struct ir3_block *block, int lvl) 437{ 438 struct log_stream *stream = mesa_log_streami(); 439 440 tab(stream, lvl); 441 mesa_log_stream_printf(stream, "block%u {\n", block_id(block)); 442 443 if (block->predecessors_count > 0) { 444 tab(stream, lvl + 1); 445 mesa_log_stream_printf(stream, "pred: "); 446 for (unsigned i = 0; i < block->predecessors_count; i++) { 447 struct ir3_block *pred = block->predecessors[i]; 448 if (i != 0) 449 mesa_log_stream_printf(stream, ", "); 450 mesa_log_stream_printf(stream, "block%u", block_id(pred)); 451 } 452 mesa_log_stream_printf(stream, "\n"); 453 } 454 455 if (block->physical_predecessors_count > 0) { 456 tab(stream, lvl + 1); 457 mesa_log_stream_printf(stream, "physical pred: "); 458 for (unsigned i = 0; i < block->physical_predecessors_count; i++) { 459 struct ir3_block *pred = block->physical_predecessors[i]; 460 if (i != 0) 461 mesa_log_stream_printf(stream, ", "); 462 mesa_log_stream_printf(stream, "block%u", block_id(pred)); 463 } 464 mesa_log_stream_printf(stream, "\n"); 465 } 466 467 foreach_instr (instr, &block->instr_list) { 468 print_instr(stream, instr, lvl + 1); 469 } 470 471 tab(stream, lvl + 1); 472 mesa_log_stream_printf(stream, "/* keeps:\n"); 473 for (unsigned i = 0; i < block->keeps_count; i++) { 474 print_instr(stream, block->keeps[i], lvl + 2); 475 } 476 tab(stream, lvl + 1); 477 mesa_log_stream_printf(stream, " */\n"); 478 479 if (block->successors[1]) { 480 /* leading into if/else: */ 481 tab(stream, lvl + 1); 482 mesa_log_stream_printf(stream, "/* succs: if "); 483 switch (block->brtype) { 484 case IR3_BRANCH_COND: 485 break; 486 case IR3_BRANCH_ANY: 487 mesa_log_stream_printf(stream, "any "); 488 break; 489 case IR3_BRANCH_ALL: 490 mesa_log_stream_printf(stream, "all "); 491 break; 492 case IR3_BRANCH_GETONE: 493 mesa_log_stream_printf(stream, "getone "); 494 break; 495 } 496 if (block->condition) 497 mesa_log_stream_printf(stream, SYN_SSA("ssa_%u") " ", 498 block->condition->serialno); 499 mesa_log_stream_printf(stream, "block%u; else block%u; */\n", 500 block_id(block->successors[0]), 501 block_id(block->successors[1])); 502 } else if (block->successors[0]) { 503 tab(stream, lvl + 1); 504 mesa_log_stream_printf(stream, "/* succs: block%u; */\n", 505 block_id(block->successors[0])); 506 } 507 if (block->physical_successors[0]) { 508 tab(stream, lvl + 1); 509 mesa_log_stream_printf(stream, "/* physical succs: block%u", 510 block_id(block->physical_successors[0])); 511 if (block->physical_successors[1]) { 512 mesa_log_stream_printf(stream, ", block%u", 513 block_id(block->physical_successors[1])); 514 } 515 mesa_log_stream_printf(stream, " */\n"); 516 } 517 tab(stream, lvl); 518 mesa_log_stream_printf(stream, "}\n"); 519} 520 521void 522ir3_print(struct ir3 *ir) 523{ 524 foreach_block (block, &ir->block_list) 525 print_block(block, 0); 526} 527