/* * Copyright (c) 2005 - 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. * * Parsing of, and icode generation for, sub-expressions */ #include "expr.h" #include "subexpr.h" #include #include #include "token.h" #include "error.h" #include "misc.h" #include "defs.h" #include "decl.h" #include "scope.h" #include "type.h" #include "icode.h" #include "symlist.h" #include "cc1_main.h" #include "debug.h" #include "functions.h" #include "backend.h" #include "reg.h" #include "x87_nonsense.h" #include "builtins.h" #include "n_libc.h" struct context { struct type curtype; struct decl *var_lvalue; struct vreg *curitem; struct vreg *load; int is_lvalue; int indir; }; static struct s_expr * alloc_s_expr(void) { struct s_expr *ret = n_xmalloc(sizeof *ret); static struct s_expr nullop; *ret = nullop; return ret; } static int ambig_to_unary(struct token *t, int is_prefix) { int op = *(int *)t->data; switch (op) { case TOK_OP_AMB_PLUS: if (is_prefix) op = TOK_OP_UPLUS; break; case TOK_OP_AMB_MINUS: if (is_prefix) op = TOK_OP_UMINUS; break; case TOK_OP_AMB_MULTI: if (is_prefix) op = TOK_OP_DEREF; break; case TOK_OP_AMB_BAND: if (is_prefix) op = TOK_OP_ADDR; break; case TOK_OP_AMB_INCR: if (is_prefix) op = TOK_OP_INCPRE; else op = TOK_OP_INCPOST; break; case TOK_OP_AMB_DECR: if (is_prefix) op = TOK_OP_DECPRE; else op = TOK_OP_DECPOST; break; default: /* XXX handle labels! foo: */ ; break; } if (is_prefix && !IS_UNARY(op)) { /* * foo + bar is ok * / foo is not */ errorfl(t, "Invalid use of operator `%s'", t->ascii); return -1; } return op; } static int check_memb_select(struct type *curtype, struct token *op) { char *p; if (curtype->code == 0) { errorfl(op, "Invalid use of `%s' operator", op->ascii); return -1; } if (curtype->tstruc == NULL) { errorfl(op, "`%s' operator applied to non-union/structure type", op->ascii); return -1; } if (op->type == TOK_OP_STRUMEMB) { /* . requires plain struct/union type */ if (curtype->tlist != NULL) { p = curtype->code == TY_UNION? "union": "struct"; if (curtype->tstruc->tag != NULL) { errorfl(op, "`.' operator applied to something " "that is not of type `%s %s'", p, curtype->tstruc->tag); } else { errorfl(op, "`.' operator applied to something " "that is not of type `%s'", p); } return -1; } } else { /* * -> requires *single* pointer (or * array, since array types decay * into pointers in expressions) * struct/union */ if (curtype->tlist == NULL || curtype->tlist->next != NULL || curtype->tlist->tfunc != NULL) { p = curtype->code == TY_UNION? "union": "struct"; if (curtype->tstruc->tag != NULL) { errorfl(op, "`->' operator applied to " "something that is not of " "type `%s %s *'", p, curtype->tstruc->tag); } else { errorfl(op, "`->' operator applied to " "something that is not of " "type `pointer to struct/union'"); } return -1; } } return 0; } static void print_nomemb(struct type *curtype, struct token *tok, const char *member) { char *p; p = curtype->code == TY_UNION? "Union": "Structure"; if (curtype->tstruc->tag != NULL) { errorfl(tok, "%s type `%s' has no member named `%s'", p, curtype->tstruc->tag, member); } else { errorfl(tok, "%s type has no member named `%s'", p, member); } } /* * XXX this stuff ends up handling unions and structs identically, thus * deferring all the work to the backend :( * I've tried removing the union details at this point and patching up * all member stack addressing info in the backend as necessary ... But * it just never works. Would be cool to have base addresses and offsets * such that the var_backed/from_const/stack_addr/from_ptr mess becomes * say one ``struct address'' and then one could do * struct address *member = addr_offset(base, nbytes); * or something like that. Deferring all of this offset addressing * nonsense is terrible to the maximum :( */ static int do_struct_member( struct token *tok, struct context *context, struct icode_list *il, int eval) { struct decl *dp; struct decl *uniondp = NULL; struct type *curtype = &context->curtype; struct token *ident = tok->data; struct vreg *vr = NULL; int is_ptr; int is_struct; int is_anon_union = 0; is_ptr = tok->type == TOK_OP_STRUPMEMB; is_struct = curtype->code == TY_STRUCT; if (check_memb_select(curtype, tok) == -1) { return -1; } if (curtype->tstruc->incomplete) { errorfl(tok, "Cannot access members of incomplete structure type"); return -1; } ++curtype->tstruc->references; dp = access_symbol(curtype->tstruc->scope, ident->data, 0); if (dp == NULL) { /* * Member not found. This may be an anonymous union member! */ struct sym_entry *se; for (se = curtype->tstruc->scope->slist; se != NULL; se = se->next) { struct type *ty = se->dec->dtype; if (ty->code == TY_UNION && ty->tlist == NULL && ty->name == NULL) { /* * This is an anonymous union! But does it * contain the identifier we are looing for? */ dp = access_symbol(se->dec->dtype->tstruc->scope, ident->data, 0); if (dp != NULL) { uniondp = se->dec; break; } } } if (dp == NULL) { print_nomemb(curtype, tok, ident->data); return -1; } is_anon_union = 1; } copy_type(curtype, dp->dtype, 0); curtype->storage = 0; if (eval) { if (is_ptr) { vr = vreg_alloc(NULL, NULL, NULL, dp->dtype); /* Avoid indirection chain */ #if 0 if (context->curitem->from_ptr) { vreg_anonymify(&context->curitem, NULL, NULL, il); } #endif /* * XXX 07/09/07: The UNCONDITIONAL anonymify and * vreg_disconnect are new. Without them, map_pregs * in backend.c trashed a shared vreg register * reference when assigning NULL to pregs[0]! */ vreg_anonymify(&context->curitem, NULL, NULL, il); context->curitem = vreg_disconnect(context->curitem); if (!is_anon_union) { vr->from_ptr = context->curitem; } } else { vr = vreg_alloc(dp, NULL, NULL, dp->dtype); } vr->memberdecl = dp; if (is_anon_union) { vr->parent = vreg_alloc(NULL, NULL, NULL, uniondp->dtype); vr->parent->memberdecl = uniondp; if (is_ptr) { vr->parent->from_ptr = context->curitem; } vr->parent->parent = context->curitem; } else { vr->parent = context->curitem; } context->curitem = vr; } else { vr = vreg_alloc(NULL, NULL, NULL, NULL); vreg_set_new_type(vr, dp->dtype); context->curitem = vr; } if (!is_ptr) { context->var_lvalue = dp; } else { context->var_lvalue = NULL; } context->is_lvalue = 1; if (context->load == NULL) { context->load = vr; } return 0; } static int do_subscript(struct context *context, struct token *tok, struct icode_list *il, int eval) { struct expr *ex; struct vreg *exvr; struct type *curtype = &context->curtype; if (curtype->code == 0) { errorfl(tok, "Parse error at `['"); return -1; } ex = tok->data; exvr = expr_to_icode(ex, NULL, il, 0, eval); if (exvr == NULL) { return -1; } if (exvr->type->tlist == NULL && curtype->tlist == NULL) { errorfl(tok, "Array subscript operator applied to non-pointer type"); return -1; } else if (exvr->type->tlist != NULL && curtype->tlist != NULL) { errorfl(tok, "Index with bad type for array subscript operator"); return -1; } else if (curtype->code == TY_VOID) { if (curtype->tlist != NULL && curtype->tlist->next == NULL) { errorfl(tok, "Cannot subscript void pointers"); return -1; } } else if ((exvr->type->tlist == NULL && !is_integral_type(exvr->type)) || (curtype->tlist == NULL && !is_integral_type(curtype))) { errorfl(tok, "Subscript not of integral type"); return -1; } if (eval) { #if 0 vreg_faultin(NULL, NULL, exvr, il, 0); #endif /* XXX doesn't work for num[ptr] */ if (promote(&exvr, NULL, 0, NULL, il, 1) == NULL) { return -1; } vreg_faultin_protected(exvr, NULL, NULL, context->curitem, il, 0); if (context->curitem->type->tlist->type == TN_ARRAY_OF) { context->curitem = vreg_disconnect(context->curitem); context->curitem->type = n_xmemdup(context->curitem->type, sizeof(struct type)); copy_tlist(&context->curitem->type->tlist, context->curitem->type->tlist); context->curitem->type->tlist->type = TN_POINTER_TO; context->curitem->size = backend->get_sizeof_type(context->curitem->type, NULL); } do_add_sub(&context->curitem, &exvr, TOK_OP_PLUS, NULL, il, eval); free_pregs_vreg(exvr, il, 0, 0); } copy_type(&context->curtype, context->curitem->type, 0); context->curtype.storage = 0; copy_tlist(&context->curtype.tlist, context->curtype.tlist->next); context->is_lvalue = 1; context->var_lvalue = NULL; #if 0 if (context->curitem->from_ptr && eval) { #endif /* Avoid chain of indirection */ #if 0 vreg_anonymify(&context->curitem, NULL, NULL, il); #endif context->curitem = vreg_disconnect(context->curitem); #if 0 if (context->curitem->type->tlist && context->curitem->type->tlist->type == TN_ARRAY_OF) { context->curitem->type = addrofify_type(context->curitem->type); } #endif #if 0 } #endif context->curitem = vreg_alloc(NULL, NULL, context->curitem, NULL); return 0; } struct fcall_data * alloc_fcall_data(void) { struct fcall_data *ret = n_xmalloc(sizeof *ret); static struct fcall_data nullfdat; *ret = nullfdat; return ret; } static struct fcall_data * do_func_call(struct token *meat, struct token **tok, int eval) { struct expr *ex; struct expr *args = NULL; struct expr *argstail = NULL; struct token *t = *tok; struct fcall_data *fcall = NULL; int nargs = 0; if (next_token(&t) != 0) { return NULL; } if (meat && meat->type == TOK_IDENTIFIER) { size_t len = sizeof "__builtin_" - 1; if (strncmp(meat->data, "__builtin_", len) == 0) { *tok = t; return get_builtin(tok, meat); } else if (*(char *)meat->data == 'a' && strcmp(meat->data, "alloca") == 0) { /* * alloca() support SUCKS! Linux/BSD libraries * assume __builtin_alloca() if __GNUC__ is * defined. However, if we don't define it, it * declares alloca() without prodiving a * definition! So we have to catch that too * because we occasionally have to undefine * __GNUC__ (gcc itself cannot build programs * using alloca() if we explicitly pass * -U__GNUC__.) */ *tok = t; return get_builtin(tok, meat); } } for (;;) { if (t->type == TOK_PAREN_CLOSE) { /* Function call ends here */ #ifdef DEBUG3 printf("Parsed function call with %d arguments\n", nargs); #endif if (eval) { fcall = alloc_fcall_data(); fcall->args = args; fcall->nargs = nargs; } break; } ex = parse_expr(&t, TOK_OP_COMMA, TOK_PAREN_CLOSE, 0, 1); if (ex != NULL) { ++nargs; append_expr(&args, &argstail, ex); if (t->type != TOK_PAREN_CLOSE) { /* Must be comma */ if (next_token(&t) != 0) { return NULL; } } } else { return NULL; } } *tok = t; return fcall; } /* * Parses sizeof or alignof operator because they act exactly the same, * including the type of the result */ static int do_sizeof( struct token *t, struct token **endp, struct token **is_sizeof, int extype, int delim, int delim2) { struct token *newconst = NULL; struct type *restype; int is_alignof = t->type == TOK_KEY_ALIGNOF; int is_vla = 0; struct s_expr *subex = NULL; *is_sizeof = t; if (next_token(&t) != 0) { return -1; } if (t->type == TOK_PAREN_OPEN && t->next && IS_TYPE(t->next)) { struct decl **d; /* * sizeof(type-name) */ (void) next_token(&t); d = parse_decl(&t, DECL_CAST); if (d == NULL) { return -1; } restype = d[0]->dtype; if (restype->is_vla) { is_vla = 1; (*is_sizeof)->type = TOK_SIZEOF_VLA_EXPR; (*is_sizeof)->data = d[0]->dtype; } free(d); /* Skip ``)'' */ if (next_token(&t) != 0) { return -1; } } else { /* sizeof sub-expr */ subex = get_sub_expr(&t, delim, delim2, 0); if (subex == NULL) { return -1; } /* Don't evaluate! */ if (s_expr_to_icode(subex, NULL, NULL, 0) == NULL) { return -1; } if (subex->type == NULL) { return -1; } restype = subex->type; if (restype->is_vla) { is_vla = 1; (*is_sizeof)->type = TOK_SIZEOF_VLA_EXPR; (*is_sizeof)->data = subex; } } *endp = t; if (restype->tlist && restype->tlist->type == TN_FUNCTION) { errorfl(*is_sizeof, "Cannot take size of function"); return -1; } if (restype->incomplete || (restype->tstruc != NULL && restype->tstruc->incomplete && is_basic_agg_type(restype))) { errorfl(*is_sizeof, "Cannot take size of incomplete type"); return -1; } if (!is_vla) { newconst = const_from_type(restype, is_alignof, extype); (*is_sizeof)->data = vreg_alloc(NULL, newconst, NULL, NULL); } return 0; } static struct icode_instr * do_identifier( struct context *context, struct token *t, int extype, struct decl **is_func_call, int *err, int eval) { struct decl *dec; struct type *curtype = &context->curtype; struct icode_instr *ret = NULL; struct vreg *vr; int is_implicit = 0; dec = access_symbol(curscope, t->data, 1); *err = 1; if (is_func_call) { if ((*is_func_call = dec) == NULL) { /* * Call to an undeclared (implicitly * int in C89) or builtin function. */ int is_builtin = 0; if (strcmp(t->data, "malloc") == 0 || strcmp(t->data, "realloc") == 0 || strcmp(t->data, "calloc") == 0) { warningfl(t, "Incorrect call to `%s' without " "declaration, please `#include " "'", t->data); } else if (strncmp(t->data, "__builtin_", sizeof "__builtin_" - 1) != 0) { warningfl(t, "Call to `%s' without declaration (illegal in C99)", t->data); } else { is_builtin = 1; } if (!is_builtin) { dec = put_implicit(t->data); is_implicit = 1; } else { *err = 0; return NULL; } *is_func_call = dec; } else if (0 /* is not really a function!~*/) { /* XXX handle */ } else if (dec->dtype->implicit) { is_implicit = 1; } *err = 0; } if (dec == NULL) { if (/*(dec=lookup_undeclared(curscope,t->data,1))==NULL*/1) { errorfl(t, "Undeclared identifier `%s'", (char *)t->data); } return NULL; } else if (extype == EXPR_CONST) { if (*is_func_call) { errorfl(t, "Function call in constant expression"); } else { errorfl(t, "Use of variable in constant expression"); } return NULL; } else if (extype == EXPR_CONSTINIT || extype == EXPR_OPTCONSTINIT || extype == EXPR_OPTCONSTARRAYSIZE) { if (t->prev->type != TOK_OPERATOR || *(int *)t->prev->data != TOK_OP_ADDR) { #if 0 if (*is_func_call) { /* * This may be a call to a builtin function * which yields a constant result, such as * __builtin_offsetof(). In that case we * want to evaluate the value as a constant * expression */ } #endif if (extype == EXPR_CONSTINIT) { if (*is_func_call) { errorfl(t, "Function call in constant expression"); } else { errorfl(t, "Use of variable in constant " "expression"); } return NULL; } else if (extype == EXPR_OPTCONSTINIT) { warningfl(t, "Variable array/struct " "initializers are not allowed in ISO " "C90 (only GNU C and C99)"); } else { /* OPTCONSTARRAYSIZE */ warningfl(t, "Variable-size arrays " "are not allowed in ISO C90 (only GNU " "C and C99)"); } } if (dec->dtype->storage != TOK_KEY_STATIC && dec->dtype->storage != TOK_KEY_EXTERN) { if (extype == EXPR_CONSTINIT) { errorfl(t, "Address of non-static variable taken in constant initializer"); return NULL; } else { warningfl(t, "Variable array/struct " "initializers are not allowed in ISO " "C90 (only GNU C and C99)"); } } } else { /* * in * int foo; foo = 0; ... the second foo identifier can now * be freed. This is not possible in case of a static variable, * because it will have a different name (such as _Static_foo0) */ if (dec->dtype->storage != TOK_KEY_STATIC && !is_implicit) { /* XXX this breaks with errorfl() for some reason!! * see bs.c with valgrind */ #if 0 free(t->data); t->data = dec->dtype->name; #endif } } context->var_lvalue = dec; context->is_lvalue = 1; copy_type(curtype, dec->dtype, 0); if (dec->vreg != NULL) { /* Variable is already register-backed */ *err = 0; context->load = context->curitem = dec->vreg; return NULL; } if (eval) { *err = 0; vr = vreg_alloc(dec, NULL, NULL, NULL); context->load = dec->vreg = context->curitem = vr; } else { *err = 0; /* XXX ...?! */ vr = vreg_alloc(NULL, NULL, NULL, dec->dtype); context->curitem = vr; return NULL; } return ret; } static void do_constant( struct context *context, struct token *t, int eval) { struct type *curtype = &context->curtype; struct type *ctype; struct vreg *vr; ctype = make_basic_type(t->type); copy_type(curtype, ctype, 0); if (eval) { vr = vreg_alloc(NULL, t, NULL, NULL); context->load = context->curitem = vr; } else { vr = vreg_alloc(NULL, NULL, NULL, ctype); context->curitem = vr; } } static struct vreg * do_string(struct context *context, struct token *t, int eval) { struct type *curtype = &context->curtype; struct vreg *vr; copy_type(curtype, ((struct ty_string *)t->data)->ty, 0); if (eval) { /* * XXX 10/30/07: Hmmm, t will get the size of the * string, not the pointer size, is this always * desirable? It seems ``decay'' of string constants * are not handled e.g. when loading a string * address into a register to call a function. * Check by aborting on vr->size != pr->size in * vreg_map_preg() and call fputs("hello", stdout) */ vr = vreg_alloc(NULL, t, NULL, NULL); return context->load = context->curitem = vr; } else { vr = vreg_alloc(NULL, NULL, NULL, NULL); vr->size = backend->get_sizeof_type(curtype, NULL); vr->type = ((struct ty_string *)t->data)->ty; return context->curitem = vr; } } /* * Increment/decrement current item. If we're dealing with postfix * increment/decrement operators, the result will be stored to the * item backing the virtual register we're operating on and then the * inc/dec step is reversed such that the old value of that vreg * will be used in the expression. This is sort of slow and kludgy * but sort of works ok for now */ static void do_inc_dec(struct context *context, int op, struct icode_list *il, struct token *optok, int eval) { struct icode_instr *ii; /* XXXXXXXXXXXXXXXXXXX use eval! */ if (context->curtype.tlist != NULL) { /*int*/ long factor = backend->get_sizeof_elem_type( &context->curtype); struct token *tmp; struct type *tytmp; struct vreg *tmpvr = vreg_alloc(NULL, NULL, NULL, NULL); if (context->curtype.code == TY_VOID) { /* if (! GNUC) { errorfl(optok, "Cannot " "perform pointer arithmetic " "on void pointers (cast to " "`(char *)' instead!)"); } else { */ warningfl(optok, "Pointer arithmetic " "on void pointers is a GNU C " "extension (you should cast " "to `(char *)' instead!)"); } tmp = const_from_value(&factor, /*NULL*/make_basic_type(TY_LONG)); tmpvr->from_const = tmp; tytmp = make_basic_type( /*TY_INT*/ TY_LONG); tmpvr->type = n_xmemdup(tytmp, sizeof *tytmp); tmpvr->size = backend->get_sizeof_type(tytmp, NULL); vreg_faultin(NULL, NULL, tmpvr, il, 0); vreg_faultin_protected(tmpvr, NULL, NULL, context->curitem, il, 0); /* XXX this stuff sucks... changing the tmpvr from int to long * is better for 64bit systems..but the real solution is * probbly to use ptrarit()!? */ if (op == TOK_OP_INCPRE || op == TOK_OP_INCPOST) { ii = icode_make_add(context->curitem, tmpvr); } else { ii = icode_make_sub(context->curitem, tmpvr); } append_icode_list(il, ii); icode_make_store(curfunc, context->curitem, context->curitem, il); if (op == TOK_OP_INCPOST) { ii = icode_make_sub(context->curitem, tmpvr); append_icode_list(il, ii); } else if (op == TOK_OP_DECPOST) { ii = icode_make_add(context->curitem, tmpvr); append_icode_list(il, ii); } } else { if (context->curitem->type->code == TY_BOOL) { /* Ooops */ struct reg *r = NULL; struct reg *oldvalue; /* if (lang == cplusplus) { if (IS_DECREMENT) { error("cannot decrement booolllllllllol"); lol } } */ vreg_faultin(NULL, NULL, context->curitem, il, 0); oldvalue = context->curitem->pregs[0]; if (op == TOK_OP_INCPOST || op == TOK_OP_DECPOST) { /* New value goes into r */ r = ALLOC_GPR(curfunc, 1, il, NULL); icode_make_copyreg(r, oldvalue, context->curitem->type, context->curitem->type, il); /* XXX copyreg shouldn't set used */ vreg_map_preg(context->curitem, oldvalue); } if (op == TOK_OP_INCPRE || op == TOK_OP_INCPOST) { /* ++ always sets to 1 */ ii = icode_make_setreg(r? r: oldvalue, 1); append_icode_list(il, ii); } else { /* decrement - wraps around :( */ struct icode_instr *label; struct icode_instr *endlabel; label = icode_make_label(NULL); endlabel = icode_make_label(NULL); ii = icode_make_cmp(context->curitem, NULL); append_icode_list(il, ii); ii = icode_make_branch(label, INSTR_BR_EQUAL, context->curitem); append_icode_list(il, ii); ii = icode_make_setreg(r? r: oldvalue, 0); append_icode_list(il, ii); ii = icode_make_jump(endlabel); append_icode_list(il, ii); append_icode_list(il, label); ii = icode_make_setreg(r? r: oldvalue, 1); append_icode_list(il, ii); append_icode_list(il, endlabel); } vreg_map_preg(context->curitem, r? r: oldvalue); icode_make_store(curfunc, context->curitem, context->curitem, il); if (r != NULL) { free_preg(r, il, 1, 0); icode_make_copyreg(oldvalue, r, context->curitem->type, context->curitem->type, il); vreg_map_preg(context->curitem, r); } } else { /* * 08/22/07: Allow floating point increment/decrement */ struct vreg *fp_one = NULL; struct token *otok; struct vreg *orig_curitem = context->curitem; int x87_trash = 0; if (is_floating_type(context->curitem->type)) { otok = fp_const_from_ascii("1.0", context->curitem->type->code); fp_one = vreg_alloc(NULL, otok, NULL, NULL); if (!is_x87_trash(context->curitem)) { vreg_faultin(NULL, NULL, context->curitem, il, 0); reg_set_unallocatable( context->curitem->pregs[0]); vreg_faultin(NULL, NULL, fp_one, il, 0); reg_set_allocatable( context->curitem->pregs[0]); } else { x87_trash = 1; } } else { vreg_faultin(NULL, NULL, context->curitem, il, 0); } if (op == TOK_OP_INCPRE || op == TOK_OP_INCPOST) { if (fp_one) { if (x87_trash) { context->curitem = x87_do_binop(fp_one, context->curitem, TOK_OP_PLUS, il); } else { ii = icode_make_add(context->curitem, fp_one); } } else { ii = icode_make_inc(context->curitem); } } else { if (fp_one) { if (x87_trash) { context->curitem = x87_do_binop(fp_one, context->curitem, TOK_OP_MINUS, il); } else { ii = icode_make_sub(context->curitem, fp_one); } } else { ii = icode_make_dec(context->curitem); } } if (!x87_trash) { append_icode_list(il, ii); } if (x87_trash) { vreg_faultin_x87(NULL, NULL, context->curitem, il, 0); vreg_map_preg(orig_curitem, context->curitem->pregs[0]); icode_make_store(curfunc, orig_curitem, orig_curitem, il); } else { icode_make_store(curfunc, context->curitem, context->curitem, il); } if (op == TOK_OP_INCPOST) { if (fp_one) { if (x87_trash) { context->curitem = x87_do_binop(fp_one, context->curitem, TOK_OP_MINUS, il); } else { ii = icode_make_sub(context->curitem, fp_one); } } else { ii = icode_make_dec(context->curitem); } if (!x87_trash) { append_icode_list(il, ii); } } else if (op == TOK_OP_DECPOST) { if (fp_one) { if (x87_trash) { context->curitem = x87_do_binop(fp_one, context->curitem, TOK_OP_PLUS, il); } else { ii = icode_make_add(context->curitem, fp_one); } } else { ii = icode_make_inc(context->curitem); } if (!x87_trash) { append_icode_list(il, ii); } } } } vreg_anonymify(&context->curitem, NULL, NULL, il); context->is_lvalue = 0; } static int unary_to_icode(struct context *context, struct token *op, int extype, struct icode_list *il, int eval) { struct type *curtype = &context->curtype; int opval = *(int *)op->data; int is_x87 = 0; struct icode_instr *ii = NULL; struct vreg *vr; struct reg *r; struct operator *operator; struct icode_instr *label; operator = &operators[LOOKUP_OP2(opval)]; if (is_floating_type(&context->curtype)) { if (opval != TOK_OP_LNEG && opval != TOK_OP_UPLUS && opval != TOK_OP_UMINUS && opval != TOK_OP_ADDR && opval != TOK_OP_INCPOST && opval != TOK_OP_INCPRE && opval != TOK_OP_DECPOST && opval != TOK_OP_DECPRE) { errorfl(op, "`%s' operator applied to floating " "point type", op->ascii); return -1; } if (is_x87_trash(context->curitem)) { is_x87 = 1; } } if (IS_INCDEC_OP(opval)) { if (extype == EXPR_CONST || extype == EXPR_CONSTINIT) { errorfl(op, "Increment/decrement " "operators are not allowed in " "constant %ss", extype == EXPR_CONST? "expression" : "initializer"); return -1; } else if (extype == EXPR_OPTCONSTINIT) { warningfl(op, "Variable array/struct " "initializers are not allowed in ISO " "C90 (only GNU C and C99)"); } else if (extype == EXPR_OPTCONSTARRAYSIZE) { warningfl(op, "Variable-size arrays " "are not allowed in ISO C90 (only GNU " "C and C99)"); } if (is_basic_agg_type(curtype)) { errorfl(op, "Operator cannot be applied to basic " "aggregate types"); return -1; } } switch (opval) { case TOK_OP_DECPRE: case TOK_OP_INCPRE: if (!context->is_lvalue) { errorfl(op, "Pre-%s operator applied " "to something that is not an lvalue", opval == TOK_OP_INCPRE? "increment": "decrement"); return -1; } if (eval) { do_inc_dec(context, opval, il, op, eval); } context->is_lvalue = 0; break; case TOK_OP_INCPOST: case TOK_OP_DECPOST: if (!context->is_lvalue) { errorfl(op, "Post-%s operator applied " "to something that is not an lvalue", opval == TOK_OP_INCPOST? "increment": "decrement"); return -1; } if (eval) { do_inc_dec(context, opval, il, op, eval); } context->is_lvalue = 0; break; case TOK_OP_LNEG: /* * XXX as with the short circuit and relation operators, * use conditional set instructions instead! */ if (eval) { r = ALLOC_GPR(curfunc, backend->get_sizeof_type( make_basic_type(TY_INT), NULL), il, NULL); reg_set_unallocatable(r); /* XXX hmm x87 stuff not encapsulated */ vreg_faultin_x87(NULL, NULL, context->curitem, il, 0); vreg_anonymify(&context->curitem, NULL, NULL, il); reg_set_allocatable(r); ii = icode_make_setreg(r, 0); append_icode_list(il, ii); #if 0 ii = icode_make_cmp(context->curitem, NULL); #endif ii = compare_vreg_with_zero(context->curitem, il); append_icode_list(il, ii); if (!context->curitem->is_multi_reg_obj) { free_pregs_vreg(context->curitem, il, 0, 0); } /* If result zero, set to 1 */ label = icode_make_label(NULL); ii = icode_make_branch(label, INSTR_BR_NEQUAL, context->curitem); append_icode_list(il, ii); if (context->curitem->is_multi_reg_obj) { ii = icode_make_cmp(context->curitem, NULL); append_icode_list(il, ii); ii = icode_make_branch(label, INSTR_BR_NEQUAL, context->curitem); append_icode_list(il, ii); } ii = icode_make_setreg(r, 1); append_icode_list(il, ii); append_icode_list(il, label); } context->is_lvalue = 0; context->curitem = vreg_alloc(NULL, NULL, NULL, NULL); if (eval) { vreg_map_preg(context->curitem, r); } context->curitem->type = make_basic_type(TY_INT); copy_type(&context->curtype, context->curitem->type, 0); context->curitem->size = backend->get_sizeof_type(&context->curtype, NULL); ii = NULL; break; case TOK_OP_UPLUS: case TOK_OP_UMINUS: /* * Operand must have arithmetic type (ie. integral or * floating point) */ if (!is_arithmetic_type(curtype)) { errorfl(op, "Operand of `%s' operator must have " "arithmetic type", operator->name); return -1; } if (opval == TOK_OP_UPLUS) { /* * This operator does not do anything except * for causing integral promotion - it only * exists for symmetry with unary ``-'' */ struct type *tytmp; struct type *oldtype = context->curitem->type; if (is_x87 && eval) { context->curitem = x87_do_unop(context->curitem, TOK_OP_UPLUS, il); tytmp = oldtype; } else { tytmp = promote(&context->curitem, NULL, opval, op, eval? (void *)il: (void *)NULL, eval); if (tytmp == NULL) { return -1; } vreg_anonymify(&context->curitem,NULL,NULL,il); } if (tytmp != oldtype) { copy_type(&context->curtype, tytmp, 0); } } else { /* Minus */ if (is_x87 && eval) { context->curitem = x87_do_unop(context->curitem, TOK_OP_UMINUS, il); } else { if (eval) { vreg_faultin(NULL, NULL, context->curitem,il,0); } vreg_anonymify(&context->curitem,NULL,NULL,il); pro_mote(&context->curitem, il, eval); if (eval) { ii = icode_make_neg(context->curitem); } } } break; case TOK_OP_BNEG: if (!is_integral_type(curtype)) { errorfl(op, "Operand of `%s' operator must have " "arithmetic type", operator->name); return -1; } if (eval) { vreg_faultin(NULL, NULL, context->curitem, il, 0); } vreg_anonymify(&context->curitem, NULL, NULL, il); pro_mote(&context->curitem, il, eval); if (eval) { ii = icode_make_not(context->curitem); context->curitem = n_xmemdup(context->curitem, sizeof *context->curitem); vreg_map_preg(context->curitem, context->curitem->pregs[0]); if (context->curitem->is_multi_reg_obj) { vreg_map_preg2(context->curitem, context->curitem->pregs[1]); } } context->is_lvalue = 0; break; case TOK_OP_ADDR: if (!context->is_lvalue) { errorfl(op, "Address-of operator applied " "to something that is not an lvalue"); return -1; } else if (context->curtype.storage == TOK_KEY_REGISTER) { warningfl(op, "Address-of operator applied to " "`register' variable"); } else if (context->curtype.tlist != NULL && context->curtype.tlist->type == TN_FUNCTION) { context->is_lvalue = 0; break; } context->is_lvalue = 0; context->var_lvalue = NULL; if (eval) { struct vreg *vr2 = NULL; if (context->curitem->parent) { vr2 = get_parent_struct(context-> curitem); if (vr2->from_ptr) { vreg_faultin(NULL, NULL, vr2->from_ptr, il, 0); } } ii = icode_make_addrof(context->curitem, il); append_icode_list(il, ii); if (vr2 && vr2->from_ptr) { free_preg(vr2->from_ptr->pregs[0], il, 0, 0); } } vr = vreg_alloc(NULL, NULL, NULL, NULL); if (eval) { vreg_map_preg(vr, ii->dat); } vr->type = addrofify_type(&context->curtype); vr->size = backend-> get_sizeof_type(vr->type, NULL); context->curtype.tlist = /*tn*/vr->type->tlist; ii = NULL; context->curitem = vr; break; case TOK_OP_DEREF: if (curtype->tlist == NULL) { errorfl(op, "Attempt to dereference something that is not " "a pointer"); return -1; } if (curtype->tlist->type == TN_FUNCTION || (curtype->tlist->next && curtype->tlist->next->type == TN_FUNCTION)) { /* (*f)(); is the same as f(); */ break; } if (curtype->code == TY_VOID && curtype->tlist->next == NULL) { /* XXX This actually isn't true */ errorfl(op, "Cannot dereference void pointers"); return -1; } if (curtype->incomplete || (curtype->tstruc && curtype->tstruc->incomplete && curtype->tlist->next == NULL)) { errorfl(op, "Cannot dereference pointer to incomplete type"); return -1; } /* * XXX we need to pay more attention to storage class * stuff when modifying types... This was just made * aparent by an error message about taking the address * of * register char *p; &p[i]; * ... here p[i] shouldn't be register-specified! */ context->curtype.storage = 0; copy_tlist(&context->curtype.tlist, context->curtype.tlist->next); context->is_lvalue = 1; context->var_lvalue = NULL; if (eval) { struct reg *r; /* Fault in pointer */ r = vreg_faultin(NULL, NULL, context->curitem, il, 0); ii = icode_make_indir(r); append_icode_list(il, ii); context->indir = 1; ii = NULL; } /* * If the parent pointer also came from a pointer, it must * be anonymified to avoid long chains of indirection */ if (context->curitem->from_ptr && eval) { vreg_anonymify(&context->curitem, NULL, NULL, il); } context->curitem = vreg_alloc(NULL, NULL, context->curitem, NULL); break; default: puts("WHAT??????????????"); printf("%d\n", opval); abort(); } if (ii != NULL && eval) { append_icode_list(il, ii); } return 0; } static int do_cast(struct context *context, struct token *cast, struct icode_list *il, int eval) { struct decl *d = cast->data; struct type *ty = d->dtype; struct type *curtype = &context->curtype; int is_ptr_cast = 0; if (curtype->tlist != NULL) { /* Possibly dangerous cast */ if (ty->tlist == NULL && ty->code != TY_VOID) { if (ty->code == TY_FLOAT || ty->code == TY_DOUBLE || ty->code == TY_LDOUBLE) { errorfl(cast, "Invalid cast of pointer " "to floating point type"); return 1; } else if (ty->code != TY_ULONG && ty->code != TY_ULLONG && pedanticflag) { warningfl(cast, "Dangerous cast of pointer to" " %s type, use " "`unsigned long' to improve " "portability", ty->code == TY_LONG? "signed long" : "small integral"); } } else if (ty->tlist != NULL) { /* IMPORTANT: Depends on type order in token.h! */ is_ptr_cast = 1; if (ty->code > curtype->code && ty->code != TY_VOID && curtype->code != TY_VOID && (IS_CHAR(ty->code) /* XXX kludge ... */ != IS_CHAR(curtype->code) || IS_SHORT(ty->code) != IS_SHORT(curtype->code) || IS_INT(ty->code) != IS_INT(curtype->code) || IS_LONG(ty->code) != IS_LONG(curtype->code) || IS_LLONG(ty->code) != IS_LLONG(curtype->code) || IS_FLOATING(ty->code))) { if (pedanticflag) { warningfl(cast, "Cast to pointer type with possibly " "higher alignment constraints"); } } } } if (eval) { context->curitem = backend->icode_make_cast(context->curitem, ty, il); } else { context->curitem = n_xmemdup(context->curitem, sizeof *context->curitem); context->curitem->type = ty; context->curitem->size = backend->get_sizeof_type(ty, NULL); } context->is_lvalue = 1; copy_type(curtype, ty, 0); return 0; } static void do_comp_literal(struct context *context, struct token *item, struct icode_list *il, int eval) { struct comp_literal *comp_lit = item->data; struct initializer *init = comp_lit->init; struct type *ty = comp_lit->struct_type; struct vreg *vr; if (curscope == &global_scope) { /* ?@?@?@? WHAT ,this is never executed?!?!?! XXXXX */ vr = vreg_alloc(NULL,NULL,NULL,NULL); vreg_set_new_type(vr, ty); unimpl(); } else { if (eval) { vr = vreg_stack_alloc(ty, il, 1, init); } else { vr = vreg_alloc(NULL,NULL,NULL,NULL); vreg_set_new_type(vr, ty); } } copy_type(&context->curtype, ty, 0); /* * Yes, you heard right, compound literals are lvalues! That is to * make it possible to take their address (as a side effect, one * can also assign to them) */ context->is_lvalue = 1; context->curitem = vr; context->load = vr; } /* * Function that reads what I like to refer to as ``sub-expression''. * A sub-expression is just an argument to the binary and ternary * operators, and it includes all possible unary/prefix/postfix * operators (bitwise/logical negation, pre/post-inc/dec-rement, * function call, indirection, etc.) * This is typically an identifier or a constant, possibly including * operators, but it may also be a parenthesized expression, which * in turn consists of one or more sub-expressions. * * In other words, a ``sub-expression'' is what the standard refers to * as ``unary expression'' * * Examples: * +foo + *bar-- * ^^^^ ^^^^^^ * *(x + 5) - foo? bar: baz * ^ ^ * ^^^^^^^^ ^^^ ^^^ ^^^ */ struct s_expr * get_sub_expr(struct token **tok, int delim, int delim2, int extype) { struct token *t; struct token *meat = NULL; struct token *left; struct token *right; struct token *is_sizeof = NULL; struct token *continue_at = NULL; struct token *endp = NULL; struct expr *ex = NULL; struct expr *is_expr = NULL; struct decl **decv; int is_func_call = 0; struct token *operators[128]; /* XXX */ struct token *operators_f[128]; void *res = NULL; struct icode_instr *ii; struct icode_list *il; struct s_expr *ret = NULL; int op; int i; int j = 0; t = *tok; il = alloc_icode_list(); il->head = il->tail = NULL; /* * Read prefix or unary operators, if any, and the * identifier/constant/parenthesized expression * constituting this sub-expression */ for (t = *tok, i = 0; t != NULL; t = t->next) { ii = NULL; if (expr_ends(t, delim, delim2)) { if (t == *tok) { errorfl(t, "Parse error at `%s'", t->ascii); return NULL; } endp = t; goto out; } else if (t->type == TOK_PAREN_OPEN) { /* Must be cast or parenthesized expression */ if (t->next == NULL) { errorfl(t, "Unexpected end of file"); return NULL; } /* * 10/01/07: This was missing the check for an * identifier. Thus * * typepdef int foo; * { * int foo; * (foo); * * ... the last line was interpreted as a cast * expression. Not sure if this is fully correct * now */ if (IS_TYPE(t->next) && (t->next->type != TOK_IDENTIFIER || lookup_symbol(curscope, t->next->data, 1) == NULL)) { /* Is cast */ struct token *casttok = t; struct type *ty; operators[i++] = t; t = t->next; decv = parse_decl(&t, DECL_CAST); if (decv == NULL) { return NULL; } ty = decv[0]->dtype; casttok->data = decv[0]; free(decv); if (t->next && t->next->type == TOK_COMP_OPEN && is_basic_agg_type(ty)) { struct initializer *init; int init_type; struct comp_literal *comp_lit; if (curscope == &global_scope) { init_type = EXPR_CONSTINIT; } else { init_type = EXPR_OPTCONSTINIT; } /* * Must be compound literal */ (void) next_token(&t); meat = casttok; init = get_init_expr(&t, init_type, ty, 1, 1); if (init == NULL) { return NULL; } comp_lit = n_xmalloc(sizeof *comp_lit); comp_lit->init = init; comp_lit->struct_type = ty; meat->type = TOK_COMP_LITERAL; meat->data = comp_lit; operators[--i] = NULL; if (ty->tlist != NULL && ty->tlist->type == TN_ARRAY_OF && ty->tlist->arrarg->const_value == NULL) { /* * No size specified - update * using initializer info! */ init_to_array_size(ty, init); } break; } else { casttok->type = TOK_OP_CAST; } } else { /* Is parenthesized expression */ t = t->next; ex = parse_expr(&t, TOK_PAREN_CLOSE, 0, extype, 0); if (ex == NULL) { return NULL; } is_expr = ex; continue_at = t; break; } } else if (t->type == TOK_OPERATOR) { /* Must be unary prefix operator */ if ((op = ambig_to_unary(t, 1)) == -1) { return NULL; } *(int *)t->data = op; operators[i++] = t; } else if (t->type == TOK_KEY_SIZEOF || t->type == TOK_KEY_ALIGNOF) { if (do_sizeof(t, &endp, &is_sizeof, extype, delim, delim2) == -1) { return NULL; } operators[i++] = t; break; } else if (t->type == TOK_IDENTIFIER) { if (t->next && t->next->type == TOK_PAREN_OPEN) { /* Is function call */ is_func_call = 1; } meat = t; break; } else if (t->type == TOK_STRING_LITERAL || IS_CONSTANT(t->type)) { meat = t; break; } else { errorfl(t, "Parse error at `%s'(#1)", t->ascii); abort(); } } if (is_expr) { right = continue_at; if (next_token(&right) != 0) { return NULL; } } else if (is_func_call) { right = meat->next; } else if (!is_sizeof) { if (meat != *tok) { left = meat->prev; } else { left = NULL; } if (meat->type == TOK_COMP_LITERAL) { right = t; } else { right = meat; if (next_token(&right) != 0) { return NULL; } } } else { right = NULL; } /* * Compare prefix with postfix operators and * bind them accordingly */ if (i > 0) { left = operators[--i]; } else { left = NULL; } j = 0; for (;;) { if (right != NULL) { endp = right; if (expr_ends(right, delim, delim2)) { right = NULL; } } /* * Postfix operators always have higher precedence * than prefix and unary operators */ res = NULL; if (right != NULL) { if (right->type == TOK_PAREN_OPEN) { struct token *old_right = right; struct fcall_data *fres; /* * Must be function call. This isn't allowed * in constant expressions if we have a real * function, but there are some builtins, * such as __builtin_offsetof(), which yield * a constant value and should be computable * in constant expressions. Thus we check if * that's the case here */ res = do_func_call(meat, &right, 1); if (res == NULL) { return NULL; } fres = res; if (fres->builtin != NULL && fres->builtin->type == BUILTIN_OFFSETOF) { goto const_ok; } if (extype == EXPR_CONST || extype == EXPR_CONSTINIT) { errorfl(right, "Function calls are not " "allowed in constant %ss", extype == EXPR_CONST? "expression": "initializer"); return NULL; } else if (extype == EXPR_OPTCONSTINIT) { warningfl(right, "Variable array/struct " "initializers are not allowed in ISO " "C90 (only GNU C and C99)"); } else if (extype == EXPR_OPTCONSTARRAYSIZE) { warningfl(right, "Variable-size arrays " "are not allowed in ISO C90 (only GNU " "C and C99)"); } const_ok: if (fres->builtin && fres->builtin->type == BUILTIN_EXPECT) { if (is_expr) { errorfl(right, "Parse error at `%s'", right->ascii); return NULL; } is_expr = fres->builtin->args[0]; } else { operators_f[j] = old_right; operators_f[j++]->data = res; } } else if (right->type == TOK_OPERATOR) { /* * Must be postfix operator or end of * sub-expression */ op = ambig_to_unary(right, 0); if (IS_UNARY(op)) { /* XXX handle op */ operators_f[j++] = right; *(int *)right->data = op; } else if (op == TOK_OP_STRUMEMB || op == TOK_OP_STRUPMEMB) { operators_f[j] = right; right->type = op; if (next_token(&right) != 0) { return NULL; } if (right->type != TOK_IDENTIFIER) { errorfl(right, "Structure indirection" " operator not " "followed by structure" " member name"); return NULL; } operators_f[j++]->data = right; } else { right = NULL; } } else if (right->type == TOK_ARRAY_OPEN) { operators_f[j] = right; if (next_token(&right) != 0) { return NULL; } res = parse_expr(&right, TOK_ARRAY_CLOSE, 0, 0, 1); if (res == NULL) { return NULL; } operators_f[j++]->data = res; } else { right = NULL; } if (right) right = right->next; } else if (left != NULL) { operators_f[j++] = left; if (i > 0) { left = operators[--i]; } else { left = NULL; } } else { break; } } operators_f[j++] = NULL; out: *tok = endp; if (meat == NULL && is_expr == NULL && is_sizeof == NULL) { errorfl(endp, "Parse error at `%s'", endp->ascii); } ret = alloc_s_expr(); memcpy(ret->operators, operators_f, j * sizeof *operators_f); ret->is_expr = is_expr; ret->meat = meat; ret->is_sizeof = is_sizeof; return ret; } struct vreg * s_expr_to_icode( struct s_expr *s, struct vreg *lvalue, struct icode_list *il, int eval) { struct context context; struct icode_instr *ii; struct fcall_data *fcall; struct type_node *funcnode = NULL; struct decl *is_func_call = NULL; struct type *fty; int i; context.curtype.code = 0; context.var_lvalue = NULL; context.is_lvalue = 0; context.curitem = NULL; context.indir = 0; context.load = NULL; s->only_load = 1; if (s->is_expr) { context.curitem = expr_to_icode(s->is_expr, lvalue, il, 0, eval); if (context.curitem == NULL) { return NULL; } copy_type(&context.curtype, context.curitem->type, 0); /* * The property of being an lvalue is kept across parenthses; * int x; &(x); * and * int *p; ... ++(*p); * both have to work */ if (s->is_expr->op == 0) { /* data may be NULL if this is a statement-as-expr */ if (s->is_expr->data != NULL) { context.is_lvalue = s->is_expr->data->is_lvalue; context.var_lvalue = s->is_expr->data->var_lvalue; } } } else if (s->meat) { if (s->meat->type == TOK_IDENTIFIER) { struct decl **is_func_call_p = NULL; int err; if (s->operators[0] != NULL && s->operators[0]->type == TOK_PAREN_OPEN) { is_func_call_p = &is_func_call; } ii = do_identifier(&context, s->meat, s->extype, is_func_call_p, &err, eval); if (ii == NULL) { if (err) { return NULL; } } else { append_icode_list(il, ii); } } else if (s->meat->type == TOK_STRING_LITERAL) { (void) do_string(&context, s->meat, eval); } else if (IS_CONSTANT(s->meat->type)) { do_constant(&context, s->meat, eval); } else if (s->meat->type == TOK_COMP_LITERAL) { do_comp_literal(&context, s->meat, il, eval); } else { puts("BUG: bad item in sub-expression"); exit(EXIT_FAILURE); } } else if (s->is_sizeof) { ; } else { puts("BUG: sub-expression has no body??"); exit(EXIT_FAILURE); } s->load = context.load; if (s->extype == EXPR_CONST || s->extype == EXPR_CONSTINIT || s->extype == EXPR_OPTCONSTINIT || s->extype == EXPR_OPTCONSTARRAYSIZE) { return s->res = context.curitem; } for (i = 0; s->operators[i] != NULL; ++i) { s->only_load = 0; switch (s->operators[i]->type) { case TOK_OP_STRUPMEMB: case TOK_OP_STRUMEMB: if (do_struct_member( s->operators[i], &context, il, eval) != 0) { return NULL; } context.curitem->struct_ret = 0; break; case TOK_OPERATOR: if (unary_to_icode(&context, s->operators[i], s->extype, il, eval) != 0) { return NULL; } break; case TOK_KEY_SIZEOF: case TOK_KEY_ALIGNOF: case TOK_SIZEOF_VLA_TYPE: case TOK_SIZEOF_VLA_EXPR: if (s->operators[i]->type == TOK_SIZEOF_VLA_TYPE) { struct type *ty; unimpl(); ty = s->operators[i]->data; vla_decl_to_icode(ty, il); } else if (s->operators[i]->type == TOK_SIZEOF_VLA_EXPR) { struct s_expr *s_ex; struct vreg *vr; s_ex = s->operators[i]->data; vr = s_expr_to_icode(s_ex, NULL, il, 1); if (vr == NULL) { return NULL; } vr = backend->get_sizeof_vla_type(vr->type, il); context.curitem = vr; } else { context.curitem = s->operators[i]->data; } context.curitem->type = backend->get_size_t(); copy_type(&context.curtype, context.curitem->type, 0); context.curitem->size = backend-> get_sizeof_type(context.curitem->type, NULL); break; case TOK_PAREN_OPEN: /* Function call */ fcall = s->operators[i]->data; fcall->lvalue = lvalue; if ((fcall->callto = context.var_lvalue) == NULL && fcall->builtin == NULL) { fcall->calltovr = context.curitem; if (fcall->calltovr == NULL) { /* XXX error message?! */ return NULL; } } else if (fcall->builtin == NULL) { fcall->calltovr = context.curitem; } fty = NULL; if (fcall->callto != NULL) { fty = fcall->callto->dtype; } else if (fcall->calltovr != NULL) { fty = fcall->calltovr->type; } if (fty && fty->tlist == NULL) { errorfl(s->operators[i], "Call to something that is not " "a function"); return NULL; } if (fty != NULL) { fcall->functype = NULL; if (fty->tlist->type == TN_FUNCTION) { fcall->functype = fty->tlist->tfunc; funcnode = fty->tlist; } else if (fty->tlist->next != NULL && fty->tlist->next->type == TN_FUNCTION) { fcall->functype = fty->tlist->next->tfunc; funcnode = fty->tlist->next; } if (fcall->functype == NULL) { errorfl(s->operators[i], "Call to something that is not " "a function"); return NULL; } } if (fcall->builtin != NULL) { context.curitem = fcall->builtin-> builtin->toicode(fcall, il); } else { /* * 08/16/07: Check whether we have to create * an anonymous stack struct to hold the * result of the function call. This is * necessary if the result is not directly * assigned to a variable, but e.g. passed * to another function, or some operator is * applied to it; * * struct foo func(); * ... * int x = func().x; * ... * void func2(struct foo f); * ... * func2(func()); */ int needanon = 0; if (s->operators[i + 1] != NULL && (fty->code == TY_STRUCT || fty->code == TY_UNION) && funcnode->next == NULL) { /* Need anonymous struct */ needanon = 1; } fcall->need_anon = needanon; context.curitem = fcall_to_icode(fcall, il, s->meat, eval); } if (context.curitem == NULL) { return NULL; } copy_type(&context.curtype, context.curitem->type, 0); break; case TOK_OP_CAST: if (do_cast(&context, s->operators[i], il, eval)) { return NULL; } break; case TOK_ARRAY_OPEN: if (do_subscript(&context, s->operators[i], il, eval) != 0) { return NULL; } break; } } s->var_lvalue = context.var_lvalue; s->is_lvalue = context.is_lvalue; s->type = n_xmalloc(sizeof(struct type)); copy_type(s->type, &context.curtype, 1); return s->res = context.curitem; }