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