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