1/*
2 * Copyright 2011 Christoph Bumiller
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 shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23#include "nv50_ir.h"
24#include "nv50_ir_target.h"
25
26namespace nv50_ir {
27
28enum TextStyle
29{
30   TXT_DEFAULT,
31   TXT_GPR,
32   TXT_REGISTER,
33   TXT_FLAGS,
34   TXT_MEM,
35   TXT_IMMD,
36   TXT_BRA,
37   TXT_INSN
38};
39
40static const char *_colour[8] =
41{
42   "\x1b[00m",
43   "\x1b[34m",
44   "\x1b[35m",
45   "\x1b[35m",
46   "\x1b[36m",
47   "\x1b[33m",
48   "\x1b[37m",
49   "\x1b[32m"
50};
51
52static const char *_nocolour[8] =
53{
54      "", "", "", "", "", "", "", ""
55};
56
57static const char **colour;
58
59static void init_colours()
60{
61   if (getenv("NV50_PROG_DEBUG_NO_COLORS") != NULL)
62      colour = _nocolour;
63   else
64      colour = _colour;
65}
66
67static const char *OpClassStr[OPCLASS_OTHER + 1] =
68{
69   "MOVE",
70   "LOAD",
71   "STORE",
72   "ARITH",
73   "SHIFT",
74   "SFU",
75   "LOGIC",
76   "COMPARE",
77   "CONVERT",
78   "ATOMIC",
79   "TEXTURE",
80   "SURFACE",
81   "FLOW",
82   "(INVALID)",
83   "PSEUDO",
84   "OTHER"
85};
86
87const char *operationStr[OP_LAST + 1] =
88{
89   "nop",
90   "phi",
91   "union",
92   "split",
93   "merge",
94   "consec",
95   "mov",
96   "ld",
97   "st",
98   "add",
99   "sub",
100   "mul",
101   "div",
102   "mod",
103   "mad",
104   "fma",
105   "sad",
106   "abs",
107   "neg",
108   "not",
109   "and",
110   "or",
111   "xor",
112   "shl",
113   "shr",
114   "max",
115   "min",
116   "sat",
117   "ceil",
118   "floor",
119   "trunc",
120   "cvt",
121   "set and",
122   "set or",
123   "set xor",
124   "set",
125   "selp",
126   "slct",
127   "rcp",
128   "rsq",
129   "lg2",
130   "sin",
131   "cos",
132   "ex2",
133   "exp",
134   "log",
135   "presin",
136   "preex2",
137   "sqrt",
138   "pow",
139   "bra",
140   "call",
141   "ret",
142   "cont",
143   "break",
144   "preret",
145   "precont",
146   "prebreak",
147   "brkpt",
148   "joinat",
149   "join",
150   "discard",
151   "exit",
152   "barrier",
153   "vfetch",
154   "pfetch",
155   "export",
156   "linterp",
157   "pinterp",
158   "emit",
159   "restart",
160   "tex",
161   "texbias",
162   "texlod",
163   "texfetch",
164   "texquery",
165   "texgrad",
166   "texgather",
167   "texcsaa",
168   "suld",
169   "sust",
170   "dfdx",
171   "dfdy",
172   "rdsv",
173   "wrsv",
174   "pixld",
175   "quadop",
176   "quadon",
177   "quadpop",
178   "popcnt",
179   "insbf",
180   "extbf",
181   "texbar",
182   "(invalid)"
183};
184
185static const char *DataTypeStr[] =
186{
187   "-",
188   "u8", "s8",
189   "u16", "s16",
190   "u32", "s32",
191   "u64", "s64",
192   "f16", "f32", "f64",
193   "b96", "b128"
194};
195
196static const char *RoundModeStr[] =
197{
198   "", "rm", "rz", "rp", "rni", "rmi", "rzi", "rpi"
199};
200
201static const char *CondCodeStr[] =
202{
203   "never",
204   "lt",
205   "eq",
206   "le",
207   "gt",
208   "ne",
209   "ge",
210   "",
211   "(invalid)",
212   "ltu",
213   "equ",
214   "leu",
215   "gtu",
216   "neu",
217   "geu",
218   "",
219   "no",
220   "nc",
221   "ns",
222   "na",
223   "a",
224   "s",
225   "c",
226   "o"
227};
228
229static const char *SemanticStr[SV_LAST + 1] =
230{
231   "POSITION",
232   "VERTEX_ID",
233   "INSTANCE_ID",
234   "INVOCATION_ID",
235   "PRIMITIVE_ID",
236   "VERTEX_COUNT",
237   "LAYER",
238   "VIEWPORT_INDEX",
239   "Y_DIR",
240   "FACE",
241   "POINT_SIZE",
242   "POINT_COORD",
243   "CLIP_DISTANCE",
244   "SAMPLE_INDEX",
245   "TESS_FACTOR",
246   "TESS_COORD",
247   "TID",
248   "CTAID",
249   "NTID",
250   "GRIDID",
251   "NCTAID",
252   "LANEID",
253   "PHYSID",
254   "NPHYSID",
255   "CLOCK",
256   "LBASE",
257   "SBASE",
258   "?",
259   "(INVALID)"
260};
261
262static const char *interpStr[16] =
263{
264   "pass",
265   "mul",
266   "flat",
267   "sc",
268   "cent pass",
269   "cent mul",
270   "cent flat",
271   "cent sc",
272   "off pass",
273   "off mul",
274   "off flat",
275   "off sc",
276   "samp pass",
277   "samp mul",
278   "samp flat",
279   "samp sc"
280};
281
282#define PRINT(args...)                                \
283   do {                                               \
284      pos += snprintf(&buf[pos], size - pos, args);   \
285   } while(0)
286
287#define SPACE_PRINT(cond, args...)                      \
288   do {                                                 \
289      if (cond)                                         \
290         buf[pos++] = ' ';                              \
291      pos += snprintf(&buf[pos], size - pos, args);     \
292   } while(0)
293
294#define SPACE()                                    \
295   do {                                            \
296      if (pos < size)                              \
297         buf[pos++] = ' ';                         \
298   } while(0)
299
300int Modifier::print(char *buf, size_t size) const
301{
302   size_t pos = 0;
303
304   if (bits)
305      PRINT("%s", colour[TXT_INSN]);
306
307   size_t base = pos;
308
309   if (bits & NV50_IR_MOD_NOT)
310      PRINT("not");
311   if (bits & NV50_IR_MOD_SAT)
312      SPACE_PRINT(pos > base && pos < size, "sat");
313   if (bits & NV50_IR_MOD_NEG)
314      SPACE_PRINT(pos > base && pos < size, "neg");
315   if (bits & NV50_IR_MOD_ABS)
316      SPACE_PRINT(pos > base && pos < size, "abs");
317
318   return pos;
319}
320
321int LValue::print(char *buf, size_t size, DataType ty) const
322{
323   const char *postFix = "";
324   size_t pos = 0;
325   int idx = join->reg.data.id >= 0 ? join->reg.data.id : id;
326   char p = join->reg.data.id >= 0 ? '$' : '%';
327   char r;
328   int col = TXT_DEFAULT;
329
330   switch (reg.file) {
331   case FILE_GPR:
332      r = 'r'; col = TXT_GPR;
333      if (reg.size == 2) {
334         if (p == '$') {
335            postFix = (idx & 1) ? "h" : "l";
336            idx /= 2;
337         } else {
338            postFix = "s";
339         }
340      } else
341      if (reg.size == 8) {
342         postFix = "d";
343      } else
344      if (reg.size == 16) {
345         postFix = "q";
346      } else
347      if (reg.size == 12) {
348         postFix = "t";
349      }
350      break;
351   case FILE_PREDICATE:
352      r = 'p'; col = TXT_REGISTER;
353      if (reg.size == 2)
354         postFix = "d";
355      else
356      if (reg.size == 4)
357         postFix = "q";
358      break;
359   case FILE_FLAGS:
360      r = 'c'; col = TXT_FLAGS;
361      break;
362   case FILE_ADDRESS:
363      r = 'a'; col = TXT_REGISTER;
364      break;
365   default:
366      assert(!"invalid file for lvalue");
367      r = '?';
368      break;
369   }
370
371   PRINT("%s%c%c%i%s", colour[col], p, r, idx, postFix);
372
373   return pos;
374}
375
376int ImmediateValue::print(char *buf, size_t size, DataType ty) const
377{
378   size_t pos = 0;
379
380   PRINT("%s", colour[TXT_IMMD]);
381
382   switch (ty) {
383   case TYPE_F32: PRINT("%f", reg.data.f32); break;
384   case TYPE_F64: PRINT("%f", reg.data.f64); break;
385   case TYPE_U8:  PRINT("0x%02x", reg.data.u8); break;
386   case TYPE_S8:  PRINT("%i", reg.data.s8); break;
387   case TYPE_U16: PRINT("0x%04x", reg.data.u16); break;
388   case TYPE_S16: PRINT("%i", reg.data.s16); break;
389   case TYPE_U32: PRINT("0x%08x", reg.data.u32); break;
390   case TYPE_S32: PRINT("%i", reg.data.s32); break;
391   case TYPE_U64:
392   case TYPE_S64:
393   default:
394      PRINT("0x%016lx", reg.data.u64);
395      break;
396   }
397   return pos;
398}
399
400int Symbol::print(char *buf, size_t size, DataType ty) const
401{
402   return print(buf, size, NULL, NULL, ty);
403}
404
405int Symbol::print(char *buf, size_t size,
406                  Value *rel, Value *dimRel, DataType ty) const
407{
408   size_t pos = 0;
409   char c;
410
411   if (ty == TYPE_NONE)
412      ty = typeOfSize(reg.size);
413
414   if (reg.file == FILE_SYSTEM_VALUE) {
415      PRINT("%ssv[%s%s:%i%s", colour[TXT_MEM],
416            colour[TXT_REGISTER],
417            SemanticStr[reg.data.sv.sv], reg.data.sv.index, colour[TXT_MEM]);
418      if (rel) {
419         PRINT("%s+", colour[TXT_DEFAULT]);
420         pos += rel->print(&buf[pos], size - pos);
421      }
422      PRINT("%s]", colour[TXT_MEM]);
423      return pos;
424   }
425
426   switch (reg.file) {
427   case FILE_MEMORY_CONST:  c = 'c'; break;
428   case FILE_SHADER_INPUT:  c = 'a'; break;
429   case FILE_SHADER_OUTPUT: c = 'o'; break;
430   case FILE_MEMORY_GLOBAL: c = 'g'; break;
431   case FILE_MEMORY_SHARED: c = 's'; break;
432   case FILE_MEMORY_LOCAL:  c = 'l'; break;
433   default:
434      assert(!"invalid file");
435      c = '?';
436      break;
437   }
438
439   if (c == 'c')
440      PRINT("%s%c%i[", colour[TXT_MEM], c, reg.fileIndex);
441   else
442      PRINT("%s%c[", colour[TXT_MEM], c);
443
444   if (dimRel) {
445      pos += dimRel->print(&buf[pos], size - pos, TYPE_S32);
446      PRINT("%s][", colour[TXT_MEM]);
447   }
448
449   if (rel) {
450      pos += rel->print(&buf[pos], size - pos);
451      PRINT("%s%c", colour[TXT_DEFAULT], (reg.data.offset < 0) ? '-' : '+');
452   } else {
453      assert(reg.data.offset >= 0);
454   }
455   PRINT("%s0x%x%s]", colour[TXT_IMMD], abs(reg.data.offset), colour[TXT_MEM]);
456
457   return pos;
458}
459
460void Instruction::print() const
461{
462   #define BUFSZ 512
463
464   const size_t size = BUFSZ;
465
466   char buf[BUFSZ];
467   int s, d;
468   size_t pos = 0;
469
470   PRINT("%s", colour[TXT_INSN]);
471
472   if (join)
473      PRINT("join ");
474
475   if (predSrc >= 0) {
476      const size_t pre = pos;
477      if (getSrc(predSrc)->reg.file == FILE_PREDICATE) {
478         if (cc == CC_NOT_P)
479            PRINT("not");
480      } else {
481         PRINT("%s", CondCodeStr[cc]);
482      }
483      if (pos > pre)
484         SPACE();
485      pos += getSrc(predSrc)->print(&buf[pos], BUFSZ - pos);
486      PRINT(" %s", colour[TXT_INSN]);
487   }
488
489   if (saturate)
490      PRINT("sat ");
491
492   if (asFlow()) {
493      PRINT("%s", operationStr[op]);
494      if (op == OP_CALL && asFlow()->builtin) {
495         PRINT(" %sBUILTIN:%i", colour[TXT_BRA], asFlow()->target.builtin);
496      } else
497      if (op == OP_CALL && asFlow()->target.fn) {
498         PRINT(" %s%s:%i", colour[TXT_BRA],
499               asFlow()->target.fn->getName(),
500               asFlow()->target.fn->getLabel());
501      } else
502      if (asFlow()->target.bb)
503         PRINT(" %sBB:%i", colour[TXT_BRA], asFlow()->target.bb->getId());
504   } else {
505      PRINT("%s ", operationStr[op]);
506      if (op == OP_LINTERP || op == OP_PINTERP)
507         PRINT("%s ", interpStr[ipa]);
508      if (subOp)
509         PRINT("(SUBOP:%u) ", subOp);
510      if (perPatch)
511         PRINT("patch ");
512      if (asTex())
513         PRINT("%s ", asTex()->tex.target.getName());
514      if (postFactor)
515         PRINT("x2^%i ", postFactor);
516      PRINT("%s%s", dnz ? "dnz " : (ftz ? "ftz " : ""),  DataTypeStr[dType]);
517   }
518
519   if (rnd != ROUND_N)
520      PRINT(" %s", RoundModeStr[rnd]);
521
522   if (defExists(1))
523      PRINT(" {");
524   for (d = 0; defExists(d); ++d) {
525      SPACE();
526      pos += getDef(d)->print(&buf[pos], size - pos);
527   }
528   if (d > 1)
529      PRINT(" %s}", colour[TXT_INSN]);
530   else
531   if (!d && !asFlow())
532      PRINT(" %s#", colour[TXT_INSN]);
533
534   if (asCmp())
535      PRINT(" %s%s", colour[TXT_INSN], CondCodeStr[asCmp()->setCond]);
536
537   if (sType != dType)
538      PRINT(" %s%s", colour[TXT_INSN], DataTypeStr[sType]);
539
540   for (s = 0; srcExists(s); ++s) {
541      if (s == predSrc || src(s).usedAsPtr)
542         continue;
543      const size_t pre = pos;
544      SPACE();
545      pos += src(s).mod.print(&buf[pos], BUFSZ - pos);
546      if (pos > pre + 1)
547         SPACE();
548      if (src(s).isIndirect(0) || src(s).isIndirect(1))
549         pos += getSrc(s)->asSym()->print(&buf[pos], BUFSZ - pos,
550                                          getIndirect(s, 0),
551                                          getIndirect(s, 1));
552      else
553         pos += getSrc(s)->print(&buf[pos], BUFSZ - pos, sType);
554   }
555   if (exit)
556      PRINT("%s exit", colour[TXT_INSN]);
557
558   PRINT("%s", colour[TXT_DEFAULT]);
559
560   buf[MIN2(pos, BUFSZ - 1)] = 0;
561
562   INFO("%s (%u)\n", buf, encSize);
563}
564
565class PrintPass : public Pass
566{
567public:
568   PrintPass() : serial(0) { }
569
570   virtual bool visit(Function *);
571   virtual bool visit(BasicBlock *);
572   virtual bool visit(Instruction *);
573
574private:
575   int serial;
576};
577
578bool
579PrintPass::visit(Function *fn)
580{
581   INFO("\n%s:%i\n", fn->getName(), fn->getLabel());
582
583   return true;
584}
585
586bool
587PrintPass::visit(BasicBlock *bb)
588{
589#if 0
590   INFO("---\n");
591   for (Graph::EdgeIterator ei = bb->cfg.incident(); !ei.end(); ei.next())
592      INFO(" <- BB:%i (%s)\n",
593           BasicBlock::get(ei.getNode())->getId(),
594           ei.getEdge()->typeStr());
595#endif
596   INFO("BB:%i (%u instructions) - ", bb->getId(), bb->getInsnCount());
597
598   if (bb->idom())
599      INFO("idom = BB:%i, ", bb->idom()->getId());
600
601   INFO("df = { ");
602   for (DLList::Iterator df = bb->getDF().iterator(); !df.end(); df.next())
603      INFO("BB:%i ", BasicBlock::get(df)->getId());
604
605   INFO("}\n");
606
607   for (Graph::EdgeIterator ei = bb->cfg.outgoing(); !ei.end(); ei.next())
608      INFO(" -> BB:%i (%s)\n",
609           BasicBlock::get(ei.getNode())->getId(),
610           ei.getEdge()->typeStr());
611
612   return true;
613}
614
615bool
616PrintPass::visit(Instruction *insn)
617{
618   INFO("%3i: ", serial++);
619   insn->print();
620   return true;
621}
622
623void
624Function::print()
625{
626   PrintPass pass;
627   pass.run(this, true, false);
628}
629
630void
631Program::print()
632{
633   PrintPass pass;
634   init_colours();
635   pass.run(this, true, false);
636}
637
638void
639Function::printLiveIntervals() const
640{
641   INFO("printing live intervals ...\n");
642
643   for (ArrayList::Iterator it = allLValues.iterator(); !it.end(); it.next()) {
644      const Value *lval = Value::get(it)->asLValue();
645      if (lval && !lval->livei.isEmpty()) {
646         INFO("livei(%%%i): ", lval->id);
647         lval->livei.print();
648      }
649   }
650}
651
652} // namespace nv50_ir
653