1 /**
2  * Pretty print data structures
3  *
4  * Compiler implementation of the
5  * $(LINK2 https://www.dlang.org, D programming language).
6  *
7  * Copyright:   Copyright (C) 1985-1998 by Symantec
8  *              Copyright (C) 2000-2023 by The D Language Foundation, All Rights Reserved
9  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
10  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
11  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/debug.c, backend/debugprint.d)
12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/debug.c
13  */
14 
15 module dmd.backend.debugprint;
16 
17 import core.stdc.stdio;
18 import core.stdc.stdlib;
19 import core.stdc.string;
20 
21 import dmd.backend.cdef;
22 import dmd.backend.cc;
23 import dmd.backend.el;
24 import dmd.backend.global;
25 import dmd.backend.code;
26 import dmd.backend.code_x86;
27 import dmd.backend.goh;
28 import dmd.backend.oper;
29 import dmd.backend.symtab;
30 import dmd.backend.ty;
31 import dmd.backend.type;
32 
33 import dmd.backend.dlist;
34 import dmd.backend.dvec;
35 
36 
37 nothrow:
38 @safe:
39 
40 @trusted
41 void ferr(const(char)* p) { printf("%s", p); }
42 
43 /*******************************
44  * Write out storage class.
45  */
46 
47 @trusted
48 const(char)* class_str(SC c)
49 {
50     __gshared const char[10][SCMAX] sc =
51     [
52         "unde",
53         "auto",
54         "static",
55         "thread",
56         "extern",
57         "register",
58         "pseudo",
59         "global",
60         "comdat",
61         "parameter",
62         "regpar",
63         "fastpar",
64         "shadowreg",
65         "typedef",
66         "explicit",
67         "mutable",
68         "label",
69         "struct",
70         "enum",
71         "field",
72         "const",
73         "member",
74         "anon",
75         "inline",
76         "sinline",
77         "einline",
78         "overload",
79         "friend",
80         "virtual",
81         "locstat",
82         "template",
83         "functempl",
84         "ftexpspec",
85         "linkage",
86         "public",
87         "comdef",
88         "bprel",
89         "namespace",
90         "alias",
91         "funcalias",
92         "memalias",
93         "stack",
94         "adl",
95     ];
96     __gshared char[9 + 3] buffer;
97 
98     static assert(sc.length == SCMAX);
99     if (cast(uint) c < SCMAX)
100         snprintf(buffer.ptr,buffer.length,"SC%s",sc[c].ptr);
101     else
102         snprintf(buffer.ptr,buffer.length,"SC%u",cast(uint)c);
103     assert(strlen(buffer.ptr) < buffer.length);
104     return buffer.ptr;
105 }
106 
107 /***************************
108  * Convert OPER to string.
109  * Params:
110  *      oper = operator number
111  * Returns:
112  *      pointer to string
113  */
114 
115 const(char)* oper_str(uint oper) pure
116 {
117     assert(oper < OPMAX);
118     return &debtab[oper][0];
119 }
120 
121 /*******************************
122  * Convert tym_t to string.
123  * Params:
124  *      ty = type number
125  * Returns:
126  *      pointer to malloc'd string
127  */
128 @trusted
129 const(char)* tym_str(tym_t ty)
130 {
131     enum MAX = 100;
132     __gshared char[MAX + 1] buf;
133 
134     char* pstart = &buf[0];
135     char* p = pstart;
136     *p = 0;
137     if (ty & mTYnear)
138         strcat(p, "mTYnear|");
139     if (ty & mTYfar)
140         strcat(p, "mTYfar|");
141     if (ty & mTYcs)
142         strcat(p, "mTYcs|");
143     if (ty & mTYconst)
144         strcat(p, "mTYconst|");
145     if (ty & mTYvolatile)
146         strcat(p, "mTYvolatile|");
147     if (ty & mTYshared)
148         strcat(p, "mTYshared|");
149     if (ty & mTYxmmgpr)
150         strcat(p, "mTYxmmgpr|");
151     if (ty & mTYgprxmm)
152         strcat(p, "mTYgprxmm|");
153     const tyb = tybasic(ty);
154     if (tyb >= TYMAX)
155     {
156         printf("TY %x\n",cast(int)ty);
157         assert(0);
158     }
159     strcat(p, "TY");
160     strcat(p, tystring[tyb]);
161     assert(strlen(p) <= MAX);
162     return strdup(p);
163 }
164 
165 /*******************************
166  * Convert BC to string.
167  * Params:
168  *      bc = block exit code
169  * Returns:
170  *      pointer to string
171  */
172 @trusted
173 const(char)* bc_str(uint bc)
174 {
175     __gshared const char[9][BCMAX] bcs =
176         ["BCunde  ","BCgoto  ","BCtrue  ","BCret   ","BCretexp",
177          "BCexit  ","BCasm   ","BCswitch","BCifthen","BCjmptab",
178          "BCtry   ","BCcatch ","BCjump  ",
179          "BC_try  ","BC_filte","BC_final","BC_ret  ","BC_excep",
180          "BCjcatch","BC_lpad ",
181         ];
182 
183     return bcs[bc].ptr;
184 }
185 
186 /************************
187  * Write arglst
188  */
189 
190 @trusted
191 void WRarglst(list_t a)
192 { int n = 1;
193 
194   if (!a) printf("0 args\n");
195   while (a)
196   {     const(char)* c = cast(const(char)*)list_ptr(a);
197         printf("arg %d: '%s'\n", n, c ? c : "NULL");
198         a = a.next;
199         n++;
200   }
201 }
202 
203 /***************************
204  * Write out equation elem.
205  */
206 
207 @trusted
208 void WReqn(elem *e)
209 { __gshared int nest;
210 
211   if (!e)
212         return;
213   if (OTunary(e.Eoper))
214   {
215         ferr(oper_str(e.Eoper));
216         ferr(" ");
217         if (OTbinary(e.EV.E1.Eoper))
218         {       nest++;
219                 ferr("(");
220                 WReqn(e.EV.E1);
221                 ferr(")");
222                 nest--;
223         }
224         else
225                 WReqn(e.EV.E1);
226   }
227   else if (e.Eoper == OPcomma && !nest)
228   {     WReqn(e.EV.E1);
229         printf(";\n\t");
230         WReqn(e.EV.E2);
231   }
232   else if (OTbinary(e.Eoper))
233   {
234         if (OTbinary(e.EV.E1.Eoper))
235         {       nest++;
236                 ferr("(");
237                 WReqn(e.EV.E1);
238                 ferr(")");
239                 nest--;
240         }
241         else
242                 WReqn(e.EV.E1);
243         ferr(" ");
244         ferr(oper_str(e.Eoper));
245         ferr(" ");
246         if (e.Eoper == OPstreq)
247             printf("%d", cast(int)type_size(e.ET));
248         ferr(" ");
249         if (OTbinary(e.EV.E2.Eoper))
250         {       nest++;
251                 ferr("(");
252                 WReqn(e.EV.E2);
253                 ferr(")");
254                 nest--;
255         }
256         else
257                 WReqn(e.EV.E2);
258   }
259   else
260   {
261         switch (e.Eoper)
262         {   case OPconst:
263                 elem_print_const(e);
264                 break;
265             case OPrelconst:
266                 ferr("#");
267                 goto case OPvar;
268 
269             case OPvar:
270                 printf("%s",e.EV.Vsym.Sident.ptr);
271                 if (e.EV.Vsym.Ssymnum != SYMIDX.max)
272                     printf("(%d)", cast(int) e.EV.Vsym.Ssymnum);
273                 if (e.EV.Voffset != 0)
274                 {
275                     if (e.EV.Voffset.sizeof == 8)
276                         printf(".x%llx", cast(ulong)e.EV.Voffset);
277                     else
278                         printf(".%d",cast(int)e.EV.Voffset);
279                 }
280                 break;
281             case OPasm:
282             case OPstring:
283                 printf("\"%s\"",e.EV.Vstring);
284                 if (e.EV.Voffset)
285                     printf("+%lld",cast(long)e.EV.Voffset);
286                 break;
287             case OPmark:
288             case OPgot:
289             case OPframeptr:
290             case OPhalt:
291             case OPdctor:
292             case OPddtor:
293                 ferr(oper_str(e.Eoper));
294                 ferr(" ");
295                 break;
296             case OPstrthis:
297                 break;
298             default:
299                 ferr(oper_str(e.Eoper));
300                 assert(0);
301         }
302   }
303 }
304 
305 @trusted
306 void WRblocklist(list_t bl)
307 {
308     foreach (bl2; ListRange(bl))
309     {
310         block *b = list_block(bl2);
311 
312         if (b && b.Bweight)
313             printf("B%d (%p) ",b.Bdfoidx,b);
314         else
315             printf("%p ",b);
316     }
317     ferr("\n");
318 }
319 
320 @trusted
321 void WRdefnod()
322 { int i;
323 
324   for (i = 0; i < go.defnod.length; i++)
325   {     printf("defnod[%d] in B%d = (", go.defnod[i].DNblock.Bdfoidx, i);
326         WReqn(go.defnod[i].DNelem);
327         printf(");\n");
328   }
329 }
330 
331 @trusted
332 void WRFL(FL fl)
333 {
334     __gshared const(char)[7][FLMAX] fls =
335     [    "unde  ","const ","oper  ","func  ","data  ",
336          "reg   ",
337          "pseudo",
338          "auto  ","fast  ","para  ","extrn ",
339          "code  ","block ","udata ","cs    ","swit  ",
340          "fltrg ","offst ","datsg ",
341          "ctor  ","dtor  ","regsav","asm   ",
342          "ndp   ",
343          "farda ","csdat ",
344          "local ","tlsdat",
345          "bprel ","frameh","blocko","alloca",
346          "stack ","dsym  ",
347          "got   ","gotoff",
348          "funcar",
349     ];
350 
351     if (cast(uint)fl >= FLMAX)
352         printf("FL%d",fl);
353     else
354       printf("FL%s",fls[fl].ptr);
355 }
356 
357 /***********************
358  * Write out block.
359  */
360 
361 @trusted
362 void WRblock(block *b)
363 {
364     if (OPTIMIZER)
365     {
366         if (b && b.Bweight)
367                 printf("B%d: (%p), weight=%d",b.Bdfoidx,b,b.Bweight);
368         else
369                 printf("block %p",b);
370         if (!b)
371         {       ferr("\n");
372                 return;
373         }
374         printf(" flags=x%x weight=%d",b.Bflags,b.Bweight);
375         //printf("\tfile %p, line %d",b.Bfilptr,b.Blinnum);
376         printf(" %s Btry=%p Bindex=%d",bc_str(b.BC),b.Btry,b.Bindex);
377         if (b.BC == BCtry)
378             printf(" catchvar = %p",b.catchvar);
379         printf("\n");
380         printf("\tBpred: "); WRblocklist(b.Bpred);
381         printf("\tBsucc: "); WRblocklist(b.Bsucc);
382         if (b.Belem)
383         {       if (debugf)                     /* if full output       */
384                         elem_print(b.Belem);
385                 else
386                 {       ferr("\t");
387                         WReqn(b.Belem);
388                         printf(";\n");
389                 }
390         }
391         if (b.Bcode)
392             b.Bcode.print();
393         ferr("\n");
394     }
395     else
396     {
397         assert(b);
398         printf("%2d: %s", b.Bnumber, bc_str(b.BC));
399         if (b.Btry)
400             printf(" Btry=B%d",b.Btry ? b.Btry.Bnumber : 0);
401         if (b.Bindex)
402             printf(" Bindex=%d",b.Bindex);
403         if (b.BC == BC_finally)
404             printf(" b_ret=B%d", b.b_ret ? b.b_ret.Bnumber : 0);
405         if (b.Bsrcpos.Sfilename)
406             printf(" %s(%u)", b.Bsrcpos.Sfilename, b.Bsrcpos.Slinnum);
407         printf("\n");
408         if (b.Belem)
409         {
410             if (debugf)
411                 elem_print(b.Belem);
412             else
413             {
414                 ferr("\t");
415                 WReqn(b.Belem);
416                 printf(";\n");
417             }
418         }
419         if (b.Bpred)
420         {
421             printf("\tBpred:");
422             foreach (bl; ListRange(b.Bpred))
423                 printf(" B%d",list_block(bl).Bnumber);
424             printf("\n");
425         }
426 
427         switch (b.BC)
428         {
429             case BCswitch:
430                 printf("\tncases = %d\n", cast(int)b.Bswitch.length);
431                 list_t bl = b.Bsucc;
432                 printf("\tdefault: B%d\n",list_block(bl) ? list_block(bl).Bnumber : 0);
433                 foreach (val; b.Bswitch)
434                 {
435                     bl = list_next(bl);
436                     printf("\tcase %lld: B%d\n", cast(long)val, list_block(bl).Bnumber);
437                 }
438                 break;
439 
440             case BCiftrue:
441             case BCgoto:
442             case BCasm:
443             case BCtry:
444             case BCcatch:
445             case BCjcatch:
446             case BC_try:
447             case BC_filter:
448             case BC_finally:
449             case BC_lpad:
450             case BC_ret:
451             case BC_except:
452                 if (list_t bl = b.Bsucc)
453                 {
454                     printf("\tBsucc:");
455                     for ( ; bl; bl = list_next(bl))
456                         printf(" B%d",list_block(bl).Bnumber);
457                     printf("\n");
458                 }
459                 break;
460 
461             case BCret:
462             case BCretexp:
463             case BCexit:
464                 break;
465 
466             default:
467                 printf("bc = %d\n", b.BC);
468                 assert(0);
469         }
470     }
471 }
472 
473 /*****************************
474  * Number the blocks starting at 1.
475  * So much more convenient than pointer values.
476  */
477 void numberBlocks(block *startblock)
478 {
479     uint number = 0;
480     for (block *b = startblock; b; b = b.Bnext)
481         b.Bnumber = ++number;
482 }
483 
484 /**************************************
485  * Print out the intermediate code for a function.
486  * Params:
487  *      msg = label for the print
488  *      sfunc = function to print
489  *      startblock = intermediate code
490  */
491 @trusted
492 void WRfunc(const char* msg, Symbol* sfunc, block* startblock)
493 {
494     printf("............%s...%s().............\n", msg, sfunc.Sident.ptr);
495     numberBlocks(startblock);
496     for (block *b = startblock; b; b = b.Bnext)
497         WRblock(b);
498 }