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