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