Added standalone grgen dumper for firm graphs
[libfirm] / ir / ir / irdump_grgen.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 * @file
22 * @brief   Write ir graph as a grgen construction rule
23 * @author  Andreas Schoesser
24 * @version $Id:$
25 */
26
27 #define MAX_NODENAME_LEN 100
28
29 #include <assert.h>
30 #include <stdio.h>
31 #include <obst.h>
32
33
34 #include "irgraph.h"
35 #include "firm_types.h"
36 #include "pmap.h"
37 #include "tv.h"
38 #include "irgwalk.h"
39 #include "firm_types.h"
40
41
42 typedef struct
43 {
44         ir_graph *irg;
45         struct pmap *mode_edge_map;
46         struct pmap *edge_name_map;
47         struct pmap *node_name_map;  // Contains the mapping firm node -> node name
48         struct obstack node_names;   // Contains the node name data
49         struct pmap *mode_name_map;  // Contains the mapping firm mode -> mode node name
50         struct obstack mode_names;   // Contains the "mode node name" data
51         struct pmap *nodes_to_dump;  // Contains firm nodes, that have to be dumped
52 } grgen_dumpinfo_t;
53
54 typedef struct                  // Holds information needed througout the usage of a grgen dumper instance
55 {
56         FILE *output_file;      // The file the grgen rules will be dumped to
57 } irg_grgen_dumper_env_t;
58
59 static void dump_grg_node(ir_node *n, grgen_dumpinfo_t *dump_info, FILE *fp);
60 static void dump_grg_egde(ir_node *n, int n_edge, grgen_dumpinfo_t *dump_info, FILE *fp);
61 static void dump_grgen_mode(ir_node *n, grgen_dumpinfo_t *dump_info, FILE *fp, ir_mode *alt_mode);
62 static char *dump_grgen_mode_node(ir_mode *irn_mode, grgen_dumpinfo_t *dump_info, FILE *fp);
63 static void dump_grgen_eval(ir_node *n, grgen_dumpinfo_t *dump_info, FILE *fp);
64 static int dump_pattern(grgen_dumpinfo_t *dump_info, FILE *fp);
65 static int  get_indent(void);
66 static void set_indent(int i);
67
68
69
70 /*****************************************************************************
71 * Program:              grgen_dumper.c
72 * Function:             Dumps parts of a firm graph (those which have to be extracted
73 *                               as search and replace patterns) as a grGen rule.
74 * Depends on:   Needs the analysis info generated by the pattern creator
75 * Author:               Andreas Schoesser
76 * Date:         2006-12-07
77 *****************************************************************************/
78
79
80 // ---------------------------- INCLUDES --------------------------------
81
82
83
84
85 /* #include "grgen_dumper__t.h"
86 #include "create_pattern_t.h"
87 #include "firm_node_ext.h" */
88
89 // ----------------------------- GLOABALS --------------------------------
90
91 // Saves the current indent value and keeps spaces in a string
92 #define MAX_INDENT 100
93 static char indent[MAX_INDENT] = "";
94 static int  current_indent = 0;
95
96 // Saves the current node number to generate node names
97 static int node_counter;
98 static int edge_counter;
99
100
101
102
103 /************************************************************************
104 * Initializes the grgen_dumper module and dumps the GrGen File header
105 * Returns:              An environment to be passed to all functions of the GrGen
106 *                               dumper.
107 * Parameters:   file:   filename of the file to dump to
108 *                               append: 1 if the previous file content should be
109 *                                               maintained.
110 ************************************************************************/
111
112 irg_grgen_dumper_env_t *init_irg_grgen_dumper(char *file, int append)
113 {
114         irg_grgen_dumper_env_t *grgen_dumper_env = malloc(sizeof(irg_grgen_dumper_env_t));
115         FILE *fp;
116
117         if(append)
118                 fp = fopen(file, "at");
119         else
120         {
121                 fp = fopen(file, "wt");
122
123                 // *** Dump header
124                 fprintf(fp, "%susing Firm;\n\n", indent);
125         }
126
127         grgen_dumper_env -> output_file = fp;
128         return(grgen_dumper_env);
129 }
130
131
132
133 /************************************************************************
134 * Frees information used by the grgen_dumper and closes the output file
135 ************************************************************************/
136
137 void deinit_irg_grgen_dumper(irg_grgen_dumper_env_t *grgen_dumper_env)
138 {
139         fclose(grgen_dumper_env->output_file);
140         free(grgen_dumper_env);
141 }
142
143 static void collect_nodes(ir_node *n, void * env)
144 {
145         pmap *nodes_to_dump = (pmap *) env;
146
147         pmap_insert(nodes_to_dump, n, NULL);
148 }
149
150
151
152
153 /************************************************************************
154  * Starts dumping
155  ************************************************************************/
156
157 void dump_irg_grgen(ir_graph *irg, char *filename, int append)
158 {
159         FILE *fp;
160         grgen_dumpinfo_t dump_info;
161         int uses_memory = 0;
162
163
164         irg_grgen_dumper_env_t *grgen_dumper_env = init_irg_grgen_dumper(filename, append);
165         fp = grgen_dumper_env -> output_file;
166
167         // Do initialization
168         dump_info.irg = irg;                    // Basically copy the graph_ana_struct, ugly
169         dump_info.mode_edge_map = pmap_create();                // Create some additional pmaps to hold
170         dump_info.edge_name_map = pmap_create();                // node and edge name information etc.
171         dump_info.node_name_map = pmap_create();
172         dump_info.mode_name_map = pmap_create();
173         dump_info.nodes_to_dump = pmap_create();
174
175         // Node and egde count start at 0 for each pattern to be dumped.
176         node_counter = 0;
177         edge_counter = 0;
178         obstack_init(&(dump_info.node_names));
179         obstack_init(&(dump_info.mode_names));
180
181         // Dump rule header
182         set_indent(0);
183         fprintf(fp, "\n\n%srule %s\n%s{\n", indent, get_entity_name(get_irg_entity(irg)), indent);
184         set_indent(2);
185
186         fprintf(fp, "%spattern { }\n", indent);                  // Empty pattern
187         fprintf(fp, "%sreplace\n%s{\n", indent, indent); // Graph is contrcuted in the replacement part
188
189         set_indent(4);
190
191         irg_walk_graph(irg, collect_nodes, NULL, dump_info.nodes_to_dump);
192         uses_memory = dump_pattern(&dump_info, fp);
193
194         // *** Dump footer
195         set_indent(0);
196         fprintf(fp, "%s}\n", indent);
197
198         // Clean up
199         pmap_destroy(dump_info.mode_edge_map);
200         pmap_destroy(dump_info.edge_name_map);
201         pmap_destroy(dump_info.node_name_map);
202         pmap_destroy(dump_info.mode_name_map);
203         obstack_free(&(dump_info.node_names), NULL);
204         obstack_finish(&(dump_info.node_names));
205         obstack_free(&(dump_info.mode_names), NULL);
206         obstack_finish(&(dump_info.mode_names));
207
208         deinit_irg_grgen_dumper(grgen_dumper_env);
209 }
210
211
212 /************************************************************************
213  * Dumps the left hand side of the rule
214  ************************************************************************/
215
216 static int dump_pattern(grgen_dumpinfo_t *dump_info, FILE *fp)
217 {
218         struct pmap *nodes_to_dump = dump_info->nodes_to_dump;
219         pmap_entry *entry;
220         int uses_memory = 0;
221
222         // Dump all nodes
223         pmap_foreach(nodes_to_dump, entry)
224         {
225                 ir_node *n = (ir_node *) entry->key;
226
227                 // Dump node
228                 if(get_irn_opcode(n) == iro_Proj && get_irn_modecode(n) == irm_M)
229                         uses_memory = 1;
230                 dump_grg_node(n, dump_info, fp);
231                 dump_grgen_mode(n, dump_info, fp, NULL);
232         }
233
234         // Dump all edges
235         pmap_foreach(nodes_to_dump, entry)
236         {
237                 ir_node *n = (ir_node *) entry->key;
238                 int i;
239
240                 // Dump edges
241                 for(i = -1; i < get_irn_arity(n); i++)
242                         dump_grg_egde(n, i, dump_info, fp);
243         }
244
245         fprintf(fp, "%seval {\n", indent);
246         set_indent(6);
247         pmap_foreach(nodes_to_dump, entry)
248         {
249                 ir_node *n = (ir_node *) entry->key;
250                 dump_grgen_eval(n, dump_info, fp);
251         }
252         set_indent(4);
253         fprintf(fp, "%s}\n", indent);
254
255
256         set_indent(2);
257         fprintf(fp, "%s} /* Replacement */\n", indent);
258         return(uses_memory);
259 }
260
261
262
263 /************************************************************************
264 * Dumps a node in GrGen Format
265 ************************************************************************/
266
267 static void dump_grg_node(ir_node *n, grgen_dumpinfo_t *dump_info, FILE *fp)
268 {
269         char *node_name;
270
271         // Already dumped the node? Then do nothing
272         if(pmap_contains(dump_info -> node_name_map, n))
273                 return;
274
275         // Else generate new node name and dump the node
276
277         node_name = obstack_alloc(&(dump_info -> node_names), MAX_NODENAME_LEN);
278
279         sprintf(node_name, "%s%d", get_op_name(get_irn_op(n)), get_irn_node_nr(n));
280         fprintf(fp, "%s%s : %s;\n", indent, node_name, get_op_name(get_irn_op(n)));
281
282         pmap_insert(dump_info -> node_name_map, n, node_name);
283         node_counter++;
284 }
285
286
287
288 /************************************************************************
289 * Dumps an edge in GrGen format
290 ************************************************************************/
291
292 static void dump_grg_egde(ir_node *n, int n_edge, grgen_dumpinfo_t *dump_info, FILE *fp)
293 {
294         ir_node *to_node;
295         char *from_node_name, *to_node_name;
296         char **nodes_edge_names;
297
298
299         // Check if to_node has also to be dumped. If not, skip this edge
300         // We have to dump to_node here, because to_node has to be known by grgen before
301         // connecting an edge to it.
302         to_node =  get_irn_n(n, n_edge);
303         if(!pmap_contains(dump_info -> nodes_to_dump, to_node))
304                 return;
305
306         if((nodes_edge_names = pmap_get(dump_info -> edge_name_map, n)) == NULL)
307         {
308                 nodes_edge_names = (char **) obstack_alloc(&(dump_info->node_names), (get_irn_arity(n) + 1) * sizeof(char *));
309                 memset(nodes_edge_names, 0, (get_irn_arity(n) + 1) * sizeof(char *));
310                 pmap_insert(dump_info->edge_name_map, n, nodes_edge_names);
311         }
312
313         assert(pmap_contains(dump_info -> node_name_map, n));
314         assert(pmap_contains(dump_info -> node_name_map, to_node));
315         from_node_name = (char *) pmap_get(dump_info -> node_name_map, n);
316         to_node_name = (char *) pmap_get(dump_info -> node_name_map, to_node);
317
318         {
319
320         char edge_name[50], *edge_name_obst;
321
322         sprintf(edge_name, "pos%d_%d", n_edge + 1, edge_counter++);
323         edge_name_obst = obstack_alloc(&(dump_info->node_names), strlen(edge_name) + 1);
324         strcpy(edge_name_obst, edge_name);
325         nodes_edge_names[n_edge + 1] = edge_name_obst;
326
327         fprintf(fp, "%s%s -%s:df-> %s;\n", indent, from_node_name, edge_name_obst, to_node_name);
328         }
329
330
331 }
332
333
334
335 /************************************************************************
336 * Dumps an FIRM Mode as GrGen Code
337 * If source_node_name == NULL, that name of n that was already
338 * generated is used.
339 * If source_node_name != NULL, this given source will be used
340 * (useful for retyped nodes)
341 ************************************************************************/
342
343 static void dump_grgen_mode(ir_node *n, grgen_dumpinfo_t *dump_info, FILE *fp, ir_mode *alt_mode)
344 {
345         char *node_name = (char *) pmap_get(dump_info -> node_name_map, n);
346         ir_mode *irn_mode = (alt_mode != NULL) ? alt_mode : get_irn_mode(n);
347         char edge_name[50];
348         char *mode_node_name;
349
350         mode_node_name = dump_grgen_mode_node(irn_mode, dump_info, fp);
351
352         //mode_code =  get_mode_modecode(irn_mode);
353         //mode_name =  get_mode_name(irn_mode);
354
355         // Yes, use the given mode-node
356         //mode_node_name = pmap_get(dump_info -> mode_name_map, (void *) mode_code);
357         sprintf(edge_name, "m%d", edge_counter++);
358
359         if(pmap_get(dump_info->mode_edge_map, n) == NULL)
360         {
361                 char *edge_name_obst = obstack_alloc(&(dump_info->node_names), strlen(edge_name) + 1);
362                 strcpy(edge_name_obst, edge_name);
363                 pmap_insert(dump_info->mode_edge_map, n, edge_name_obst);
364         }
365
366         // Dump the edge from the current node to it's mode node
367         fprintf(fp, "%s%s -%s:has_mode-> %s;\n", indent, node_name, edge_name, mode_node_name);
368 }
369
370
371
372 /************************************************************************
373 * Dumps a node representing a node
374 ************************************************************************/
375
376 static char *dump_grgen_mode_node(ir_mode *irn_mode, grgen_dumpinfo_t *dump_info, FILE *fp)
377 {
378         modecode mode_code = get_mode_modecode(irn_mode);
379         const char *mode_name =  get_mode_name(irn_mode);
380         char *mode_node_name;
381
382         if(!pmap_contains(dump_info -> mode_name_map, (void *) mode_code))
383         {
384                 // No, create a new mode-node
385                 mode_node_name = obstack_alloc(&(dump_info -> mode_names), MAX_NODENAME_LEN);
386                 sprintf(mode_node_name, "mode_%s_node", mode_name);
387                 pmap_insert(dump_info -> mode_name_map, (void *) mode_code, mode_node_name);
388                 fprintf(fp, "%s%s : Mode_%s;\n", indent, mode_node_name, mode_name);
389                 return(mode_node_name);
390         }
391         else
392         {
393                 return((char *) pmap_get(dump_info -> mode_name_map, (void *) mode_code));
394         }
395
396 }
397
398
399
400 /************************************************************************
401 * Dumps the condition for the given node, depending on the node's
402 * attributes and the node's opcode
403 ************************************************************************/
404
405 static void dump_grgen_eval(ir_node *n, grgen_dumpinfo_t *dump_info, FILE *fp)
406 {
407         char *node_name;
408         ir_opcode code = get_irn_opcode(n);
409
410         if(code == iro_Const)
411         {
412                 node_name = pmap_get(dump_info->node_name_map, n);
413                 fprintf(fp, "%s%s.value = \"%d\";\n", indent, node_name, get_tarval_long(get_Const_tarval(n)));
414         }
415
416
417         if(code == iro_Proj)
418         {
419                 node_name = pmap_get(dump_info->node_name_map, n);
420                 fprintf(fp, "%s%s.proj = %d;\n", indent, node_name, get_Proj_proj(n));
421         }
422
423         /*if(code == iro_Block)
424         {
425                 node_name = pmap_get(dump_info->node_name_map, n);
426                 fprintf(fp, "%s%s.pos = %d;\n", indent, node_name, ??);
427         }
428
429         if(code == iro_Phi)
430         {
431                 node_name = pmap_get(dump_info->node_name_map, n);
432                 fprintf(fp, "%s%s.pos = %d;\n", indent, node_name, ??);
433         }*/
434
435         // TODO: Dump Block evals: edge numbers
436
437
438         if(code == iro_Phi || code == iro_Block)
439         {
440                 char **edge_names;
441                 int i;
442
443                 //assert((get_irn_arity(n) == get_irn_arity(phi_block)) && "Phi has other arity than it's block! Pattern seems to be broken.");
444
445                 // Load the edge names that have been saved
446                 edge_names = pmap_get(dump_info->edge_name_map, n);
447                 assert(edge_names && "Some edge names have not been dumped!");
448
449                 // Correlate the matched phi edges with the matched block edges
450                 // Caution: Position 0 in the edge_names array is the block edge, so start at 1
451                 for(i = 0; i < get_irn_arity(n) + 1; i++)
452                 {
453                         assert(edge_names[i] != NULL && "Some edges have not been dumped!");
454
455                         fprintf(fp, "%s%s.pos = %d;\n", indent, edge_names[i], i);
456                 }
457                 return;
458         }
459 }
460
461
462
463
464
465 /************************************************************************
466 * Sets current indent
467 ************************************************************************/
468
469 static void set_indent(int i)
470 {
471         int j;
472
473         // Generate a string containing i blank characters
474         if(i < MAX_INDENT - 1)
475         {
476                 for(j = 0; j < i; j++)
477                         indent[j] = ' ';
478                 indent[j] = 0x0;
479                 current_indent = i;
480         }
481 }
482
483
484
485 /************************************************************************
486 * Gets current indent value
487 ************************************************************************/
488
489 static int  get_indent(void)
490 {
491         return(current_indent);
492 }
493
494
495 /************************************************************************
496 * Collects all nodes of a ir graph, so that the ir graph can be
497 * dumped completely
498 ************************************************************************/