- add options to the list tail, so they are shown in the same order they are declared
[libfirm] / ir / debug / firm_ycomp.c
1 /*
2  * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
3  *
4  * This file is part of libFirm.
5  *
6  * This file may be distributed and/or modified under the terms of the
7  * GNU General Public License version 2 as published by the Free Software
8  * Foundation and appearing in the file LICENSE.GPL included in the
9  * packaging of this file.
10  *
11  * Licensees holding valid libFirm Professional Edition licenses may use
12  * this file in accordance with the libFirm Commercial License.
13  * Agreement provided with the Software.
14  *
15  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE.
18  */
19
20 /**
21  * @file
22  * @brief      Connect firm to ycomp
23  * @author     Christian Wuerdig
24  * @date       16.11.2006
25  * @version    $Id$
26  */
27 #include "config.h"
28
29 #include "assert.h"
30 #include "irhooks.h"
31 #include "firmnet.h"
32 #include "irnode.h"
33 #include "irgraph.h"
34 #include "irprintf.h"
35 #include "irprog.h"
36 #include "iredges.h"
37 #include "pset.h"
38 #include "obst.h"
39 #include "firmnet.h"
40
41 #define SEND_BUF_SIZE 256
42 #define HASH_EDGE(edge) \
43         ((get_irn_node_nr((edge)->src) << 17)          | \
44         ((get_irn_node_nr((edge)->tgt) & 0xEFFF) << 2) | \
45         ((edge)->pos & 0x3))
46
47 typedef struct _exchange_node_outs_assoc_t {
48         int     n_out_edges;
49         ir_node *irn;
50         ir_node *nw;
51 } exchange_node_outs_assoc_t;
52
53 typedef struct _ycomp_edge_t {
54         ir_node *src;
55         ir_node *tgt;
56         int     pos;
57 } ycomp_edge_t;
58
59 enum _firm_ycomp_node_realizer_values {
60         NODE_REALIZER_NORMAL,
61         NODE_REALIZER_PROJ,
62         NODE_REALIZER_BLOCK,
63         NODE_REALIZER_MEM,
64         NODE_REALIZER_PHI,
65         NODE_REALIZER_STARTEND,
66         NODE_REALIZER_IRG,
67         NODE_REALIZER_ID,
68         NODE_REALIZER_LAST
69 };
70
71 typedef struct _firm_ycomp_node_realizer_t {
72         unsigned   id;
73         const char *linecolor;
74         const char *fillcolor;
75         const char *textcolor;
76         const char *shape;
77 } firm_ycomp_node_realizer_t;
78
79 static firm_ycomp_node_realizer_t node_realizer[NODE_REALIZER_LAST] = {
80         { NODE_REALIZER_NORMAL,   "black", "white",    "black", "box" },
81         { NODE_REALIZER_PROJ,     "black", "yellow",   "black", "box" },
82         { NODE_REALIZER_BLOCK,    "black", "yellow",   "white", "box" },
83         { NODE_REALIZER_MEM,      "black", "blue",     "black", "box" },
84         { NODE_REALIZER_PHI,      "black", "green",    "black", "box" },
85         { NODE_REALIZER_STARTEND, "black", "blue",     "black", "box" },
86         { NODE_REALIZER_IRG,      "black", "white",    "white", "box" },
87         { NODE_REALIZER_ID,       "black", "darkgrey", "black", "box" },
88 };
89
90 enum _firm_ycomp_edge_realizer_values {
91         EDGE_REALIZER_DATA,
92         EDGE_REALIZER_MEM,
93         EDGE_REALIZER_DEP,
94         EDGE_REALIZER_CFG,
95         EDGE_REALIZER_LAST
96 };
97
98 typedef struct _firm_ycomp_edge_realizer_t {
99         unsigned   id;
100         const char *linecolor;
101         const char *textcolor;
102         unsigned   thickness;
103         const char *style;
104 } firm_ycomp_edge_realizer_t;
105
106 static firm_ycomp_edge_realizer_t edge_realizer[EDGE_REALIZER_LAST] = {
107         { EDGE_REALIZER_DATA, "black", "black", 1, "continuous" },
108         { EDGE_REALIZER_MEM,  "blue",  "black", 1, "continuous" },
109         { EDGE_REALIZER_DEP,  "green", "black", 1, "continuous" },
110         { EDGE_REALIZER_CFG,  "red",   "black", 1, "continuous" },
111 };
112
113 typedef struct _firm_ycomp_dbg_t {
114         int            fd;
115         int            has_data;
116         pset           *exchanged_nodes;
117         pset           *edges;
118         unsigned       in_dead_node_elim : 1;
119         struct obstack obst;
120         hook_entry_t   hook_new_irn;
121         hook_entry_t   hook_new_irg;
122         hook_entry_t   hook_set_edge;
123         hook_entry_t   hook_exchange;
124         hook_entry_t   hook_into_id;
125         hook_entry_t   hook_dead_node;
126 } firm_ycomp_dbg_t;
127
128 static firm_ycomp_dbg_t yy_dbg;
129
130 static int cmp_edges(const void *a, const void *b) {
131         ycomp_edge_t *e1 = (ycomp_edge_t *)a;
132         ycomp_edge_t *e2 = (ycomp_edge_t *)b;
133
134         return (e1->src != e2->src) || (e1->tgt != e2->tgt) || (e1->pos != e2->pos);
135 }
136
137 static int cmp_nodes(const void *a, const void *b) {
138         exchange_node_outs_assoc_t *n1 = (exchange_node_outs_assoc_t *)a;
139         exchange_node_outs_assoc_t *n2 = (exchange_node_outs_assoc_t *)b;
140
141         return n1->irn != n2->irn;
142 }
143
144 static INLINE void send_cmd(firm_ycomp_dbg_t *dbg, const char *buf) {
145         ssize_t res, len;
146
147         len = strlen(buf);
148         res = firmnet_send(dbg->fd, (const void *)buf, len);
149         assert(res == len);
150 }
151
152 static void wait_for_sync(firm_ycomp_dbg_t *dbg) {
153         char buf[6];
154         firmnet_recv(dbg->fd, buf, 6, 5);
155 }
156
157 static void show_and_sync(firm_ycomp_dbg_t *dbg) {
158         send_cmd(dbg, "show\n");
159         send_cmd(dbg, "sync\n");
160         wait_for_sync(dbg);
161 }
162
163 /**
164  * Register the edge and node realizer in yComp.
165  */
166 static void firm_ycomp_debug_init_realizer(firm_ycomp_dbg_t *dbg) {
167         int  i;
168         char buf[SEND_BUF_SIZE];
169
170         for (i = 0; i < NODE_REALIZER_LAST; ++i) {
171                 snprintf(buf, sizeof(buf), "addNodeRealizer \"%u\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
172                         node_realizer[i].id,
173                         node_realizer[i].linecolor,
174                         node_realizer[i].fillcolor,
175                         node_realizer[i].textcolor,
176                         node_realizer[i].shape);
177                 send_cmd(dbg, buf);
178         }
179
180         for (i = 0; i < EDGE_REALIZER_LAST; ++i) {
181                 snprintf(buf, sizeof(buf), "addEdgeRealizer \"%u\" \"%s\" \"%s\" \"%u\" \"%s\"\n",
182                         edge_realizer[i].id,
183                         edge_realizer[i].linecolor,
184                         edge_realizer[i].textcolor,
185                         edge_realizer[i].thickness,
186                         edge_realizer[i].style);
187                 send_cmd(dbg, buf);
188         }
189 }
190
191 /**
192  * Retrieve the appropriate realizer for given node.
193  */
194 static INLINE unsigned get_node_realizer(ir_node *node) {
195         unsigned realizer;
196         ir_opcode opc = get_irn_opcode(node);
197
198         switch (opc) {
199                 case iro_Block:
200                         realizer = NODE_REALIZER_BLOCK;
201                         break;
202                 case iro_Phi:
203                         realizer = NODE_REALIZER_PHI;
204                         break;
205                 case iro_Proj:
206                         if (get_irn_mode(node) == mode_M)
207                                 realizer = NODE_REALIZER_MEM;
208                         else
209                                 realizer = NODE_REALIZER_PROJ;
210                         break;
211                 case iro_Start:
212                 case iro_End:
213                         realizer = NODE_REALIZER_STARTEND;
214                         break;
215                 default:
216                         realizer = NODE_REALIZER_NORMAL;
217         };
218
219         return realizer;
220 }
221
222 /**
223  * Retrieve the appropriate realizer for given edge.
224  */
225 static INLINE unsigned get_edge_realizer(ir_node *src, ir_node *tgt) {
226         unsigned realizer;
227         ir_mode  *tgt_mode, *src_mode;
228
229         if (is_Block(tgt) || is_Block(src))
230                 return EDGE_REALIZER_CFG;
231
232         tgt_mode = get_irn_mode(tgt);
233         src_mode = is_Block(src) ? NULL : get_irn_mode(src);
234
235         if (tgt_mode == mode_M || (src_mode == mode_M && tgt_mode == mode_T))
236                 realizer = EDGE_REALIZER_MEM;
237         else if (tgt_mode == mode_X)
238                 realizer = EDGE_REALIZER_CFG;
239         else
240                 realizer = EDGE_REALIZER_DATA;
241
242         return realizer;
243 }
244
245 /**
246  * Hook: Add new nodes, resp. new blocks in yComp and add input edges.
247  */
248 static void firm_ycomp_debug_new_node(void *context, ir_graph *graph, ir_node *node) {
249         firm_ycomp_dbg_t *dbg = context;
250         char             buf[SEND_BUF_SIZE];
251         int              i;
252         unsigned         src_idx;
253
254         if (get_const_code_irg() == graph || dbg->in_dead_node_elim)
255                 return;
256
257         src_idx       = get_irn_node_nr(node);
258         dbg->has_data = 1;
259
260         if (is_Block(node)) {
261                 /* add block (subgraph) */
262                 ir_snprintf(buf, sizeof(buf), "addSubgraphNode \"%d\" \"%d\" \"%u\" \"%+F\"\n",
263                         0,                     /* parent id */
264                         get_irn_node_nr(node), /* graph id */
265                         NODE_REALIZER_BLOCK,   /* realizer id */
266                         node);                 /* label */
267         }
268         else {
269                 /* add node */
270                 ir_snprintf(buf, sizeof(buf), "addNode \"%d\" \"%u\" \"%u\" \"%+F\"\n",
271                         get_irn_node_nr(get_nodes_block(node)), /* parent id */
272                         src_idx,                                /* node id */
273                         get_node_realizer(node),                /* realizer id */
274                         node);                                  /* label */
275         }
276         send_cmd(dbg, buf);
277
278         /* add edges */
279         for (i = get_irn_arity(node) - 1; i >= 0; --i) {
280                 ir_node      *pred   = get_irn_n(node, i);
281                 unsigned     tgt_idx = get_irn_node_nr(pred);
282                 ycomp_edge_t key, *entry;
283
284                 ir_snprintf(buf, sizeof(buf), "addEdge \"n%un%up%d\" \"%u\" \"%u\" \"%u\" \"%d\"\n",
285                         src_idx, tgt_idx, i,            /* edge id */
286                         src_idx,                        /* source node id */
287                         tgt_idx,                        /* target node id */
288                         get_edge_realizer(node, pred),  /* realizer id */
289                         i);                             /* title */
290                 send_cmd(dbg, buf);
291
292                 /* insert edge */
293                 key.src = node;
294                 key.tgt = pred;
295                 key.pos = i;
296                 entry   = pset_find(dbg->edges, &key, HASH_EDGE(&key));
297                 if (! entry) {
298                         entry = obstack_alloc(&dbg->obst, sizeof(*entry));
299                         entry->src = node;
300                         entry->tgt = pred;
301                         entry->pos = i;
302                         pset_insert(dbg->edges, entry, HASH_EDGE(entry));
303                 }
304         }
305
306         show_and_sync(dbg);
307 }
308
309 /**
310  * Clear the old irg if it has some data and create a new one.
311  */
312 static void firm_ycomp_debug_new_irg(void *context, ir_graph *irg,
313                                      ir_entity *ent)
314 {
315         firm_ycomp_dbg_t *dbg = context;
316         char             buf[SEND_BUF_SIZE];
317         (void) irg;
318
319         if (yy_dbg.has_data) {
320                 send_cmd(dbg, "deleteGraph\n");
321 //              send_cmd(dbg, "show\n");
322         }
323         dbg->has_data = 0;
324
325         ir_snprintf(buf, sizeof(buf), "addSubgraphNode \"-1\" \"0\" \"%u\" \"%s\"\n",
326                 NODE_REALIZER_IRG, get_entity_name(ent));
327         send_cmd(dbg, buf);
328         send_cmd(dbg, "sync\n");
329         wait_for_sync(dbg);
330 }
331
332 /**
333  * Hook: Handle set_irn_n calls.
334  * - set new Block      OR
335  * - remove old edge and add new one
336  */
337 static void firm_ycomp_debug_set_edge(void *context, ir_node *src, int pos, ir_node *tgt, ir_node *old_tgt) {
338         firm_ycomp_dbg_t           *dbg   = context;
339         exchange_node_outs_assoc_t *entry = NULL;
340         exchange_node_outs_assoc_t key;
341         ycomp_edge_t               *old_edge, *new_edge, edge_key;
342         char                       buf[SEND_BUF_SIZE];
343         unsigned                   src_idx, tgt_idx;
344
345         if (dbg->in_dead_node_elim)
346                 return;
347
348         src_idx = get_irn_node_nr(src);
349         tgt_idx = get_irn_node_nr(tgt);
350
351         /* set_irn_n with pos -1 means: node moves to new block  */
352         if (pos < 0) {
353                 if (tgt != old_tgt) {
354                         snprintf(buf, sizeof(buf), "moveNode \"%d\" \"%d\"\n", src_idx, tgt_idx);
355                         send_cmd(dbg, buf);
356                         show_and_sync(dbg);
357                 }
358                 return;
359         }
360
361         /* check if the new edge exists */
362         edge_key.src = src;
363         edge_key.tgt = tgt;
364         edge_key.pos = pos;
365         new_edge     = pset_find(dbg->edges, &edge_key, HASH_EDGE(&edge_key));
366
367         /* if the new edge already exists and the old target is the new target -> ignore */
368         if (new_edge && tgt == old_tgt)
369                 return;
370
371         /* check if the old edge exists */
372         if (old_tgt) {
373                 int old_tgt_idx = get_irn_node_nr(old_tgt);
374
375                 edge_key.src = src;
376                 edge_key.tgt = old_tgt;
377                 edge_key.pos = pos;
378                 old_edge     = pset_find(dbg->edges, &edge_key, HASH_EDGE(&edge_key));
379
380                 /* check if old target is marked for exchange */
381                 key.irn = old_tgt;
382                 entry   = pset_find(dbg->exchanged_nodes, &key, HASH_PTR(old_tgt));
383
384                 if (entry) {
385                         /* we are called from exchange() */
386                         entry->n_out_edges--;
387                 }
388
389                 /* delete the old edge if it exists */
390                 if (old_edge) {
391                         snprintf(buf, sizeof(buf), "deleteEdge \"n%un%up%d\"\n", src_idx, old_tgt_idx, pos);
392                         send_cmd(dbg, buf);
393                         pset_remove(dbg->edges, old_edge, HASH_EDGE(old_edge));
394                 }
395         }
396
397         if (! new_edge) {
398                 /* add the new edge if it doesn't exist */
399                 snprintf(buf, sizeof(buf), "addEdge \"n%un%up%d\" \"%u\" \"%u\" \"%u\" \"%d\"\n",
400                         src_idx, tgt_idx, pos,          /* edge id */
401                         src_idx,                        /* source node id */
402                         tgt_idx,                        /* target node id */
403                         get_edge_realizer(src, tgt),    /* realizer id */
404                         pos);                           /* title */
405                 send_cmd(dbg, buf);
406
407                 /* insert the new edge */
408                 new_edge      = obstack_alloc(&dbg->obst, sizeof(*new_edge));
409                 new_edge->src = src;
410                 new_edge->tgt = tgt;
411                 new_edge->pos = pos;
412                 pset_insert(dbg->edges, new_edge, HASH_EDGE(new_edge));
413         }
414
415         /* show and sync if all edges are rerouted or if it's a normal set_irn_n */
416         if (! entry || entry->n_out_edges == 0) {
417                 show_and_sync(dbg);
418         }
419 }
420
421 /**
422  * Hook: Put nodes, about to be exchanged into a set.
423  */
424 static void firm_ycomp_debug_exchange(void *context, ir_node *old_node, ir_node *new_node) {
425         firm_ycomp_dbg_t           *dbg = context;
426         exchange_node_outs_assoc_t key, *entry;
427
428         /* put nodes, which are about to be exchanged into a set */
429
430         key.irn = old_node;
431         entry   = pset_find(dbg->exchanged_nodes, &key, HASH_PTR(old_node));
432         if (entry) {
433                 entry->n_out_edges = get_irn_n_edges(old_node);
434                 entry->nw          = new_node;
435         }
436         else {
437                 entry              = obstack_alloc(&dbg->obst, sizeof(*entry));
438                 entry->irn         = old_node;
439                 entry->nw          = new_node;
440                 entry->n_out_edges = get_irn_n_edges(old_node);
441                 pset_insert(dbg->exchanged_nodes, entry, HASH_PTR(old_node));
442         }
443 }
444
445 /**
446  * Hook: Remove all old in edges, turn node into id node, add new input edge.
447  */
448 static void firm_ycomp_debug_turn_into_id(void *context, ir_node *old_node) {
449         firm_ycomp_dbg_t           *dbg = context;
450         exchange_node_outs_assoc_t key, *entry;
451         int                        i;
452         char                       buf[SEND_BUF_SIZE];
453         unsigned                   src_idx, tgt_idx;
454         ycomp_edge_t               edge_key, *new_edge;
455
456         key.irn = old_node;
457         entry   = pset_find(dbg->exchanged_nodes, &key, HASH_PTR(old_node));
458
459         assert(entry != NULL && "Exchange entry missing");
460
461         src_idx = get_irn_node_nr(old_node);
462         tgt_idx = get_irn_node_nr(entry->nw);
463
464         /* remove all old edges */
465         for (i = get_irn_arity(old_node) - 1; i >= 0; --i) {
466                 ycomp_edge_t *old_edge;
467                 unsigned     old_tgt_idx;
468
469                 /* check if the old edge exists */
470                 edge_key.src = old_node;
471                 edge_key.tgt = get_irn_n(old_node, i);
472                 edge_key.pos = i;
473                 old_edge     = pset_find(dbg->edges, &edge_key, HASH_EDGE(&edge_key));
474
475                 old_tgt_idx  = get_irn_node_nr(edge_key.tgt);
476
477                 /* remove the old edge */
478                 if (old_edge) {
479                         snprintf(buf, sizeof(buf), "deleteEdge \"n%un%up%d\"\n", src_idx, old_tgt_idx, i);
480                         send_cmd(dbg, buf);
481                         pset_remove(dbg->edges, old_edge, HASH_EDGE(old_edge));
482                 }
483         }
484
485         /* change the old node into an id node */
486         snprintf(buf, sizeof(buf), "changeNode \"%ld\" \"%u\"\n", get_irn_node_nr(old_node), NODE_REALIZER_ID);
487         send_cmd(dbg, buf);
488
489         /* add new Id input */
490         snprintf(buf, sizeof(buf), "addEdge \"n%un%up%d\" \"%u\" \"%u\" \"%u\" \"%d\"\n",
491                 src_idx, tgt_idx, 0,                       /* edge id */
492                 src_idx,                                   /* source node id */
493                 tgt_idx,                                   /* target node id */
494                 get_edge_realizer(old_node, entry->nw),    /* realizer id */
495                 0);                                        /* title */
496         send_cmd(dbg, buf);
497
498         /* go */
499         show_and_sync(dbg);
500
501         /* add the new edge to our pset */
502         new_edge = obstack_alloc(&dbg->obst, sizeof(*new_edge));
503         new_edge->src = old_node;
504         new_edge->tgt = entry->nw;
505         new_edge->pos = 0;
506         pset_insert(dbg->edges, new_edge, HASH_EDGE(new_edge));
507 }
508
509 /**
510  * Hook: Just mark start/end of dead node elimination.
511  */
512 static void firm_ycomp_debug_dead_node_elim(void *context, ir_graph *irg, int start) {
513         firm_ycomp_dbg_t *dbg  = context;
514         (void) irg;
515         dbg->in_dead_node_elim = start != 0;
516 }
517
518 /**
519  * Establish connection to yComp and register all hooks.
520  */
521 void firm_init_ycomp_debugger(const char *host, unsigned port) {
522         static int init_once = 0;
523
524         if (init_once)
525                 return;
526
527         memset(&yy_dbg, 0, sizeof(yy_dbg));
528         yy_dbg.fd = -1;
529
530         fprintf(stderr, "connecting to %s:%u\n", host, port);
531         yy_dbg.fd = firmnet_connect_tcp(host, port);
532
533         if (yy_dbg.fd > -1) {
534                 /* We could establish a connection to ycomp -> register hooks */
535                 firm_ycomp_debug_init_realizer(&yy_dbg);
536                 yy_dbg.exchanged_nodes = new_pset(cmp_nodes, 20);
537                 yy_dbg.edges           = new_pset(cmp_edges, 20);
538                 obstack_init(&yy_dbg.obst);
539
540 #define REGISTER_HOOK(ycomp_hook, firm_hook, func)     \
541         do {                                               \
542                 yy_dbg.ycomp_hook.context           = &yy_dbg; \
543                 yy_dbg.ycomp_hook.hook._##firm_hook = func;    \
544                 register_hook(firm_hook, &yy_dbg.ycomp_hook);  \
545         } while(0)
546
547                 REGISTER_HOOK(hook_new_irn,   hook_new_node,       firm_ycomp_debug_new_node);
548                 REGISTER_HOOK(hook_new_irg,   hook_new_graph,      firm_ycomp_debug_new_irg);
549                 REGISTER_HOOK(hook_set_edge,  hook_set_irn_n,      firm_ycomp_debug_set_edge);
550                 REGISTER_HOOK(hook_exchange,  hook_replace,        firm_ycomp_debug_exchange);
551                 REGISTER_HOOK(hook_into_id,   hook_turn_into_id,   firm_ycomp_debug_turn_into_id);
552                 REGISTER_HOOK(hook_dead_node, hook_dead_node_elim, firm_ycomp_debug_dead_node_elim);
553
554 #undef REGISTER_HOOK
555         }
556
557         init_once = 1;
558 }
559
560 /**
561  * Close connection to yComp, unregister all hooks and free memory.
562  */
563 void firm_finish_ycomp_debugger(void) {
564         if (yy_dbg.fd > -1) {
565                 /* close connection */
566                 firmnet_close_socket(yy_dbg.fd);
567                 yy_dbg.fd = -1;
568
569                 /* unregister all hooks */
570                 unregister_hook(hook_new_graph,      &yy_dbg.hook_new_irg);
571                 unregister_hook(hook_new_node,       &yy_dbg.hook_new_irn);
572                 unregister_hook(hook_set_irn_n,      &yy_dbg.hook_set_edge);
573                 unregister_hook(hook_replace,        &yy_dbg.hook_exchange);
574                 unregister_hook(hook_dead_node_elim, &yy_dbg.hook_dead_node);
575                 unregister_hook(hook_turn_into_id,   &yy_dbg.hook_into_id);
576
577                 /* clear sets */
578                 del_pset(yy_dbg.exchanged_nodes);
579                 del_pset(yy_dbg.edges);
580
581                 /* free data obstack */
582                 obstack_free(&yy_dbg.obst, NULL);
583         }
584 }