/* * 'sparse' library helper routines. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "lib.h" #include "allocate.h" #include "token.h" #include "parse.h" #include "symbol.h" #include "expression.h" #include "evaluate.h" #include "scope.h" #include "linearize.h" #include "target.h" #include "machine.h" #include "bits.h" static int prettify(const char **fnamep) { const char *name = *fnamep; int len = strlen(name); if (len > 2 && !memcmp(name, "./", 2)) { name += 2; len -= 2; } *fnamep = name; return len; } static const char *show_include_chain(int stream, const char *base) { static char buffer[200]; int len = 0; while ((stream = stream_prev(stream)) >= 0) { const char *p = stream_name(stream); int pretty_len; if (p == base) break; pretty_len = prettify(&p); if (pretty_len <= 0) break; /* * At worst, we'll need " (through %s, ...)" in addition to the * new filename */ if (pretty_len + len + 20 > sizeof(buffer)) { if (!len) return ""; memcpy(buffer+len, ", ...", 5); len += 5; break; } if (!len) { memcpy(buffer, " (through ", 10); len = 10; } else { buffer[len++] = ','; buffer[len++] = ' '; } memcpy(buffer+len, p, pretty_len); len += pretty_len; } if (!len) return ""; buffer[len] = ')'; buffer[len+1] = 0; return buffer; } static const char *show_stream_name(struct position pos) { const char *name = stream_name(pos.stream); static const char *last; if (name == base_filename) return name; if (name == last) return name; last = name; fprintf(stderr, "%s: note: in included file%s:\n", base_filename, show_include_chain(pos.stream, base_filename)); return name; } static void do_warn(const char *type, struct position pos, const char * fmt, va_list args) { static char buffer[512]; /* Shut up warnings if position is bad_token.pos */ if (pos.type == TOKEN_BAD) return; vsprintf(buffer, fmt, args); fflush(stdout); fprintf(stderr, "%s:%d:%d: %s%s%s\n", show_stream_name(pos), pos.line, pos.pos, diag_prefix, type, buffer); } static int show_info = 1; void info(struct position pos, const char * fmt, ...) { va_list args; if (!show_info) return; va_start(args, fmt); do_warn("", pos, fmt, args); va_end(args); } static void do_error(struct position pos, const char * fmt, va_list args) { static int errors = 0; die_if_error = 1; show_info = 1; /* Shut up warnings if position is bad_token.pos */ if (pos.type == TOKEN_BAD) return; /* Shut up warnings after an error */ has_error |= ERROR_CURR_PHASE; if (errors > fmax_errors) { static int once = 0; show_info = 0; if (once) return; fmt = "too many errors"; once = 1; } do_warn("error: ", pos, fmt, args); errors++; } void warning(struct position pos, const char * fmt, ...) { va_list args; if (Wsparse_error) { va_start(args, fmt); do_error(pos, fmt, args); va_end(args); return; } if (!fmax_warnings || has_error) { show_info = 0; return; } if (!--fmax_warnings) { show_info = 0; fmt = "too many warnings"; } va_start(args, fmt); do_warn("warning: ", pos, fmt, args); va_end(args); } void sparse_error(struct position pos, const char * fmt, ...) { va_list args; va_start(args, fmt); do_error(pos, fmt, args); va_end(args); } void expression_error(struct expression *expr, const char *fmt, ...) { va_list args; va_start(args, fmt); do_error(expr->pos, fmt, args); va_end(args); expr->ctype = &bad_ctype; } NORETURN_ATTR void error_die(struct position pos, const char * fmt, ...) { va_list args; va_start(args, fmt); do_warn("error: ", pos, fmt, args); va_end(args); exit(1); } NORETURN_ATTR void die(const char *fmt, ...) { va_list args; static char buffer[512]; va_start(args, fmt); vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); fprintf(stderr, "%s%s\n", diag_prefix, buffer); exit(1); } //////////////////////////////////////////////////////////////////////////////// static struct token *pre_buffer_begin = NULL; static struct token **pre_buffer_next = &pre_buffer_begin; void add_pre_buffer(const char *fmt, ...) { va_list args; unsigned int size; struct token *begin, *end; char buffer[4096]; va_start(args, fmt); size = vsnprintf(buffer, sizeof(buffer), fmt, args); va_end(args); begin = tokenize_buffer(buffer, size, &end); *pre_buffer_next = begin; pre_buffer_next = &end->next; } static void create_builtin_stream(void) { // Temporary hack add_pre_buffer("#define _Pragma(x)\n"); /* add the multiarch include directories, if any */ if (multiarch_dir && *multiarch_dir) { add_pre_buffer("#add_system \"/usr/include/%s\"\n", multiarch_dir); add_pre_buffer("#add_system \"/usr/local/include/%s\"\n", multiarch_dir); } /* We add compiler headers path here because we have to parse * the arguments to get it, falling back to default. */ add_pre_buffer("#add_system \"%s/include\"\n", gcc_base_dir); add_pre_buffer("#add_system \"%s/include-fixed\"\n", gcc_base_dir); add_pre_buffer("#define __builtin_stdarg_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n"); add_pre_buffer("#define __builtin_va_start(a,b) ((a) = (__builtin_va_list)(&(b)))\n"); add_pre_buffer("#define __builtin_ms_va_start(a,b) ((a) = (__builtin_ms_va_list)(&(b)))\n"); add_pre_buffer("#define __builtin_va_arg(arg,type) ({ type __va_arg_ret = *(type *)(arg); arg += sizeof(type); __va_arg_ret; })\n"); add_pre_buffer("#define __builtin_va_alist (*(void *)0)\n"); add_pre_buffer("#define __builtin_va_arg_incr(x) ((x) + 1)\n"); add_pre_buffer("#define __builtin_va_copy(dest, src) ({ dest = src; (void)0; })\n"); add_pre_buffer("#define __builtin_ms_va_copy(dest, src) ({ dest = src; (void)0; })\n"); add_pre_buffer("#define __builtin_va_end(arg)\n"); add_pre_buffer("#define __builtin_ms_va_end(arg)\n"); add_pre_buffer("#define __builtin_va_arg_pack()\n"); } static struct symbol_list *sparse_tokenstream(struct token *token) { int builtin = token && !token->pos.stream; // Preprocess the stream token = preprocess(token); if (dump_macro_defs || dump_macros_only) { if (!builtin) dump_macro_definitions(); if (dump_macros_only) return NULL; } if (preprocess_only) { while (!eof_token(token)) { int prec = 1; struct token *next = token->next; const char *separator = ""; if (next->pos.whitespace) separator = " "; if (next->pos.newline) { separator = "\n\t\t\t\t\t"; prec = next->pos.pos; if (prec > 4) prec = 4; } printf("%s%.*s", show_token(token), prec, separator); token = next; } putchar('\n'); return NULL; } // Parse the resulting C code while (!eof_token(token)) token = external_declaration(token, &translation_unit_used_list, NULL); return translation_unit_used_list; } static struct symbol_list *sparse_file(const char *filename) { int fd; struct token *token; if (strcmp(filename, "-") == 0) { fd = 0; } else { fd = open(filename, O_RDONLY); if (fd < 0) die("No such file: %s", filename); } base_filename = filename; // Tokenize the input stream token = tokenize(NULL, filename, fd, NULL, includepath); close(fd); return sparse_tokenstream(token); } /* * This handles the "-include" directive etc: we're in global * scope, and all types/macros etc will affect all the following * files. * * NOTE NOTE NOTE! "#undef" of anything in this stage will * affect all subsequent files too, i.e. we can have non-local * behaviour between files! */ static struct symbol_list *sparse_initial(void) { int i; // Prepend any "include" file to the stream. // We're in global scope, it will affect all files! for (i = 0; i < cmdline_include_nr; i++) add_pre_buffer("#argv_include \"%s\"\n", cmdline_include[i]); return sparse_tokenstream(pre_buffer_begin); } struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **filelist) { char **args; struct symbol_list *list; base_filename = "command-line"; // Initialize symbol stream first, so that we can add defines etc init_symbols(); // initialize the default target to the native 'machine' target_config(MACH_NATIVE); args = argv; for (;;) { char *arg = *++args; if (!arg) break; if (arg[0] == '-' && arg[1]) { args = handle_switch(arg+1, args); continue; } add_ptr_list(filelist, arg); } handle_switch_finalize(); // Redirect stdout if needed if (dump_macro_defs || preprocess_only) do_output = 1; if (do_output && outfile && strcmp(outfile, "-")) { if (!freopen(outfile, "w", stdout)) die("error: cannot open %s: %s", outfile, strerror(errno)); } if (fdump_ir == 0) fdump_ir = PASS_FINAL; list = NULL; if (filelist) { // Initialize type system target_init(); init_ctype(); predefined_macros(); create_builtin_stream(); init_builtins(0); list = sparse_initial(); /* * Protect the initial token allocations, since * they need to survive all the others */ protect_token_alloc(); } /* * Evaluate the complete symbol list * Note: This is not needed for normal cases. * These symbols should only be predefined defines and * declaratons which will be evaluated later, when needed. * This is also the case when a file is directly included via * '-include ' on the command line *AND* the file only * contains defines, declarations and inline definitions. * However, in the rare cases where the given file should * contain some definitions, these will never be evaluated * and thus won't be able to be linearized correctly. * Hence the evaluate_symbol_list() here under. */ evaluate_symbol_list(list); return list; } struct symbol_list * sparse_keep_tokens(char *filename) { struct symbol_list *res; /* Clear previous symbol list */ translation_unit_used_list = NULL; new_file_scope(); res = sparse_file(filename); /* And return it */ return res; } struct symbol_list * __sparse(char *filename) { struct symbol_list *res; res = sparse_keep_tokens(filename); /* Drop the tokens for this file after parsing */ clear_token_alloc(); /* And return it */ return res; } struct symbol_list * sparse(char *filename) { struct symbol_list *res = __sparse(filename); if (has_error & ERROR_CURR_PHASE) has_error = ERROR_PREV_PHASE; /* Evaluate the complete symbol list */ evaluate_symbol_list(res); return res; }