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