/*
* Copyright (c) 2003 - 2007, Nils R. Weller
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* This file contains the hierarchial source analyzer, called
* analyze(). This function uses the global variable
* ``toklist'' which must point to the complete translation unit
*/
#include "analyze.h"
#include <string.h>
#include <stdlib.h>
#include "token.h"
#include "decl.h"
#include "error.h"
#include "backend.h"
#include "scope.h"
#include "symlist.h"
#include "type.h"
#include "expr.h"
#include "misc.h"
#include "functions.h"
#include "debug.h"
#include "builtins.h"
#include "inlineasm.h"
#include "icode.h"
#include "control.h"
#include "n_libc.h"
static void
check_main(struct token *t, struct type *ty) {
static struct type *maindef = NULL;
struct ty_func *tfunc = ty->tlist->tfunc;
struct sym_entry *se;
struct type *tytmp;
if (maindef != NULL) return;
if (ty->name[0] != 'm' || strcmp(ty->name + 1, "ain") != 0) {
return;
}
maindef = ty;
/* XXX why is it always null??? */
if (ty->code != TY_INT
|| ty->tlist->next != NULL) {
warningfl(t,
"Illegal return type for main(). ISO C says it must be `int'!");
return;
} else if (ty->storage == TOK_KEY_STATIC) {
errorfl(t, "Static (file scope) declaration of main()");
return;
}
switch (tfunc->nargs) {
case 0:
case -1:
/* int main(void) or int main() */
break;
case 1:
errorfl(t,
"main() should take no, two or three arguments");
break;
case 2:
case 3:
/* XXX check arg1 + 2 = int,char** */
se = tfunc->scope->slist;
tytmp = make_basic_type(TY_INT);
if (compare_types(se->dec->dtype, tytmp,
CMPTY_SIGN|CMPTY_CONST) != 0) {
errorfl(t, "First argument of main is not \
`int' as it should be");
}
tytmp = n_xmemdup(make_basic_type(TY_CHAR), sizeof *tytmp);
append_typelist(tytmp, TN_POINTER_TO, 0, NULL, NULL);
append_typelist(tytmp, TN_POINTER_TO, 0, NULL, NULL);
if (compare_types(se->next->dec->dtype, tytmp,
CMPTY_SIGN|CMPTY_CONST) != 0) {
errorfl(t, "Second argument of main is not \
`char **' as it should be");
}
free(tytmp);
if (tfunc->nargs == 3) {
/* XXX check arg3 = char** */
}
}
}
/*
* Do semantic analysis of translation unit.
* Initialize functions, structure, globals subsystems
* Returns 0 if no errors were found, else 1
*/
int
analyze(struct token **curtok) {
struct token *t;
struct decl **d;
struct expr *ex;
struct function *func = NULL;
struct token *builtin_tok = NULL;
struct token *starttok;
struct decl **builtin_decl = NULL;
static int level;
int braces = 0;
int is_func;
int i;
if (++level == 1) {
static int ptropval = TOK_OP_AMB_MULTI;
static int num = 1;
static const struct {
void *p;
int tokval;
} normal_va_list[] = {
{ "typedef", TOK_IDENTIFIER },
{ "char", TOK_IDENTIFIER },
{ &ptropval, TOK_OPERATOR },
{ "__builtin_va_list", TOK_IDENTIFIER },
{ NULL, TOK_SEMICOLON },
{ NULL, 0 }
}, amd64_va_list[] = {
/* typedef struct { */
{ "typedef", TOK_IDENTIFIER },
{ "struct", TOK_IDENTIFIER },
{ NULL, TOK_COMP_OPEN },
/* unsigned gp_offset; */
{ "unsigned", TOK_IDENTIFIER },
{ "gp_offset", TOK_IDENTIFIER },
{ NULL, TOK_SEMICOLON },
/* unsigned fp_offset; */
{ "unsigned", TOK_IDENTIFIER },
{ "fp_offset", TOK_IDENTIFIER },
{ NULL, TOK_SEMICOLON },
/* void *overflow_arg_area; */
{ "void", TOK_IDENTIFIER },
{ &ptropval, TOK_OPERATOR },
{ "overflow_arg_area", TOK_IDENTIFIER },
{ NULL, TOK_SEMICOLON },
/* void *reg_save_area; */
{ "void", TOK_IDENTIFIER },
{ &ptropval, TOK_OPERATOR },
{ "reg_save_area", TOK_IDENTIFIER },
{ NULL, TOK_SEMICOLON },
/* } __builtin_va_list[1]; */
{ NULL, TOK_COMP_CLOSE },
{ "__builtin_va_list", TOK_IDENTIFIER },
{ NULL, TOK_ARRAY_OPEN },
{ &num, TY_INT },
{ NULL, TOK_ARRAY_CLOSE },
{ NULL, TOK_SEMICOLON },
{ NULL, 0 }
}, *vatok;
/* Initial call; Otherwise called by parse_expr() */
curscope = &global_scope;
#if 0
store_token(&builtin_tok,
n_xstrdup("typedef"), TOK_IDENTIFIER, 0);
store_token(&builtin_tok,
n_xstrdup("char"), TOK_IDENTIFIER, 0);
i = TOK_OP_AMB_MULTI;
store_token(&builtin_tok, &i, TOK_OPERATOR, 0);
store_token(&builtin_tok, "__builtin_va_list",
TOK_IDENTIFIER, 0);
store_token(&builtin_tok, 0, TOK_SEMICOLON, 0);
store_token(&builtin_tok, NULL, 0, 0);
if ((builtin_decl = parse_decl(&builtin_tok, 0)) != NULL) {
store_decl_scope(&global_scope, builtin_decl);
} else {
warningfl(NULL, "Cannot create builtins");
}
#endif
if (backend->arch == ARCH_AMD64) {
vatok = amd64_va_list;
} else {
vatok = normal_va_list;
}
for (i = 0; vatok[i].p || vatok[i].tokval; ++i) {
void *p;
int *dummyptr = n_xmalloc(sizeof *dummyptr);
if (vatok[i].tokval == TOK_IDENTIFIER) {
p = n_xstrdup(vatok[i].p);
} else if (vatok[i].tokval == TY_INT) {
#if 0
p = n_xmemdup(vatok[i].p, sizeof(int));
#endif
p = n_xmalloc(16); /* XXX */
memcpy(p, vatok[i].p, sizeof(int)); /* XXX */
} else {
if (vatok[i].p != NULL) {
p = vatok[i].p;
} else {
p = dummyptr;
}
}
store_token(&builtin_tok, p, vatok[i].tokval, 0, NULL);
}
if ((builtin_decl = parse_decl(&builtin_tok, 0)) != NULL) {
store_decl_scope(&global_scope, builtin_decl);
builtin_va_list_type = builtin_decl[0]->dtype;
} else {
warningfl(NULL, "Cannot create builtins");
}
t = toklist; /* Start at beginning */
} else {
t = *curtok; /* Start at current token */
}
for (; t != NULL; t = t->next) {
if (t->prev
&& (t->prev->type == TOK_COMP_OPEN
|| t->prev->type == TOK_COMP_CLOSE
|| t->prev->type == TOK_SEMICOLON)) {
if (!errors) {
/*
* 09/01/07: XXX This token is apparently
* still sometimes used if we have parse
* errors!!!!
*/
free(t->prev);
t->prev = NULL;
}
}
/*
* 08/19/07: Check with lookup_symbol() added. Otherwise
* stuff like
*
* typedef int foo;
* {
* int foo;
* foo = 0; <--- bad!!
* }
*
* ... yields a syntax error because ``foo = 0;'' is
* interpreted as a declaration
*/
if (IS_TYPE(t)
&& (t->type != TOK_IDENTIFIER
|| lookup_symbol(curscope, t->data, 1) == NULL)) {
do_decl:
starttok = t;
d = parse_decl(&t, DECL_VARINIT);
if (d == NULL) {
/*
* XXX call recover() instead?
*/
t = t->next;
while (t != NULL
&& t->type != TOK_SEMICOLON
&& !IS_KEYWORD(t->type)) {
t = t->next;
}
if (t == NULL) {
break;
}
if (IS_KEYWORD(t->type)) {
t = t->prev;
}
continue;
} else {
int k;
/*
* 07/23/07: Don't free tokens if there's a
* VLA involved somewhere, cuz otherwise we
* still need to evaluate the expression(s)
*/
for (k = 0; d[k] != NULL; ++k) {
if (d[k]->dtype->is_vla) {
break;
}
}
if (d[k] == NULL) {
free_tokens(starttok, t->prev, FREE_DECL);
}
}
is_func = 0;
if ((d[0]->dtype->code == TY_STRUCT
|| d[0]->dtype->code == TY_ENUM
|| d[0]->dtype->code == TY_UNION)
&& d[0]->dtype->is_def) {
if (!d[0]->dtype->is_func) {
continue;
}
}
#ifdef DEBUG2
printf("Parsed declaration successfully!\n");
#endif
if (t->type == TOK_COMP_OPEN) {
++braces;
for (i = 0; d[i] != NULL; ++i) {
if (d[i]->dtype->is_func) {
is_func = 1;
}
}
if (is_func && i > 1) {
/*
* This must be illegal, as in
* int foo, bar(void) { ... }
*/
errorfl(t,
"Syntax error at %s - function definitions may not be paired with "
"other declarations!", t->ascii);
/* XXX what to do here? */
} else if (is_func) {
if (curscope->parent
!= &global_scope) {
/*
* ->parent because
* parse_func() already
* opens new scope
*/
errorfl(t,
"Nested function definitions are not allowed");
/*XXX what to do here?*/
} else {
d[0]->dtype->is_def = 1;
func = alloc_function();
/*func->scope = curscope;*/
func->proto = d[0];
func->fty = d[0]->dtype->tlist->tfunc;
/* XXX complete ... */
check_main(t, d[0]->dtype);
curfunc = func;
/*
* Note that in cases like
* void (*foo())() { ...
* (function returning
* function pointer), the
* current scope will now
* be that of the return
* type function declaration.
* So it has to be set to that
* of the function definition
* here
*/
curscope = d[0]->dtype->
tlist->tfunc->scope;
func->scope = curscope;
}
} else {
errorfl(t,
"Syntax error at `%s'", t->ascii);
/* XXX what to do here? */
}
}
} else if (t->type == TOK_KEY_ASM) {
/* Inline assembler statement */
struct statement *st;
struct inline_asm_stmt *inl;
if ((inl = parse_inline_asm(&t)) != NULL) {
st = alloc_statement();
st->type = ST_ASM;
st->data = inl;
append_statement(&curscope->code,
&curscope->code_tail, st);
t = t->next;
}
} else if (IS_CONTROL(t->type)) {
(void) parse_ctrl(curcont, &t, 1, NULL);
#ifdef NO_EXPR
/*
* If the expression parser is used, it will parse casts on its own -
* if it's not, we still want to parse casts in order to debug the
* code responsible for it!
*/
} else if (t->type == TOK_PAREN_OPEN
&& curscope != &global_scope) {
if (t->next == NULL) {
errorfl(t, "Unexpected end of file");
break; /* XXX */
}
if (IS_TYPE(t->next)) {
/* This has got to be a cast */
t = t->next;
d = parse_decl(&t, DECL_CAST);
if (d != NULL) {
#ifdef DEBUG2
printf("Parsed cast successfully!\n");
#endif
} else {
/* Recover from error */
while (t != NULL
&& t->type != TOK_SEMICOLON) {
t = t->next;
}
if (t == NULL) {
break;
}
}
}
#endif
} else if (t->type == TOK_COMP_OPEN) {
/* Opening a compound statement - new scope! */
struct statement *st;
if (curscope == &global_scope) {
errorfl(t,
"Invalid compound statement at top level");
/* Create new scope anyway, to match with } */
}
if (curcont != NULL && curcont->stmt == NULL) {
curcont->compound_body = 1;
curcont->stmt = alloc_statement();
curcont->stmt->type = ST_COMP;
curcont->stmt->data = new_scope(SCOPE_CODE);
curcont->braces = braces;
} else {
st = alloc_statement();
st->type = ST_COMP;
append_statement(&curscope->code,
&curscope->code_tail, st);
st->data = new_scope(SCOPE_CODE);
}
++braces;
} else if (t->type == TOK_COMP_CLOSE) {
/* Closing compound statement - leaving scope! */
close_scope();
if (--braces == 0 && func) {
#if 0
if (func && --braces == 0) {
#endif
/* This is the end of a function definition */
func->next = funclist;
funclist = func;
#if 0
if (funclist == NULL) {
funclist = funclist_tail = func;
} else {
funclist_tail->next = func;
funclist_tail = func;
}
#endif
curfunc = func = NULL;
}
if (curcont && braces == curcont->braces) {
complete_ctrl(&t, curcont);
}
if (braces == 0 && level > 1) {
/* GNU statement-as-expression ends here */
*curtok = t;
break;
}
} else {
if (curscope == &global_scope) {
/* Must be implicit function declaration */
if (t->type == TOK_SEMICOLON) {
warningfl(t,
"Empty expression used outside"
" of function - illegal in ISO"
" C");
continue;
}
goto do_decl;
}
if (t->type == TOK_IDENTIFIER) {
if (try_label(&t, NULL)) {
/* was label */
continue;
}
}
ex = parse_expr(&t, TOK_SEMICOLON, 0, 0, 1);
if (ex != NULL) {
put_expr_scope(ex);
}
}
if (t == NULL) {
break;
}
}
if (level == 1) {
curscope = &global_scope;
}
if (braces > 0) {
errorfl(NULL, "Parse error at end of input - "
"%d missing `}' tokens", braces);
}
--level;
if (!errors) {
if (level == 0) {
for (func = funclist; func != NULL; func = func->next) {
debug_print_function(func);
curfunc = func;
curscope = func->scope;
func->icode =
xlate_to_icode(func->scope->code, 1);
/*
* 10/31/07: Added this to make sure that all
* registers are completely thrown away when a
* function ends. Anything else is just nonsense
* and causes subsequent function definitions to
* perform unnecessary saves of these registers
*/
backend->invalidate_gprs(NULL, 0);
}
}
} else if (level != 0) {
return -1;
}
#if 0
if (/* check_unused */1) {
struct decl *dp;
int i;
struct decl *darr[] = {
static_init_vars, static_uninit_vars, NULL
};
for (i = 0; darr[i] != NULL; ++i) {
for (dp = darr[i]; dp != NULL; dp = dp->next) {
if (dp->dtype->storage != TOK_KEY_EXTERN
&& dp->references == 0
&& !is_basic_agg_type(dp->dtype)) {
warningfl(dp->tok,
"Unused variable `%s'",
dp->dtype->name);
}
}
}
}
#endif
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1