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