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