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