blob: e282a4fb4d54a203523c352df0031a0074804c0d [file] [log] [blame]
/*
* '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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#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 <file>' 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;
}