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