]>
Commit | Line | Data |
---|---|---|
960ebfe7 | 1 | /* MD reader for GCC. |
d353bf18 | 2 | Copyright (C) 1987-2015 Free Software Foundation, Inc. |
960ebfe7 | 3 | |
4 | This file is part of GCC. | |
5 | ||
6 | GCC is free software; you can redistribute it and/or modify it under | |
7 | the terms of the GNU General Public License as published by the Free | |
8 | Software Foundation; either version 3, or (at your option) any later | |
9 | version. | |
10 | ||
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with GCC; see the file COPYING3. If not see | |
18 | <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #include "bconfig.h" | |
21 | #include "system.h" | |
22 | #include "coretypes.h" | |
b638f5c8 | 23 | #include "errors.h" |
960ebfe7 | 24 | #include "read-md.h" |
25 | ||
26 | /* Associates PTR (which can be a string, etc.) with the file location | |
27 | specified by FILENAME and LINENO. */ | |
28 | struct ptr_loc { | |
29 | const void *ptr; | |
30 | const char *filename; | |
31 | int lineno; | |
32 | }; | |
33 | ||
77ba95d0 | 34 | /* A singly-linked list of filenames. */ |
35 | struct file_name_list { | |
36 | struct file_name_list *next; | |
37 | const char *fname; | |
38 | }; | |
39 | ||
77c2564f | 40 | /* Obstack used for allocating MD strings. */ |
960ebfe7 | 41 | struct obstack string_obstack; |
42 | ||
43 | /* A table of ptr_locs, hashed on the PTR field. */ | |
44 | static htab_t ptr_locs; | |
45 | ||
46 | /* An obstack for the above. Plain xmalloc is a bit heavyweight for a | |
47 | small structure like ptr_loc. */ | |
48 | static struct obstack ptr_loc_obstack; | |
49 | ||
50 | /* A hash table of triples (A, B, C), where each of A, B and C is a condition | |
51 | and A is equivalent to "B && C". This is used to keep track of the source | |
77c2564f | 52 | of conditions that are made up of separate MD strings (such as the split |
960ebfe7 | 53 | condition of a define_insn_and_split). */ |
54 | static htab_t joined_conditions; | |
55 | ||
56 | /* An obstack for allocating joined_conditions entries. */ | |
57 | static struct obstack joined_conditions_obstack; | |
58 | ||
b3453c30 | 59 | /* The file we are reading. */ |
60 | FILE *read_md_file; | |
960ebfe7 | 61 | |
b3453c30 | 62 | /* The filename of READ_MD_FILE. */ |
63 | const char *read_md_filename; | |
64 | ||
65 | /* The current line number in READ_MD_FILE. */ | |
66 | int read_md_lineno; | |
960ebfe7 | 67 | |
77ba95d0 | 68 | /* The name of the toplevel file that indirectly included READ_MD_FILE. */ |
69 | const char *in_fname; | |
70 | ||
71 | /* The directory part of IN_FNAME. NULL if IN_FNAME is a bare filename. */ | |
72 | static char *base_dir; | |
73 | ||
74 | /* The first directory to search. */ | |
75 | static struct file_name_list *first_dir_md_include; | |
76 | ||
77 | /* A pointer to the null terminator of the md include chain. */ | |
78 | static struct file_name_list **last_dir_md_include_ptr = &first_dir_md_include; | |
79 | ||
80 | /* This callback will be invoked whenever an md include directive is | |
81 | processed. To be used for creation of the dependency file. */ | |
82 | void (*include_callback) (const char *); | |
83 | ||
84 | /* The current maximum length of directory names in the search path | |
85 | for include files. (Altered as we get more of them.) */ | |
86 | static size_t max_include_len; | |
87 | ||
ac0640e5 | 88 | /* A table of md_constant structures, hashed by name. Null if no |
89 | constant expansion should occur. */ | |
90 | static htab_t md_constants; | |
91 | ||
5d54fceb | 92 | /* A table of enum_type structures, hashed by name. */ |
93 | static htab_t enum_types; | |
94 | ||
77ba95d0 | 95 | static void handle_file (directive_handler_t); |
96 | ||
ac0640e5 | 97 | /* Given an object that starts with a char * name field, return a hash |
98 | code for its name. */ | |
99 | ||
100 | hashval_t | |
101 | leading_string_hash (const void *def) | |
102 | { | |
103 | return htab_hash_string (*(const char *const *) def); | |
104 | } | |
105 | ||
106 | /* Given two objects that start with char * name fields, return true if | |
107 | they have the same name. */ | |
108 | ||
109 | int | |
110 | leading_string_eq_p (const void *def1, const void *def2) | |
111 | { | |
112 | return strcmp (*(const char *const *) def1, | |
113 | *(const char *const *) def2) == 0; | |
114 | } | |
115 | ||
960ebfe7 | 116 | /* Return a hash value for the pointer pointed to by DEF. */ |
117 | ||
118 | static hashval_t | |
119 | leading_ptr_hash (const void *def) | |
120 | { | |
121 | return htab_hash_pointer (*(const void *const *) def); | |
122 | } | |
123 | ||
124 | /* Return true if DEF1 and DEF2 are pointers to the same pointer. */ | |
125 | ||
126 | static int | |
127 | leading_ptr_eq_p (const void *def1, const void *def2) | |
128 | { | |
129 | return *(const void *const *) def1 == *(const void *const *) def2; | |
130 | } | |
131 | ||
132 | /* Associate PTR with the file position given by FILENAME and LINENO. */ | |
133 | ||
134 | static void | |
77c2564f | 135 | set_md_ptr_loc (const void *ptr, const char *filename, int lineno) |
960ebfe7 | 136 | { |
137 | struct ptr_loc *loc; | |
138 | ||
139 | loc = (struct ptr_loc *) obstack_alloc (&ptr_loc_obstack, | |
140 | sizeof (struct ptr_loc)); | |
141 | loc->ptr = ptr; | |
142 | loc->filename = filename; | |
143 | loc->lineno = lineno; | |
144 | *htab_find_slot (ptr_locs, loc, INSERT) = loc; | |
145 | } | |
146 | ||
147 | /* Return the position associated with pointer PTR. Return null if no | |
148 | position was set. */ | |
149 | ||
150 | static const struct ptr_loc * | |
77c2564f | 151 | get_md_ptr_loc (const void *ptr) |
960ebfe7 | 152 | { |
153 | return (const struct ptr_loc *) htab_find (ptr_locs, &ptr); | |
154 | } | |
155 | ||
156 | /* Associate NEW_PTR with the same file position as OLD_PTR. */ | |
157 | ||
158 | void | |
77c2564f | 159 | copy_md_ptr_loc (const void *new_ptr, const void *old_ptr) |
960ebfe7 | 160 | { |
77c2564f | 161 | const struct ptr_loc *loc = get_md_ptr_loc (old_ptr); |
960ebfe7 | 162 | if (loc != 0) |
77c2564f | 163 | set_md_ptr_loc (new_ptr, loc->filename, loc->lineno); |
960ebfe7 | 164 | } |
165 | ||
166 | /* If PTR is associated with a known file position, print a #line | |
73ed0c67 | 167 | directive for it to OUTF. */ |
960ebfe7 | 168 | |
169 | void | |
73ed0c67 | 170 | fprint_md_ptr_loc (FILE *outf, const void *ptr) |
960ebfe7 | 171 | { |
77c2564f | 172 | const struct ptr_loc *loc = get_md_ptr_loc (ptr); |
960ebfe7 | 173 | if (loc != 0) |
73ed0c67 | 174 | fprintf (outf, "#line %d \"%s\"\n", loc->lineno, loc->filename); |
175 | } | |
176 | ||
177 | /* Special fprint_md_ptr_loc for writing to STDOUT. */ | |
178 | void | |
179 | print_md_ptr_loc (const void *ptr) | |
180 | { | |
181 | fprint_md_ptr_loc (stdout, ptr); | |
960ebfe7 | 182 | } |
183 | ||
184 | /* Return a condition that satisfies both COND1 and COND2. Either string | |
185 | may be null or empty. */ | |
186 | ||
187 | const char * | |
188 | join_c_conditions (const char *cond1, const char *cond2) | |
189 | { | |
190 | char *result; | |
191 | const void **entry; | |
192 | ||
193 | if (cond1 == 0 || cond1[0] == 0) | |
194 | return cond2; | |
195 | ||
196 | if (cond2 == 0 || cond2[0] == 0) | |
197 | return cond1; | |
198 | ||
199 | if (strcmp (cond1, cond2) == 0) | |
200 | return cond1; | |
201 | ||
202 | result = concat ("(", cond1, ") && (", cond2, ")", NULL); | |
203 | obstack_ptr_grow (&joined_conditions_obstack, result); | |
204 | obstack_ptr_grow (&joined_conditions_obstack, cond1); | |
205 | obstack_ptr_grow (&joined_conditions_obstack, cond2); | |
206 | entry = XOBFINISH (&joined_conditions_obstack, const void **); | |
207 | *htab_find_slot (joined_conditions, entry, INSERT) = entry; | |
208 | return result; | |
209 | } | |
210 | ||
73ed0c67 | 211 | /* Print condition COND to OUTF, wrapped in brackets. If COND was created |
212 | by join_c_conditions, recursively invoke this function for the original | |
960ebfe7 | 213 | conditions and join the result with "&&". Otherwise print a #line |
214 | directive for COND if its original file position is known. */ | |
215 | ||
216 | void | |
73ed0c67 | 217 | fprint_c_condition (FILE *outf, const char *cond) |
960ebfe7 | 218 | { |
219 | const char **halves = (const char **) htab_find (joined_conditions, &cond); | |
220 | if (halves != 0) | |
221 | { | |
73ed0c67 | 222 | fprintf (outf, "("); |
223 | fprint_c_condition (outf, halves[1]); | |
224 | fprintf (outf, " && "); | |
225 | fprint_c_condition (outf, halves[2]); | |
226 | fprintf (outf, ")"); | |
960ebfe7 | 227 | } |
228 | else | |
229 | { | |
73ed0c67 | 230 | fputc ('\n', outf); |
231 | fprint_md_ptr_loc (outf, cond); | |
232 | fprintf (outf, "(%s)", cond); | |
960ebfe7 | 233 | } |
234 | } | |
235 | ||
73ed0c67 | 236 | /* Special fprint_c_condition for writing to STDOUT. */ |
237 | ||
238 | void | |
239 | print_c_condition (const char *cond) | |
240 | { | |
241 | fprint_c_condition (stdout, cond); | |
242 | } | |
243 | ||
b638f5c8 | 244 | /* A vfprintf-like function for reporting an error against line LINENO |
245 | of the current MD file. */ | |
246 | ||
247 | static void ATTRIBUTE_PRINTF(2,0) | |
36d192df | 248 | message_at_1 (file_location loc, const char *msg, va_list ap) |
b638f5c8 | 249 | { |
36d192df | 250 | fprintf (stderr, "%s:%d: ", loc.filename, loc.lineno); |
b638f5c8 | 251 | vfprintf (stderr, msg, ap); |
252 | fputc ('\n', stderr); | |
253 | } | |
254 | ||
36d192df | 255 | /* A printf-like function for reporting a message against location LOC. */ |
256 | ||
257 | void | |
258 | message_at (file_location loc, const char *msg, ...) | |
259 | { | |
260 | va_list ap; | |
261 | ||
262 | va_start (ap, msg); | |
263 | message_at_1 (loc, msg, ap); | |
264 | va_end (ap); | |
265 | } | |
266 | ||
267 | /* Like message_at, but treat the condition as an error. */ | |
268 | ||
269 | void | |
270 | error_at (file_location loc, const char *msg, ...) | |
271 | { | |
272 | va_list ap; | |
273 | ||
274 | va_start (ap, msg); | |
275 | message_at_1 (loc, msg, ap); | |
276 | va_end (ap); | |
277 | have_error = 1; | |
278 | } | |
279 | ||
e7ff515f | 280 | /* Like message_at, but treat the condition as a fatal error. */ |
281 | ||
282 | void | |
283 | fatal_at (file_location loc, const char *msg, ...) | |
284 | { | |
285 | va_list ap; | |
286 | ||
287 | va_start (ap, msg); | |
288 | message_at_1 (loc, msg, ap); | |
289 | va_end (ap); | |
290 | exit (1); | |
291 | } | |
292 | ||
960ebfe7 | 293 | /* A printf-like function for reporting an error against the current |
b3453c30 | 294 | position in the MD file. */ |
960ebfe7 | 295 | |
296 | void | |
b3453c30 | 297 | fatal_with_file_and_line (const char *msg, ...) |
960ebfe7 | 298 | { |
299 | char context[64]; | |
300 | size_t i; | |
301 | int c; | |
302 | va_list ap; | |
303 | ||
304 | va_start (ap, msg); | |
305 | ||
77c2564f | 306 | fprintf (stderr, "%s:%d: ", read_md_filename, read_md_lineno); |
960ebfe7 | 307 | vfprintf (stderr, msg, ap); |
308 | putc ('\n', stderr); | |
309 | ||
310 | /* Gather some following context. */ | |
311 | for (i = 0; i < sizeof (context)-1; ++i) | |
312 | { | |
b3453c30 | 313 | c = read_char (); |
960ebfe7 | 314 | if (c == EOF) |
315 | break; | |
316 | if (c == '\r' || c == '\n') | |
606cf8b8 | 317 | { |
318 | unread_char (c); | |
319 | break; | |
320 | } | |
960ebfe7 | 321 | context[i] = c; |
322 | } | |
323 | context[i] = '\0'; | |
324 | ||
325 | fprintf (stderr, "%s:%d: following context is `%s'\n", | |
77c2564f | 326 | read_md_filename, read_md_lineno, context); |
960ebfe7 | 327 | |
328 | va_end (ap); | |
329 | exit (1); | |
330 | } | |
331 | ||
332 | /* Report that we found character ACTUAL when we expected to find | |
b3453c30 | 333 | character EXPECTED. */ |
960ebfe7 | 334 | |
335 | void | |
b3453c30 | 336 | fatal_expected_char (int expected, int actual) |
960ebfe7 | 337 | { |
338 | if (actual == EOF) | |
b3453c30 | 339 | fatal_with_file_and_line ("expected character `%c', found EOF", |
960ebfe7 | 340 | expected); |
341 | else | |
b3453c30 | 342 | fatal_with_file_and_line ("expected character `%c', found `%c'", |
960ebfe7 | 343 | expected, actual); |
344 | } | |
345 | ||
b3453c30 | 346 | /* Read chars from the MD file until a non-whitespace char and return that. |
960ebfe7 | 347 | Comments, both Lisp style and C style, are treated as whitespace. */ |
348 | ||
349 | int | |
b3453c30 | 350 | read_skip_spaces (void) |
960ebfe7 | 351 | { |
352 | int c; | |
353 | ||
354 | while (1) | |
355 | { | |
b3453c30 | 356 | c = read_char (); |
960ebfe7 | 357 | switch (c) |
358 | { | |
606cf8b8 | 359 | case ' ': case '\t': case '\f': case '\r': case '\n': |
960ebfe7 | 360 | break; |
361 | ||
362 | case ';': | |
363 | do | |
b3453c30 | 364 | c = read_char (); |
960ebfe7 | 365 | while (c != '\n' && c != EOF); |
960ebfe7 | 366 | break; |
367 | ||
368 | case '/': | |
369 | { | |
370 | int prevc; | |
b3453c30 | 371 | c = read_char (); |
960ebfe7 | 372 | if (c != '*') |
606cf8b8 | 373 | { |
374 | unread_char (c); | |
375 | fatal_with_file_and_line ("stray '/' in file"); | |
376 | } | |
960ebfe7 | 377 | |
378 | prevc = 0; | |
b3453c30 | 379 | while ((c = read_char ()) && c != EOF) |
960ebfe7 | 380 | { |
606cf8b8 | 381 | if (prevc == '*' && c == '/') |
960ebfe7 | 382 | break; |
383 | prevc = c; | |
384 | } | |
385 | } | |
386 | break; | |
387 | ||
388 | default: | |
389 | return c; | |
390 | } | |
391 | } | |
392 | } | |
393 | ||
ac0640e5 | 394 | /* Read an rtx code name into NAME. It is terminated by any of the |
395 | punctuation chars of rtx printed syntax. */ | |
396 | ||
397 | void | |
398 | read_name (struct md_name *name) | |
399 | { | |
400 | int c; | |
401 | size_t i; | |
402 | ||
403 | c = read_skip_spaces (); | |
404 | ||
405 | i = 0; | |
406 | while (1) | |
407 | { | |
408 | if (c == ' ' || c == '\n' || c == '\t' || c == '\f' || c == '\r' | |
409 | || c == EOF) | |
410 | break; | |
411 | if (c == ':' || c == ')' || c == ']' || c == '"' || c == '/' | |
412 | || c == '(' || c == '[') | |
413 | { | |
414 | unread_char (c); | |
415 | break; | |
416 | } | |
417 | ||
418 | if (i == sizeof (name->buffer) - 1) | |
419 | fatal_with_file_and_line ("name too long"); | |
420 | name->buffer[i++] = c; | |
421 | ||
422 | c = read_char (); | |
423 | } | |
424 | ||
425 | if (i == 0) | |
426 | fatal_with_file_and_line ("missing name or number"); | |
ac0640e5 | 427 | |
428 | name->buffer[i] = 0; | |
429 | name->string = name->buffer; | |
430 | ||
431 | if (md_constants) | |
432 | { | |
433 | /* Do constant expansion. */ | |
434 | struct md_constant *def; | |
435 | ||
436 | do | |
437 | { | |
438 | struct md_constant tmp_def; | |
439 | ||
440 | tmp_def.name = name->string; | |
441 | def = (struct md_constant *) htab_find (md_constants, &tmp_def); | |
442 | if (def) | |
443 | name->string = def->value; | |
444 | } | |
445 | while (def); | |
446 | } | |
447 | } | |
448 | ||
960ebfe7 | 449 | /* Subroutine of the string readers. Handles backslash escapes. |
450 | Caller has read the backslash, but not placed it into the obstack. */ | |
451 | ||
452 | static void | |
b3453c30 | 453 | read_escape (void) |
960ebfe7 | 454 | { |
b3453c30 | 455 | int c = read_char (); |
960ebfe7 | 456 | |
457 | switch (c) | |
458 | { | |
459 | /* Backslash-newline is replaced by nothing, as in C. */ | |
460 | case '\n': | |
960ebfe7 | 461 | return; |
462 | ||
463 | /* \" \' \\ are replaced by the second character. */ | |
464 | case '\\': | |
465 | case '"': | |
466 | case '\'': | |
467 | break; | |
468 | ||
469 | /* Standard C string escapes: | |
470 | \a \b \f \n \r \t \v | |
471 | \[0-7] \x | |
472 | all are passed through to the output string unmolested. | |
473 | In normal use these wind up in a string constant processed | |
474 | by the C compiler, which will translate them appropriately. | |
475 | We do not bother checking that \[0-7] are followed by up to | |
476 | two octal digits, or that \x is followed by N hex digits. | |
477 | \? \u \U are left out because they are not in traditional C. */ | |
478 | case 'a': case 'b': case 'f': case 'n': case 'r': case 't': case 'v': | |
479 | case '0': case '1': case '2': case '3': case '4': case '5': case '6': | |
480 | case '7': case 'x': | |
481 | obstack_1grow (&string_obstack, '\\'); | |
482 | break; | |
483 | ||
484 | /* \; makes stuff for a C string constant containing | |
485 | newline and tab. */ | |
486 | case ';': | |
487 | obstack_grow (&string_obstack, "\\n\\t", 4); | |
488 | return; | |
489 | ||
490 | /* pass anything else through, but issue a warning. */ | |
491 | default: | |
492 | fprintf (stderr, "%s:%d: warning: unrecognized escape \\%c\n", | |
77c2564f | 493 | read_md_filename, read_md_lineno, c); |
960ebfe7 | 494 | obstack_1grow (&string_obstack, '\\'); |
495 | break; | |
496 | } | |
497 | ||
498 | obstack_1grow (&string_obstack, c); | |
499 | } | |
500 | ||
501 | /* Read a double-quoted string onto the obstack. Caller has scanned | |
502 | the leading quote. */ | |
503 | ||
504 | char * | |
b3453c30 | 505 | read_quoted_string (void) |
960ebfe7 | 506 | { |
507 | int c; | |
508 | ||
509 | while (1) | |
510 | { | |
b3453c30 | 511 | c = read_char (); /* Read the string */ |
606cf8b8 | 512 | if (c == '\\') |
960ebfe7 | 513 | { |
b3453c30 | 514 | read_escape (); |
960ebfe7 | 515 | continue; |
516 | } | |
517 | else if (c == '"' || c == EOF) | |
518 | break; | |
519 | ||
520 | obstack_1grow (&string_obstack, c); | |
521 | } | |
522 | ||
523 | obstack_1grow (&string_obstack, 0); | |
524 | return XOBFINISH (&string_obstack, char *); | |
525 | } | |
526 | ||
527 | /* Read a braced string (a la Tcl) onto the string obstack. Caller | |
528 | has scanned the leading brace. Note that unlike quoted strings, | |
529 | the outermost braces _are_ included in the string constant. */ | |
530 | ||
531 | static char * | |
b3453c30 | 532 | read_braced_string (void) |
960ebfe7 | 533 | { |
534 | int c; | |
535 | int brace_depth = 1; /* caller-processed */ | |
77c2564f | 536 | unsigned long starting_read_md_lineno = read_md_lineno; |
960ebfe7 | 537 | |
538 | obstack_1grow (&string_obstack, '{'); | |
539 | while (brace_depth) | |
540 | { | |
b3453c30 | 541 | c = read_char (); /* Read the string */ |
960ebfe7 | 542 | |
606cf8b8 | 543 | if (c == '{') |
960ebfe7 | 544 | brace_depth++; |
545 | else if (c == '}') | |
546 | brace_depth--; | |
547 | else if (c == '\\') | |
548 | { | |
b3453c30 | 549 | read_escape (); |
960ebfe7 | 550 | continue; |
551 | } | |
552 | else if (c == EOF) | |
553 | fatal_with_file_and_line | |
b3453c30 | 554 | ("missing closing } for opening brace on line %lu", |
77c2564f | 555 | starting_read_md_lineno); |
960ebfe7 | 556 | |
557 | obstack_1grow (&string_obstack, c); | |
558 | } | |
559 | ||
560 | obstack_1grow (&string_obstack, 0); | |
561 | return XOBFINISH (&string_obstack, char *); | |
562 | } | |
563 | ||
564 | /* Read some kind of string constant. This is the high-level routine | |
565 | used by read_rtx. It handles surrounding parentheses, leading star, | |
566 | and dispatch to the appropriate string constant reader. */ | |
567 | ||
568 | char * | |
b3453c30 | 569 | read_string (int star_if_braced) |
960ebfe7 | 570 | { |
571 | char *stringbuf; | |
572 | int saw_paren = 0; | |
573 | int c, old_lineno; | |
574 | ||
b3453c30 | 575 | c = read_skip_spaces (); |
960ebfe7 | 576 | if (c == '(') |
577 | { | |
578 | saw_paren = 1; | |
b3453c30 | 579 | c = read_skip_spaces (); |
960ebfe7 | 580 | } |
581 | ||
77c2564f | 582 | old_lineno = read_md_lineno; |
960ebfe7 | 583 | if (c == '"') |
b3453c30 | 584 | stringbuf = read_quoted_string (); |
960ebfe7 | 585 | else if (c == '{') |
586 | { | |
587 | if (star_if_braced) | |
588 | obstack_1grow (&string_obstack, '*'); | |
b3453c30 | 589 | stringbuf = read_braced_string (); |
960ebfe7 | 590 | } |
591 | else | |
b3453c30 | 592 | fatal_with_file_and_line ("expected `\"' or `{', found `%c'", c); |
960ebfe7 | 593 | |
594 | if (saw_paren) | |
595 | { | |
b3453c30 | 596 | c = read_skip_spaces (); |
960ebfe7 | 597 | if (c != ')') |
b3453c30 | 598 | fatal_expected_char (')', c); |
960ebfe7 | 599 | } |
600 | ||
77c2564f | 601 | set_md_ptr_loc (stringbuf, read_md_filename, old_lineno); |
960ebfe7 | 602 | return stringbuf; |
603 | } | |
604 | ||
664c5888 | 605 | /* Skip the rest of a construct that started at line LINENO and that |
606 | is currently nested by DEPTH levels of parentheses. */ | |
607 | ||
36d192df | 608 | static void |
609 | read_skip_construct (int depth, file_location loc) | |
664c5888 | 610 | { |
611 | struct md_name name; | |
612 | int c; | |
613 | ||
614 | do | |
615 | { | |
616 | c = read_skip_spaces (); | |
617 | if (c == EOF) | |
618 | { | |
36d192df | 619 | error_at (loc, "unterminated construct"); |
664c5888 | 620 | exit (1); |
621 | } | |
622 | switch (c) | |
623 | { | |
624 | case '(': | |
625 | depth++; | |
626 | break; | |
627 | ||
628 | case ')': | |
629 | depth--; | |
630 | break; | |
631 | ||
632 | case ':': | |
633 | case '[': | |
634 | case ']': | |
635 | case '/': | |
636 | break; | |
637 | ||
638 | case '\"': | |
639 | case '{': | |
640 | unread_char (c); | |
641 | read_string (false); | |
642 | break; | |
643 | ||
644 | default: | |
645 | unread_char (c); | |
646 | read_name (&name); | |
647 | break; | |
648 | } | |
649 | } | |
650 | while (depth > 0); | |
651 | unread_char (c); | |
652 | } | |
653 | ||
960ebfe7 | 654 | /* Given a string, return the number of comma-separated elements in it. |
655 | Return 0 for the null string. */ | |
656 | ||
657 | int | |
658 | n_comma_elts (const char *s) | |
659 | { | |
660 | int n; | |
661 | ||
662 | if (*s == '\0') | |
663 | return 0; | |
664 | ||
665 | for (n = 1; *s; s++) | |
666 | if (*s == ',') | |
667 | n++; | |
668 | ||
669 | return n; | |
670 | } | |
671 | ||
672 | /* Given a pointer to a (char *), return a pointer to the beginning of the | |
673 | next comma-separated element in the string. Advance the pointer given | |
674 | to the end of that element. Return NULL if at end of string. Caller | |
675 | is responsible for copying the string if necessary. White space between | |
676 | a comma and an element is ignored. */ | |
677 | ||
678 | const char * | |
679 | scan_comma_elt (const char **pstr) | |
680 | { | |
681 | const char *start; | |
682 | const char *p = *pstr; | |
683 | ||
684 | if (*p == ',') | |
685 | p++; | |
9af5ce0c | 686 | while (ISSPACE (*p)) |
960ebfe7 | 687 | p++; |
688 | ||
689 | if (*p == '\0') | |
690 | return NULL; | |
691 | ||
692 | start = p; | |
693 | ||
694 | while (*p != ',' && *p != '\0') | |
695 | p++; | |
696 | ||
697 | *pstr = p; | |
698 | return start; | |
699 | } | |
700 | ||
5d54fceb | 701 | /* Convert STRING to uppercase. */ |
702 | ||
703 | void | |
704 | upcase_string (char *string) | |
705 | { | |
706 | int i; | |
707 | ||
708 | for (i = 0; string[i]; i++) | |
709 | string[i] = TOUPPER (string[i]); | |
710 | } | |
711 | ||
712 | /* Add a NAME = VALUE definition to md_constants-style hash table DEFS, | |
713 | where both NAME and VALUE are malloc()ed strings. PARENT_ENUM is the | |
714 | enum to which NAME belongs, or null if NAME is a stand-alone constant. */ | |
715 | ||
716 | static struct md_constant * | |
717 | add_constant (htab_t defs, char *name, char *value, | |
718 | struct enum_type *parent_enum) | |
719 | { | |
720 | struct md_constant *def, tmp_def; | |
721 | void **entry_ptr; | |
722 | ||
723 | tmp_def.name = name; | |
724 | entry_ptr = htab_find_slot (defs, &tmp_def, INSERT); | |
725 | if (*entry_ptr) | |
726 | { | |
727 | def = (struct md_constant *) *entry_ptr; | |
728 | if (strcmp (def->value, value) != 0) | |
729 | fatal_with_file_and_line ("redefinition of `%s', was `%s', now `%s'", | |
730 | def->name, def->value, value); | |
731 | else if (parent_enum || def->parent_enum) | |
732 | fatal_with_file_and_line ("redefinition of `%s'", def->name); | |
733 | free (name); | |
734 | free (value); | |
735 | } | |
736 | else | |
737 | { | |
738 | def = XNEW (struct md_constant); | |
739 | def->name = name; | |
740 | def->value = value; | |
741 | def->parent_enum = parent_enum; | |
742 | *entry_ptr = def; | |
743 | } | |
744 | return def; | |
745 | } | |
746 | ||
ac0640e5 | 747 | /* Process a define_constants directive, starting with the optional space |
748 | after the "define_constants". */ | |
749 | ||
77ba95d0 | 750 | static void |
751 | handle_constants (void) | |
ac0640e5 | 752 | { |
753 | int c; | |
754 | htab_t defs; | |
755 | ||
ac0640e5 | 756 | c = read_skip_spaces (); |
757 | if (c != '[') | |
758 | fatal_expected_char ('[', c); | |
759 | ||
760 | /* Disable constant expansion during definition processing. */ | |
5d54fceb | 761 | defs = md_constants; |
ac0640e5 | 762 | md_constants = 0; |
763 | while ( (c = read_skip_spaces ()) != ']') | |
764 | { | |
765 | struct md_name name, value; | |
ac0640e5 | 766 | |
767 | if (c != '(') | |
768 | fatal_expected_char ('(', c); | |
769 | ||
770 | read_name (&name); | |
771 | read_name (&value); | |
5d54fceb | 772 | add_constant (defs, xstrdup (name.string), xstrdup (value.string), 0); |
ac0640e5 | 773 | |
774 | c = read_skip_spaces (); | |
775 | if (c != ')') | |
776 | fatal_expected_char (')', c); | |
777 | } | |
778 | md_constants = defs; | |
ac0640e5 | 779 | } |
780 | ||
781 | /* For every constant definition, call CALLBACK with two arguments: | |
782 | a pointer a pointer to the constant definition and INFO. | |
783 | Stop when CALLBACK returns zero. */ | |
784 | ||
785 | void | |
786 | traverse_md_constants (htab_trav callback, void *info) | |
787 | { | |
5d54fceb | 788 | htab_traverse (md_constants, callback, info); |
789 | } | |
790 | ||
791 | /* Return a malloc()ed decimal string that represents number NUMBER. */ | |
792 | ||
793 | static char * | |
8697277d | 794 | md_decimal_string (int number) |
5d54fceb | 795 | { |
796 | /* A safe overestimate. +1 for sign, +1 for null terminator. */ | |
797 | char buffer[sizeof (int) * CHAR_BIT + 1 + 1]; | |
798 | ||
799 | sprintf (buffer, "%d", number); | |
800 | return xstrdup (buffer); | |
801 | } | |
802 | ||
803 | /* Process a define_enum or define_c_enum directive, starting with | |
804 | the optional space after the "define_enum". LINENO is the line | |
805 | number on which the directive started and MD_P is true if the | |
806 | directive is a define_enum rather than a define_c_enum. */ | |
807 | ||
808 | static void | |
36d192df | 809 | handle_enum (file_location loc, bool md_p) |
5d54fceb | 810 | { |
811 | char *enum_name, *value_name; | |
812 | struct md_name name; | |
813 | struct enum_type *def; | |
814 | struct enum_value *ev; | |
815 | void **slot; | |
816 | int c; | |
817 | ||
818 | enum_name = read_string (false); | |
819 | slot = htab_find_slot (enum_types, &enum_name, INSERT); | |
820 | if (*slot) | |
821 | { | |
822 | def = (struct enum_type *) *slot; | |
823 | if (def->md_p != md_p) | |
36d192df | 824 | error_at (loc, "redefining `%s' as a different type of enum", |
825 | enum_name); | |
5d54fceb | 826 | } |
827 | else | |
828 | { | |
829 | def = XNEW (struct enum_type); | |
830 | def->name = enum_name; | |
831 | def->md_p = md_p; | |
832 | def->values = 0; | |
833 | def->tail_ptr = &def->values; | |
834 | def->num_values = 0; | |
835 | *slot = def; | |
836 | } | |
837 | ||
838 | c = read_skip_spaces (); | |
839 | if (c != '[') | |
840 | fatal_expected_char ('[', c); | |
841 | ||
842 | while ((c = read_skip_spaces ()) != ']') | |
843 | { | |
844 | if (c == EOF) | |
845 | { | |
36d192df | 846 | error_at (loc, "unterminated construct"); |
5d54fceb | 847 | exit (1); |
848 | } | |
849 | unread_char (c); | |
850 | read_name (&name); | |
851 | ||
852 | ev = XNEW (struct enum_value); | |
853 | ev->next = 0; | |
854 | if (md_p) | |
855 | { | |
856 | value_name = concat (def->name, "_", name.string, NULL); | |
857 | upcase_string (value_name); | |
858 | ev->name = xstrdup (name.string); | |
859 | } | |
860 | else | |
861 | { | |
862 | value_name = xstrdup (name.string); | |
863 | ev->name = value_name; | |
864 | } | |
865 | ev->def = add_constant (md_constants, value_name, | |
8697277d | 866 | md_decimal_string (def->num_values), def); |
5d54fceb | 867 | |
868 | *def->tail_ptr = ev; | |
869 | def->tail_ptr = &ev->next; | |
870 | def->num_values++; | |
871 | } | |
872 | } | |
873 | ||
d37b1ac9 | 874 | /* Try to find the definition of the given enum. Return null on failure. */ |
875 | ||
876 | struct enum_type * | |
877 | lookup_enum_type (const char *name) | |
878 | { | |
879 | return (struct enum_type *) htab_find (enum_types, &name); | |
880 | } | |
881 | ||
5d54fceb | 882 | /* For every enum definition, call CALLBACK with two arguments: |
883 | a pointer to the constant definition and INFO. Stop when CALLBACK | |
884 | returns zero. */ | |
885 | ||
886 | void | |
887 | traverse_enum_types (htab_trav callback, void *info) | |
888 | { | |
889 | htab_traverse (enum_types, callback, info); | |
ac0640e5 | 890 | } |
891 | ||
77ba95d0 | 892 | /* Process an "include" directive, starting with the optional space |
893 | after the "include". Read in the file and use HANDLE_DIRECTIVE | |
894 | to process each unknown directive. LINENO is the line number on | |
75de4aa2 | 895 | which the "include" occurred. */ |
960ebfe7 | 896 | |
77ba95d0 | 897 | static void |
36d192df | 898 | handle_include (file_location loc, directive_handler_t handle_directive) |
960ebfe7 | 899 | { |
77ba95d0 | 900 | const char *filename; |
901 | const char *old_filename; | |
902 | int old_lineno; | |
903 | char *pathname; | |
904 | FILE *input_file, *old_file; | |
905 | ||
906 | filename = read_string (false); | |
907 | input_file = NULL; | |
908 | ||
909 | /* If the specified file name is absolute, skip the include stack. */ | |
910 | if (!IS_ABSOLUTE_PATH (filename)) | |
911 | { | |
912 | struct file_name_list *stackp; | |
913 | ||
914 | /* Search the directory path, trying to open the file. */ | |
915 | for (stackp = first_dir_md_include; stackp; stackp = stackp->next) | |
916 | { | |
917 | static const char sep[2] = { DIR_SEPARATOR, '\0' }; | |
918 | ||
919 | pathname = concat (stackp->fname, sep, filename, NULL); | |
920 | input_file = fopen (pathname, "r"); | |
921 | if (input_file != NULL) | |
922 | break; | |
923 | free (pathname); | |
924 | } | |
925 | } | |
926 | ||
927 | /* If we haven't managed to open the file yet, try combining the | |
928 | filename with BASE_DIR. */ | |
929 | if (input_file == NULL) | |
930 | { | |
931 | if (base_dir) | |
932 | pathname = concat (base_dir, filename, NULL); | |
933 | else | |
934 | pathname = xstrdup (filename); | |
935 | input_file = fopen (pathname, "r"); | |
936 | } | |
937 | ||
938 | if (input_file == NULL) | |
939 | { | |
940 | free (pathname); | |
36d192df | 941 | error_at (loc, "include file `%s' not found", filename); |
77ba95d0 | 942 | return; |
943 | } | |
944 | ||
945 | /* Save the old cursor. Note that the LINENO argument to this | |
946 | function is the beginning of the include statement, while | |
947 | read_md_lineno has already been advanced. */ | |
948 | old_file = read_md_file; | |
949 | old_filename = read_md_filename; | |
950 | old_lineno = read_md_lineno; | |
951 | ||
952 | if (include_callback) | |
953 | include_callback (pathname); | |
954 | ||
955 | read_md_file = input_file; | |
956 | read_md_filename = pathname; | |
957 | handle_file (handle_directive); | |
958 | ||
959 | /* Restore the old cursor. */ | |
960 | read_md_file = old_file; | |
961 | read_md_filename = old_filename; | |
962 | read_md_lineno = old_lineno; | |
963 | ||
964 | /* Do not free the pathname. It is attached to the various rtx | |
965 | queue elements. */ | |
966 | } | |
967 | ||
968 | /* Process the current file, assuming that read_md_file and | |
969 | read_md_filename are valid. Use HANDLE_DIRECTIVE to handle | |
970 | unknown directives. */ | |
971 | ||
972 | static void | |
973 | handle_file (directive_handler_t handle_directive) | |
974 | { | |
975 | struct md_name directive; | |
36d192df | 976 | int c; |
77ba95d0 | 977 | |
978 | read_md_lineno = 1; | |
979 | while ((c = read_skip_spaces ()) != EOF) | |
980 | { | |
36d192df | 981 | file_location loc (read_md_filename, read_md_lineno); |
77ba95d0 | 982 | if (c != '(') |
983 | fatal_expected_char ('(', c); | |
984 | ||
985 | read_name (&directive); | |
986 | if (strcmp (directive.string, "define_constants") == 0) | |
987 | handle_constants (); | |
5d54fceb | 988 | else if (strcmp (directive.string, "define_enum") == 0) |
36d192df | 989 | handle_enum (loc, true); |
5d54fceb | 990 | else if (strcmp (directive.string, "define_c_enum") == 0) |
36d192df | 991 | handle_enum (loc, false); |
77ba95d0 | 992 | else if (strcmp (directive.string, "include") == 0) |
36d192df | 993 | handle_include (loc, handle_directive); |
664c5888 | 994 | else if (handle_directive) |
36d192df | 995 | handle_directive (loc, directive.string); |
664c5888 | 996 | else |
36d192df | 997 | read_skip_construct (1, loc); |
77ba95d0 | 998 | |
999 | c = read_skip_spaces (); | |
1000 | if (c != ')') | |
1001 | fatal_expected_char (')', c); | |
1002 | } | |
1003 | fclose (read_md_file); | |
1004 | } | |
1005 | ||
1006 | /* Like handle_file, but for top-level files. Set up in_fname and | |
1007 | base_dir accordingly. */ | |
1008 | ||
1009 | static void | |
1010 | handle_toplevel_file (directive_handler_t handle_directive) | |
1011 | { | |
82715bcd | 1012 | const char *base; |
77ba95d0 | 1013 | |
1014 | in_fname = read_md_filename; | |
82715bcd | 1015 | base = lbasename (in_fname); |
1016 | if (base == in_fname) | |
77ba95d0 | 1017 | base_dir = NULL; |
82715bcd | 1018 | else |
1019 | base_dir = xstrndup (in_fname, base - in_fname); | |
77ba95d0 | 1020 | |
1021 | handle_file (handle_directive); | |
1022 | } | |
1023 | ||
1024 | /* Parse a -I option with argument ARG. */ | |
1025 | ||
1026 | static void | |
1027 | parse_include (const char *arg) | |
1028 | { | |
1029 | struct file_name_list *dirtmp; | |
1030 | ||
1031 | dirtmp = XNEW (struct file_name_list); | |
1032 | dirtmp->next = 0; | |
1033 | dirtmp->fname = arg; | |
1034 | *last_dir_md_include_ptr = dirtmp; | |
1035 | last_dir_md_include_ptr = &dirtmp->next; | |
1036 | if (strlen (dirtmp->fname) > max_include_len) | |
1037 | max_include_len = strlen (dirtmp->fname); | |
1038 | } | |
1039 | ||
1040 | /* The main routine for reading .md files. Try to process all the .md | |
75de4aa2 | 1041 | files specified on the command line and return true if no error occurred. |
77ba95d0 | 1042 | |
1043 | ARGC and ARGV are the arguments to main. | |
1044 | ||
1045 | PARSE_OPT, if nonnull, is passed all unknown command-line arguments. | |
1046 | It should return true if it recognizes the argument or false if a | |
1047 | generic error should be reported. | |
1048 | ||
664c5888 | 1049 | If HANDLE_DIRECTIVE is nonnull, the parser calls it for each |
1050 | unknown directive, otherwise it just skips such directives. | |
77ba95d0 | 1051 | See the comment above the directive_handler_t definition for |
1052 | details about the callback's interface. */ | |
1053 | ||
1054 | bool | |
1055 | read_md_files (int argc, char **argv, bool (*parse_opt) (const char *), | |
1056 | directive_handler_t handle_directive) | |
1057 | { | |
1058 | int i; | |
1059 | bool no_more_options; | |
1060 | bool already_read_stdin; | |
1061 | int num_files; | |
1062 | ||
1063 | /* Initialize global data. */ | |
960ebfe7 | 1064 | obstack_init (&string_obstack); |
1065 | ptr_locs = htab_create (161, leading_ptr_hash, leading_ptr_eq_p, 0); | |
1066 | obstack_init (&ptr_loc_obstack); | |
1067 | joined_conditions = htab_create (161, leading_ptr_hash, leading_ptr_eq_p, 0); | |
1068 | obstack_init (&joined_conditions_obstack); | |
5d54fceb | 1069 | md_constants = htab_create (31, leading_string_hash, |
1070 | leading_string_eq_p, (htab_del) 0); | |
1071 | enum_types = htab_create (31, leading_string_hash, | |
1072 | leading_string_eq_p, (htab_del) 0); | |
77ba95d0 | 1073 | |
1074 | /* Unlock the stdio streams. */ | |
1075 | unlock_std_streams (); | |
1076 | ||
1077 | /* First we loop over all the options. */ | |
1078 | for (i = 1; i < argc; i++) | |
1079 | if (argv[i][0] == '-') | |
1080 | { | |
1081 | /* An argument consisting of exactly one dash is a request to | |
1082 | read stdin. This will be handled in the second loop. */ | |
1083 | if (argv[i][1] == '\0') | |
1084 | continue; | |
1085 | ||
1086 | /* An argument consisting of just two dashes causes option | |
1087 | parsing to cease. */ | |
1088 | if (argv[i][1] == '-' && argv[i][2] == '\0') | |
1089 | break; | |
1090 | ||
1091 | if (argv[i][1] == 'I') | |
1092 | { | |
1093 | if (argv[i][2] != '\0') | |
1094 | parse_include (argv[i] + 2); | |
1095 | else if (++i < argc) | |
1096 | parse_include (argv[i]); | |
1097 | else | |
1098 | fatal ("directory name missing after -I option"); | |
1099 | continue; | |
1100 | } | |
1101 | ||
1102 | /* The program may have provided a callback so it can | |
1103 | accept its own options. */ | |
1104 | if (parse_opt && parse_opt (argv[i])) | |
1105 | continue; | |
1106 | ||
1107 | fatal ("invalid option `%s'", argv[i]); | |
1108 | } | |
1109 | ||
1110 | /* Now loop over all input files. */ | |
1111 | num_files = 0; | |
1112 | no_more_options = false; | |
1113 | already_read_stdin = false; | |
1114 | for (i = 1; i < argc; i++) | |
1115 | { | |
1116 | if (argv[i][0] == '-') | |
1117 | { | |
1118 | if (argv[i][1] == '\0') | |
1119 | { | |
1120 | /* Read stdin. */ | |
1121 | if (already_read_stdin) | |
1122 | fatal ("cannot read standard input twice"); | |
1123 | ||
1124 | read_md_file = stdin; | |
1125 | read_md_filename = "<stdin>"; | |
1126 | handle_toplevel_file (handle_directive); | |
1127 | already_read_stdin = true; | |
1128 | continue; | |
1129 | } | |
1130 | else if (argv[i][1] == '-' && argv[i][2] == '\0') | |
1131 | { | |
1132 | /* No further arguments are to be treated as options. */ | |
1133 | no_more_options = true; | |
1134 | continue; | |
1135 | } | |
1136 | else if (!no_more_options) | |
1137 | continue; | |
1138 | } | |
1139 | ||
1140 | /* If we get here we are looking at a non-option argument, i.e. | |
1141 | a file to be processed. */ | |
1142 | read_md_filename = argv[i]; | |
1143 | read_md_file = fopen (read_md_filename, "r"); | |
1144 | if (read_md_file == 0) | |
1145 | { | |
1146 | perror (read_md_filename); | |
1147 | return false; | |
1148 | } | |
1149 | handle_toplevel_file (handle_directive); | |
1150 | num_files++; | |
1151 | } | |
1152 | ||
1153 | /* If we get to this point without having seen any files to process, | |
1154 | read the standard input now. */ | |
1155 | if (num_files == 0 && !already_read_stdin) | |
1156 | { | |
1157 | read_md_file = stdin; | |
1158 | read_md_filename = "<stdin>"; | |
1159 | handle_toplevel_file (handle_directive); | |
1160 | } | |
1161 | ||
1162 | return !have_error; | |
960ebfe7 | 1163 | } |