2010-03-01 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / genmdesc.c
1 /*
2  * genmdesc: Generates the machine description
3  *
4  * Authors:
5  *   Paolo Molaro (lupus@ximian.com)
6  *
7  * (C) 2003 Ximian, Inc.
8  */
9 #include "mini.h"
10 #include <ctype.h>
11 #include <string.h>
12 #include <mono/metadata/opcodes.h>
13
14 typedef struct {
15         int num;
16         const char *name;
17         char *desc;
18         char *comment;
19         char spec [MONO_INST_MAX];
20 } OpDesc;
21
22 static GHashTable *table;
23 static GHashTable *template_table;
24
25 #define eat_whitespace(s) while (*(s) && isspace (*(s))) s++;
26
27 static int
28 load_file (const char *name) {
29         FILE *f;
30         char buf [256];
31         char *str, *p;
32         int line;
33         OpDesc *desc;
34         GString *comment;
35
36         if (!(f = fopen (name, "r")))
37                 g_error ("Cannot open file '%s'", name);
38
39         comment = g_string_new ("");
40         /*
41          * The format of the lines are:
42          * # comment
43          * opcode: [dest:format] [src1:format] [src2:format] [flags:format] [clob:format] 
44          *      [cost:num] [res:format] [delay:num] [len:num]
45          * format is a single letter that depends on the field
46          * NOTE: no space between the field name and the ':'
47          *
48          * len: maximum instruction length
49          */
50         line = 0;
51         while ((str = fgets (buf, sizeof (buf), f))) {
52                 gboolean is_template = FALSE;
53                 ++line;
54                 eat_whitespace (str);
55                 if (!str [0])
56                         continue;
57                 if (str [0] == '#') {
58                         g_string_append (comment, str);
59                         continue;
60                 }
61                 p = strchr (str, ':');
62                 if (!p)
63                         g_error ("Invalid format at line %d in %s\n", line, name);
64                 *p++ = 0;
65                 eat_whitespace (p);
66                 if (strcmp (str, "template") == 0) {
67                         is_template = TRUE;
68                         desc = g_new0 (OpDesc, 1);
69                 } else {
70                         desc = g_hash_table_lookup (table, str);
71                         if (!desc)
72                                 g_error ("Invalid opcode '%s' at line %d in %s\n", str, line, name);
73                         if (desc->desc)
74                                 g_error ("Duplicated opcode %s at line %d in %s\n", str, line, name);
75                 }
76                 desc->desc = g_strdup (p);
77                 desc->comment = g_strdup (comment->str);
78                 g_string_truncate (comment, 0);
79                 while (*p) {
80                         if (strncmp (p, "dest:", 5) == 0) {
81                                 desc->spec [MONO_INST_DEST] = p [5];
82                                 p += 6;
83                         } else if (strncmp (p, "src1:", 5) == 0) {
84                                 desc->spec [MONO_INST_SRC1] = p [5];
85                                 p += 6;
86                         } else if (strncmp (p, "src2:", 5) == 0) {
87                                 desc->spec [MONO_INST_SRC2] = p [5];
88                                 p += 6;
89                         } else if (strncmp (p, "src3:", 5) == 0) {
90                                 desc->spec [MONO_INST_SRC3] = p [5];
91                                 p += 6;
92                         } else if (strncmp (p, "clob:", 5) == 0) {
93                                 desc->spec [MONO_INST_CLOB] = p [5];
94                                 p += 6;
95                                 /* Currently unused fields
96                         } else if (strncmp (p, "cost:", 5) == 0) {
97                                 desc->spec [MONO_INST_COST] = p [5];
98                                 p += 6;
99                         } else if (strncmp (p, "res:", 4) == 0) {
100                                 desc->spec [MONO_INST_RES] = p [4];
101                                 p += 5;
102                         } else if (strncmp (p, "flags:", 6) == 0) {
103                                 desc->spec [MONO_INST_FLAGS] = p [6];
104                                 p += 7;
105                         } else if (strncmp (p, "delay:", 6) == 0) {
106                                 desc->spec [MONO_INST_DELAY] = p [6];
107                                 p += 7;
108                                 */
109                         } else if (strncmp (p, "len:", 4) == 0) {
110                                 p += 4;
111                                 desc->spec [MONO_INST_LEN] = strtoul (p, &p, 10);
112                         } else if (strncmp (p, "template:", 9) == 0) {
113                                 char *tname;
114                                 int i;
115                                 OpDesc *tdesc;
116                                 p += 9;
117                                 tname = p;
118                                 while (*p && isalnum (*p)) ++p;
119                                 *p++ = 0;
120                                 tdesc = g_hash_table_lookup (template_table, tname);
121                                 if (!tdesc)
122                                         g_error ("Invalid template name %s at '%s' at line %d in %s\n", tname, p, line, name);
123                                 for (i = 0; i < MONO_INST_MAX; ++i) {
124                                         if (desc->spec [i])
125                                                 g_error ("The template overrides any previous value set at line %d in %s\n", line, name);
126                                 }
127                                 memcpy (desc->spec, tdesc->spec, sizeof (desc->spec));
128                         } else if (strncmp (p, "name:", 5) == 0) {
129                                 char *tname;
130                                 if (!is_template)
131                                         g_error ("name tag only valid in templates at '%s' at line %d in %s\n", p, line, name);
132                                 if (desc->name)
133                                         g_error ("Duplicated name tag in template %s at '%s' at line %d in %s\n", desc->name, p, line, name);
134                                 p += 5;
135                                 tname = p;
136                                 while (*p && isalnum (*p)) ++p;
137                                 *p++ = 0;
138                                 if (g_hash_table_lookup (template_table, tname))
139                                         g_error ("Duplicated template %s at line %d in %s\n", tname, line, name);
140                                 desc->name = g_strdup (tname);
141                                 g_hash_table_insert (template_table, (void*)desc->name, desc);
142                         } else {
143                                 g_error ("Parse error at '%s' at line %d in %s\n", p, line, name);
144                         }
145                         eat_whitespace (p);
146                 }
147                 if (is_template && !desc->name)
148                         g_error ("Template without name at line %d in %s\n", line, name);
149         }
150         fclose (f);
151         return 0;
152 }
153
154 static OpDesc *opcodes = NULL;
155
156 static void
157 init_table (void) {
158         int i;
159         OpDesc *desc;
160
161         template_table = g_hash_table_new (g_str_hash, g_str_equal);
162         table = g_hash_table_new (g_str_hash, g_str_equal);
163
164         opcodes = g_new0 (OpDesc, OP_LAST);
165         for (i = OP_LOAD; i < OP_LAST; ++i) {
166                 desc = opcodes + i;
167                 desc->num = i;
168                 desc->name = mono_inst_name (i);
169                 g_hash_table_insert (table, (char *)desc->name, desc);
170         }
171 }
172
173 static void
174 output_char (FILE *f, char c) {
175         if (isalnum (c))
176                 fprintf (f, "%c", c);
177         else
178                 fprintf (f, "\\x%x\" \"", c);
179 }
180
181 static void
182 build_table (const char *fname, const char *name) {
183         FILE *f;
184         int i, j, idx;
185         OpDesc *desc;
186         GString *idx_array =  g_string_new ("");
187         /* use this to remove duplicates */
188         GHashTable *desc_ht = g_hash_table_new (g_str_hash, g_str_equal);
189
190         if (!(f = fopen (fname, "w")))
191                 g_error ("Cannot open file '%s'", fname);
192         fprintf (f, "/* File automatically generated by genmdesc, don't change */\n\n");
193         fprintf (f, "const char %s [] = {\n", name);
194         fprintf (f, "\t\"");
195         for (j = 0; j < MONO_INST_MAX; ++j)
196                 fprintf (f, "\\x0");
197         fprintf (f, "\"\t/* null entry */\n");
198         idx = 1;
199         g_string_append_printf (idx_array, "const guint16 %s_idx [] = {\n", name);
200
201         for (i = OP_LOAD; i < OP_LAST; ++i) {
202                 desc = opcodes + i;
203                 if (!desc->desc)
204                         g_string_append_printf (idx_array, "\t0,\t/* %s */\n", desc->name ? desc->name : "");
205                 else {
206                         fprintf (f, "\t\"");
207                         for (j = 0; j < MONO_INST_MAX; ++j)
208                                 output_char (f, desc->spec [j]);
209                         fprintf (f, "\"\t/* %s */\n", desc->name);
210                         g_string_append_printf (idx_array, "\t%d,\t/* %s */\n", idx * MONO_INST_MAX, desc->name);
211                         ++idx;
212                 }
213         }
214         fprintf (f, "};\n\n");
215         fprintf (f, "%s};\n\n", idx_array->str);
216         fclose (f);
217         g_string_free (idx_array, TRUE);
218         g_hash_table_destroy (desc_ht);
219 }
220
221 static void
222 dump (void) {
223         int i;
224         OpDesc *desc;
225         
226         for (i = 0; i < MONO_CEE_LAST; ++i) {
227                 desc = opcodes + i;
228                 if (desc->comment)
229                         g_print ("%s", desc->comment);
230                 if (!desc->desc)
231                         g_print ("%s:\n", desc->name);
232                 else {
233                         g_print ("%s: %s", desc->name, desc->desc);
234                         if (!strchr (desc->desc, '\n'))
235                                 g_print ("\n");
236                 }
237         }
238         for (i = OP_LOAD; i < OP_LAST; ++i) {
239                 desc = opcodes + i;
240                 if (!desc->desc)
241                         g_print ("%s:\n", desc->name);
242                 else {
243                         g_print ("%s: %s", desc->name, desc->desc);
244                         if (!strchr (desc->desc, '\n'))
245                                 g_print ("\n");
246                 }
247         }
248 }
249
250 /*
251  * TODO: output the table (possibly merged), in the input format 
252  */
253 int 
254 main (int argc, char* argv [])
255 {
256         init_table ();
257         if (argc == 2) {
258                 /* useful to get a new file when some opcodes are added: looses the comments, though */
259                 load_file (argv [1]);
260                 dump ();
261         } else if (argc < 4) {
262                 g_print ("Usage: genmdesc arguments\n");
263                 g_print ("\tgenmdesc desc                        Output to stdout the description file.\n");
264                 g_print ("\tgenmdesc output name desc [desc1...] Write to output the description in a table named 'name'.\n");
265                 return 1;
266         } else {
267                 int i;
268                 for (i = 3; i < argc; ++i)
269                         load_file (argv [i]);
270                 build_table (argv [1], argv [2]);
271         }
272         return 0;
273 }
274