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