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