/* * 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. */ #include "icode.h" #include #include #include #include #include #include #include "misc.h" #include "token.h" #include "control.h" #include "decl.h" #include "type.h" #include "debug.h" #include "defs.h" #include "attribute.h" #include "expr.h" #include "error.h" #include "subexpr.h" #include "symlist.h" #include "reg.h" #include "typemap.h" #include "functions.h" #include "backend.h" /* for get_sizeof() */ #include "scope.h" #include "x87_nonsense.h" #include "inlineasm.h" #include "n_libc.h" int optimizing; static int doing_stmtexpr; #if 0 sparc mov arg1, %o0 mov arg2, %o1 ... page 297 #endif struct vreg * fcall_to_icode( struct fcall_data *fcall, struct icode_list *il, struct token *t, int eval) { struct expr *ex; struct vreg *args[128]; struct vreg **argp = NULL; struct vreg *ret = NULL; struct type *fty; struct ty_func *fdecl; struct sym_entry *se; int i = 0; int j; int nstack = sizeof args / sizeof args[0]; int alloc = 0; /* XXX bad */ if (fcall->callto != NULL) { fty = fcall->callto->dtype; } else { fty = fcall->calltovr->type; } fdecl = fcall->functype; for (ex = fcall->args; ex != NULL; ex = ex->next) { if (i == nstack - 1) { alloc = nstack * 2; argp = n_xmalloc(alloc * sizeof *argp); memcpy(argp, args, nstack * sizeof *argp); } else if (i >= nstack) { if (i == alloc - 1) { alloc *= 2; argp = n_xrealloc(argp, alloc * sizeof *argp); } } if (ex->op == 0 && !ex->data->is_expr && ex->data->only_load) { /* * Don't need to generate anything */ if (argp != NULL) argp[i++] = ex->data->res; else args[i++] = ex->data->res; continue; } if ((ex->res = expr_to_icode(ex, NULL, il, 0, eval)) == NULL) { /* XXX free stuff */ if (i > nstack) free(argp); return NULL; } if (argp != NULL) argp[i++] = ex->res; else args[i++] = ex->res; } if (argp != NULL) argp[i] = NULL; else args[i] = NULL; /* Check correctness of arguments */ if (fdecl->nargs != -1 && fdecl->nargs != fcall->nargs) { if (!fdecl->variadic || fdecl->nargs - 1 > fcall->nargs) { if (fdecl->was_just_declared) { warningfl(t, "Call to function `%s' with wrong number of aguments", fty->name? fty->name: ""); } else { errorfl(t, "Call to function `%s' with wrong number of aguments", fty->name? fty->name: ""); return NULL; } } } if (fdecl->scope != NULL) { se = fdecl->scope->slist; } else { se = NULL; } /*if (fcall->calltovr == NULL)*/ /* XXX */ for (j = 0; j < i; ++j) { struct vreg *arg = argp? argp[j]: args[j]; if (fdecl->variadic) { #if 0 if ((arg->type->code == TY_STRUCT || arg->type->code == TY_UNION) && arg->type->tlist == NULL) { errorfl(t, "Passing aggregate type as argument %d to variadic function", j+1); return NULL; } #endif if (se != NULL && se->dec == fdecl->lastarg) { se = NULL; } } else if (se != NULL) { struct type *param_type = NULL; if (se->dec->dtype->code == TY_UNION && se->dec->dtype->tlist == NULL) { /* * This may be a paramter with multiple * possible argument types */ struct attrib *attr = se->dec->dtype->attributes; for (; attr != NULL; attr = attr->next) { if (attr->code == ATTRS_TRANSPARENT_UNION) { /* Yes! */ param_type = get_transparent_union_type(t, se->dec->dtype, arg); if (param_type == NULL) { /* No match */ return NULL; } break; } } } if (param_type == NULL) { /* * Not transparent union - check type */ param_type = se->dec->dtype; if (fdecl->type == FDTYPE_KR) { if (check_types_assign(t, se->dec->dtype, arg, 1, 1) != 0) { /* * There is a mismatch here, but * we only warn instead of * erroring because most other * compilers probably accept this * code without warning because * it is a K&R function (e.g. Ruby * declares a ``void *'' parameter * but passes a ``long'' */ warningfl(t, "Incompatible " "argument type. This " "may be an undetected " "bug due to a K&R " "declaration instead " "of an ANSI prototype."); } } else { if (check_types_assign(t, se->dec->dtype, arg, 1, 0) != 0) { return NULL; } } } if (is_arithmetic_type(param_type) && eval) { /* * Make parameter agree with argument */ arg = backend-> icode_make_cast(arg,param_type,il); if (argp) { argp[j] = arg; } else { args[j] = arg; } } se = se->next; } else { /* Must be implicitly declared */ ; } } if (fdecl->nargs == -1) { /* XXX finish this .................... */ } if (eval) { ret = backend->icode_make_fcall(fcall, argp? argp: args, i, il); } else { ret = vreg_alloc(NULL,NULL,NULL,NULL); ret->type = n_xmemdup(fcall->calltovr->type, sizeof(struct type)); functype_to_rettype(ret->type); if (ret->type->code != TY_VOID || ret->type->tlist != NULL) { ret->size = backend->get_sizeof_type(ret->type,NULL); } else { ret->size = 0; } } if (i > nstack) free(argp); return ret; } /* * Promote vr if necessary */ int pro_mote(struct vreg **vr, struct icode_list *il, int eval) { struct type *ty = (*vr)->type; if (il != NULL && eval) { if (is_x87_trash(*vr)) { *vr = x87_anonymify(*vr, il); } else { vreg_anonymify(vr, NULL, NULL, il); } } if (ty->tlist != NULL) { return 0; } if (IS_CHAR(ty->code) || IS_SHORT(ty->code)) { /* Need promotion! */ ty = make_basic_type(TY_INT); if (il != NULL && eval) { *vr = backend->icode_make_cast(*vr, ty, il); } (*vr)->type = ty; (*vr)->size = backend->get_sizeof_type(ty, NULL); return 1; } return 0; } /* * Perform usual arithmetic conversions */ static int convert_operands( struct vreg **left, struct vreg **right, struct icode_list *left_il, struct icode_list *right_il, int op0, struct token *optok, int eval) { struct type *lt = (*left)->type; struct type *rt = (*right)->type; struct operator *op; int larit; int rarit; larit = is_arithmetic_type(lt); rarit = is_arithmetic_type(rt); op = &operators[LOOKUP_OP2(op0)]; if (larit && rarit) { /* * Both are arithmetic types and may need usual * arithmetic conversions */ pro_mote(left, left_il, eval); pro_mote(right, right_il, eval); lt = (*left)->type; rt = (*right)->type; if ((op0 != TOK_OP_BSHL && op0 != TOK_OP_BSHR) /*|| backend->arch == ARCH_X868 */ /* :-( */) { if (rt->code > lt->code) { if (eval) { *left = backend-> icode_make_cast(*left, rt, left_il); } else { (*left)->type = rt; } } else if (lt->code > rt->code) { if (eval) { *right = backend-> icode_make_cast(*right, lt, right_il); } else { (*right)->type = lt; } } } return 0; } else if (lt->tlist == NULL && (lt->code == TY_STRUCT || lt->code == TY_UNION)) { errorfl(optok, "Cannot use %s type with `%s' operator", lt->code == TY_STRUCT? "structure": "union", op->name); return 1; } else if (rt->tlist == NULL && (rt->code == TY_STRUCT || rt->code == TY_UNION)) { errorfl(optok, "Cannot use %s type with `%s' operator", rt->code == TY_STRUCT? "structure": "union", op->name); return 1; } else { /* At least one side is a pointer or array */ if (lt->tlist == NULL || rt->tlist == NULL) { } else { } } return 1; } /* * XXX There is some duplicated stuff in promote() and expr_to_icode(), * which should be cleaned up. And this is improprely named because it * doesn't only do promotions but also usual arithmetic conversion. And * in some cases it does it wrongly * * Should be fully replaced with convert_operands() and pro_mote() */ struct type * promote(struct vreg **left, struct vreg **right, int op0, struct token *optok, struct icode_list *il, int eval) { struct type *lt = (*left)->type; struct type *rt = right? (void *)(*right)->type: (void *)NULL; struct type *ret; struct type *towhat = NULL; struct token *lfrom_const = (*left)->from_const; struct token *rfrom_const = right? (void *)(*right)->from_const: (void *)NULL; struct type_node *tnl; struct type_node *tnr; struct operator *op; int is_void = 0; if (il != NULL) { if (is_x87_trash(*left)) { *left = x87_anonymify(*left, il); } else { vreg_anonymify(left, NULL, NULL, il); } } if (op0 == 0) { op = NULL; /* XXX */ } else { op = &operators[LOOKUP_OP2(op0)]; } if (right == NULL) { /* * Promoting argument to unary operator or result of * conditional operator */ towhat = (*left)->type; if (lt->tlist != NULL) { if (op0 == TOK_OP_LNEG) { } } else if (IS_CHAR(lt->code) || IS_SHORT(lt->code)) { towhat = make_basic_type(TY_INT); if (il != NULL) { if (eval) { *left = backend->icode_make_cast(*left, towhat, il); } else { (*left)->type = towhat; } } } return towhat; } if (lt->tlist == NULL || rt->tlist == NULL) { if (lt->tlist != rt->tlist) { /* * Basic type used with pointer type - * only valid for some operations */ struct type *pointer; struct vreg *basic_vreg; struct type *basic; struct token *basic_from_const; if (lt->tlist) { pointer = lt; basic = rt; basic_vreg = *right; basic_from_const = rfrom_const; } else { pointer = rt; basic = lt; basic_vreg = *left; basic_from_const = lfrom_const; } if (basic->code == TY_STRUCT || basic->code == TY_UNION) { errorfl(optok, "Cannot use " "%s type with `%s' " "operator", basic->code == TY_STRUCT? "structure": "union", op? op->name: ""); return NULL; } else if (IS_FLOATING(basic->code)) { errorfl(optok, "Cannot use " "pointer types with floating " "point types"); return NULL; } else if (op0 == TOK_OP_PLUS || op0 == TOK_OP_MINUS) { /* Probably OK - pointer arithmetic */ if (pointer->code == TY_VOID) { /* if (std != GNU) { 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!)"); #if 0 return NULL; #endif } } else if ((op0 == TOK_OP_LEQU || op0 == TOK_OP_LNEQU) && basic_vreg->is_nullptr_const) { ; } else if (op0 == TOK_OP_COMMA) { ; } else { errorfl(optok, "Cannot use " "pointer type with `%s' operator " "and basic type", op? op->name: ""); return NULL; } if (pointer->tlist->type == TN_ARRAY_OF) { size_t newsize; pointer = n_xmemdup(pointer, sizeof *pointer); copy_tlist(&pointer->tlist, pointer->tlist); pointer->tlist->type = TN_POINTER_TO; newsize = backend-> get_sizeof_type(pointer, NULL); } return pointer; } else { /* Both are basic types */ if (convert_operands(left, right, il, il, op0, optok, eval) != 0) { return NULL; } else { return (*left)->type; } } } tnl = lt->tlist; tnr = rt->tlist; if (tnl->type == TN_FUNCTION && tnr->type == TN_POINTER_TO) { tnr = tnr->next; } else if (tnr->type == TN_FUNCTION && tnl->type == TN_POINTER_TO) { tnl = tnl->next; } /* Dealing with two pointer types */ if ((lt->code == TY_VOID && tnl->next == NULL) || (rt->code == TY_VOID && tnr->next == NULL)) { /* * 08/03/07: Avoid error for * * char *p; * if (&p == (void *)bla) { * * May not be 100% correct */ is_void = 1; } else { for (; tnl != NULL && tnr != NULL; tnl = tnl->next, tnr = tnr->next) { if (tnl->type != tnr->type || (tnl->type == TN_ARRAY_OF && tnl->arrarg_const != tnr->arrarg_const)) { if (tnl != lt->tlist || (tnl->type != TN_ARRAY_OF && tnr->type != TN_ARRAY_OF && tnl->type != TN_VARARRAY_OF && tnr->type != TN_VARARRAY_OF)) { errorfl(optok, "Incompatible pointer types in " "expression"); return NULL; } } } } if (!is_void) { if (tnl != tnr && !(*right)->is_nullptr_const && !(*left)->is_nullptr_const) { /* * XXX this fails for function vs void pointers!!!! * !!!!!!!!!!!!!!!!!!!!! */ errorfl(optok, "Incompatible types in expression"); return NULL; } } /* XXX this is complete nonsense */ if (op0 == TOK_OP_LEQU || op0 == TOK_OP_LNEQU || op0 == TOK_OP_GREAT || op0 == TOK_OP_SMALL || op0 == TOK_OP_GREATEQ || op0 == TOK_OP_SMALLEQ) { /* * The resulting type of relational operators applied to * two pointer values is of type ``int'' */ ret = make_basic_type(TY_INT); ret = n_xmemdup(ret, sizeof *ret); return ret; } else if (op0 == TOK_OP_MINUS) { ret = make_basic_type(TY_INT); ret = n_xmemdup(ret, sizeof *ret); return ret; } return lt; } /* * Perform pointer arithmetic on lres or rres (result is returned.) For add * this means: mul n, elemsize; add ptr, n * For sub this needs to do one of two separate things: Either subtract a * pointer from another pointer and calculate the number of elements between * them (sub ptr1, ptr2; div result, elemsize), or just subtract an element * count (mul n, elemsize; sub ptr, n) * * As a simple optimization, the scaling is done using shift left/shift right * rather than mul/div instructions if the element size is a power of two */ static void ptrarit( struct vreg **lres0, struct vreg **rres0, struct icode_list *ilp, int op, int eval) { struct vreg *toscale; struct vreg *addto; struct vreg *lres = *lres0; struct vreg *rres = *rres0; struct type *ty; struct icode_instr *ii; int factor; int both_ptr; struct vreg *tmpvr; both_ptr = lres->type->tlist != NULL && rres->type->tlist != NULL; reg_set_unallocatable(rres->pregs[0]); reg_set_unallocatable(lres->pregs[0]); if (lres->type->tlist) { toscale = rres; addto = lres; vreg_anonymify(&addto, NULL, NULL, ilp); #if 0 addto = vreg_disconnect(addto); #endif *lres0 = lres = addto; } else { toscale = lres; addto = rres; vreg_anonymify(&addto, NULL, NULL, ilp); #if 0 addto = vreg_disconnect(addto); #endif *rres0 = rres = addto; } reg_set_allocatable(rres->pregs[0]); reg_set_allocatable(lres->pregs[0]); ty = addto->type; factor = backend-> get_sizeof_elem_type(ty); if (factor > 1) { /* * Scaling something that may be from a variable - * register is not cached value anymore afterwards */ tmpvr = vreg_alloc(NULL, NULL, NULL, make_basic_type(TY_INT)); if (toscale == lres) { vreg_anonymify(&toscale, NULL, NULL, ilp); pro_mote(&toscale, ilp, eval); *lres0 = lres = toscale; } else { vreg_anonymify(&toscale, NULL, NULL, ilp); pro_mote(&toscale, ilp, eval); *rres0 = rres = toscale; } if ((factor & (factor - 1)) == 0) { /* * Scaling by power of two - we can shift! */ int shift_by = 0; while (factor /= 2) { ++shift_by; } tmpvr->from_const = const_from_value(&shift_by, NULL); if (op == TOK_OP_PLUS) { ii = icode_make_shl(toscale, tmpvr); append_icode_list(ilp, ii); } if (op == TOK_OP_PLUS) { ii = icode_make_add(addto, toscale); append_icode_list(ilp, ii); } else { if (!both_ptr) { /* Second sub operand is scaled */ ii = icode_make_shl(toscale, tmpvr); append_icode_list(ilp, ii); } ii = icode_make_sub(addto, toscale); append_icode_list(ilp, ii); if (both_ptr) { /* Result of p1 - p is scaled */ ii = icode_make_shr(addto, tmpvr); append_icode_list(ilp, ii); } } } else { tmpvr->from_const = const_from_value(&factor, NULL); if (op == TOK_OP_PLUS) { backend-> icode_prepare_op(&toscale, &tmpvr, TOK_OP_MULTI, ilp); ii = icode_make_mul(toscale, tmpvr); append_icode_list(ilp, ii); } if (op == TOK_OP_PLUS) { vreg_faultin_protected(toscale, NULL, NULL, addto, ilp, 0); ii = icode_make_add(addto, toscale); append_icode_list(ilp, ii); } else { if (!both_ptr) { backend-> icode_prepare_op(&toscale, &tmpvr, TOK_OP_MULTI, ilp); ii = icode_make_mul(toscale, tmpvr); append_icode_list(ilp, ii); } ii = icode_make_sub(addto, toscale); append_icode_list(ilp, ii); if (both_ptr) { backend->icode_prepare_op (&addto, &tmpvr, TOK_OP_DIVIDE, ilp); ii = icode_make_div(addto, tmpvr); append_icode_list(ilp, ii); } } } } else { pro_mote(&toscale, ilp, eval); if (op == TOK_OP_PLUS) { ii = icode_make_add(addto, toscale); } else { ii = icode_make_sub(addto, toscale); } append_icode_list(ilp, ii); } if (op == TOK_OP_PLUS && toscale == lres) { /* * This is an uncommon operand ordering, such as ``123 + buf''. * The result is stored in buf's register, but 123's register * is returned! */ icode_make_copyreg(toscale->pregs[0], addto->pregs[0], addto->type, addto->type, ilp); /* XXX long long?? */ } free_pregs_vreg(toscale, ilp, 0, 0); } void do_add_sub(struct vreg **lres, struct vreg **rres, int op, struct token *optok, struct icode_list *il, int eval) { struct icode_instr *ii = NULL; if ((*lres)->type->tlist != NULL || (*rres)->type->tlist != NULL) { /* Need to scale first */ struct type *newty = NULL; if (is_floating_type((*lres)->type) || is_floating_type((*rres)->type)) { errorfl(optok, "Cannot do pointer arithmetic with floating " "point values"); return; } if ((*lres)->type->tlist != NULL && (*rres)->type->tlist != NULL) { /* ptr - ptr2 */ if (op == TOK_OP_PLUS) { errorfl(optok, "Cannot add pointers to pointers"); return; } else { /* Result of p - p2 is of type ptrdiff_t */ newty = make_basic_type(TY_LONG); /* XXX */ } } else if ((*lres)->type->tlist != NULL) { /* ptr +/- integer */ if ((*lres)->type->tlist->type == TN_ARRAY_OF) { /* Becomes pointer */ newty = n_xmemdup((*lres)->type, sizeof *(*lres)->type); copy_tlist(&newty->tlist, newty->tlist); newty->tlist->type = TN_POINTER_TO; } } else { /* if ((*rres)->type->tlist != NULL) { */ /* integer +/- ptr */ if ((*rres)->type->tlist->type == TN_ARRAY_OF) { /* Becomes pointer */ newty = n_xmemdup((*rres)->type, sizeof *(*rres)->type); copy_tlist(&newty->tlist, newty->tlist); newty->tlist->type = TN_POINTER_TO; } else { /* Is pointer */ newty = n_xmemdup((*rres)->type, sizeof(struct type)); } } if (eval) { ptrarit(lres, rres, il, op, eval); } if (newty != NULL) { /* * The result still has type ``pointer'' and * may be stored in a 64bit register (e.g. on * AMD64) that is bigger than int. Hence, * we need to change type and register */ if (eval) { *lres = backend-> icode_make_cast(*lres, newty, il); } else { vreg_set_new_type(*lres, newty); } #if 0 (*lres)->type = newty; (*lres)->size = backend->get_sizeof_type(newty, NULL); #endif } return; } else { if (eval) { if (is_x87_trash(*lres)) { *lres = x87_do_binop(*lres, *rres, op, il); } else { vreg_anonymify(lres, NULL, NULL, il); if (op == TOK_OP_PLUS) { ii = icode_make_add(*lres, *rres); } else { ii = icode_make_sub(*lres, *rres); } } } } if (eval && ii != NULL) { append_icode_list(il, ii); } } static int do_mul(struct vreg **lres0, struct vreg *rres, struct operator *operator, struct token *op, struct icode_list *il, int eval) { struct icode_instr *ii; struct vreg *lres; struct reg *lres_preg1 = NULL; struct reg *lres_preg2 = NULL; if (eval) { if (!is_x87_trash(*lres0)) { vreg_anonymify(lres0, NULL, NULL, il); } else { *lres0 = x87_anonymify(*lres0, il); } } lres = *lres0; if (operator->value == TOK_OP_MOD) { if (!is_integral_type(lres->type) || !is_integral_type(rres->type)) { errorfl(op /* XXX */, "Operands of `%%' operator must have integral" " type"); return -1; } } else if (!is_arithmetic_type(lres->type) || !is_arithmetic_type(rres->type)) { errorfl(op /* XXX */, "Operands of `%s' operator must have arithmetic " "(integral or floating point) type", operator->name); return -1; } if (!eval) { /* We're already done */ return 0; } if (lres->is_multi_reg_obj) { /* * long long ... so this operation is carried out using * a function call and we need to invalidate GPRs * XXX hm this belongs into icode_prepare_op ?!??! */ lres->pregs[0]->used = lres->pregs[1]->used = 0; rres->pregs[0]->used = rres->pregs[1]->used = 0; lres_preg1 = lres->pregs[0]; lres_preg2 = lres->pregs[1]; backend->invalidate_gprs(il, 1); } if (is_x87_trash(lres)) { *lres0 = x87_do_binop(*lres0, rres, operator->value, il); } else { if (operator->value == TOK_OP_MULTI) { ii = icode_make_mul(lres, rres); } else if (operator->value == TOK_OP_DIVIDE) { ii = icode_make_div(lres, rres); } else { /* MOD */ ii = icode_make_mod(lres, rres); } append_icode_list(il, ii); } if (lres->is_multi_reg_obj) { if (backend->arch == ARCH_X86) { /* long long results are returned in eax:edx */ vreg_map_preg(lres, &x86_gprs[0]); vreg_map_preg2(lres, &x86_gprs[3]); } else if (backend->arch == ARCH_POWER) { /* Results are returned in dest pregs */ vreg_map_preg(lres, lres_preg1); vreg_map_preg2(lres, lres_preg2); } else { unimpl(); } } return 0; } static int do_bitwise(struct vreg **lres0, struct vreg *rres, struct operator *operator, struct token *optok, struct icode_list *il, int eval) { struct icode_instr *ii = NULL; struct vreg *lres; int op = operator->value; if (eval) { vreg_anonymify(lres0, NULL, NULL, il); } lres = *lres0; if (!is_integral_type(lres->type) || !is_integral_type(rres->type)) { errorfl(optok, "Both operands of the `%s' operator have to be " "of integral type", operator->name); return -1; } if (!eval) { /* We're already done */ return 0; } /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ... anonymify broken */ lres = n_xmemdup(lres, sizeof *lres); if (op == TOK_OP_BSHL) { ii = icode_make_shl(lres, rres); } else if (op == TOK_OP_BSHR) { ii = icode_make_shr(lres, rres); } else if (op == TOK_OP_BAND) { ii = icode_make_and(lres, rres); } else if (op == TOK_OP_BOR) { ii = icode_make_or(lres, rres); } else if (op == TOK_OP_BXOR) { ii = icode_make_xor(lres, rres); } append_icode_list(il, ii); return 0; } void boolify_result(struct vreg *vr, struct icode_list *il) { struct icode_instr *ii; struct icode_instr *label = icode_make_label(NULL); ii = icode_make_cmp(vr, NULL); append_icode_list(il, ii); ii = icode_make_branch(label, INSTR_BR_EQUAL, vr); append_icode_list(il, ii); /* At this point, the value is nonzero so it always becomes 1 */ ii = icode_make_setreg(vr->pregs[0], 1); append_icode_list(il, ii); append_icode_list(il, label); } /* * XXX not ``eval-clean'' (sizeof) */ static struct vreg * do_comp_assign(struct vreg *lres, struct vreg *rres, int op, struct token *optok, struct icode_list *il, int eval) { struct operator *operator; struct vreg *destvr = n_xmemdup(lres, sizeof *lres); struct type *desttype = lres->type; int op2 = op; int needprep = 0; if (op == TOK_OP_CODIVIDE || op == TOK_OP_COMULTI || op == TOK_OP_COMOD || op == TOK_OP_COBSHL || op == TOK_OP_COBSHR || op == TOK_OP_COBAND || op == TOK_OP_COBXOR || op == TOK_OP_COBOR) { if (op == TOK_OP_CODIVIDE) op2 = TOK_OP_DIVIDE; else if (op == TOK_OP_COMULTI) op2 = TOK_OP_MULTI; else if (op == TOK_OP_COMOD) op2 = TOK_OP_MOD; else if (op == TOK_OP_COBSHL) op2 = TOK_OP_BSHL; else if (op == TOK_OP_COBSHR) op2 = TOK_OP_BSHR; else if (op == TOK_OP_COBAND) op2 = TOK_OP_BAND; else if (op == TOK_OP_COBXOR) op2 = TOK_OP_BXOR; else if (op == TOK_OP_COBOR) op2 = TOK_OP_BOR; needprep = 1; } else if (op == TOK_OP_COPLUS) { op2 = TOK_OP_PLUS; } else if (op == TOK_OP_COMINUS) { op2 = TOK_OP_MINUS; } if (eval) { (void) promote(&lres, &rres, op2, optok, il, eval); /* XXX .... as promote may move stuff :( */ if (is_x87_trash(lres)) { /* * The target may not have been loaded yet */ #if 0 vreg_faultin_x87(NULL, NULL, lres, il, 0); vreg_map_preg(lres, &x86_fprs[1]); vreg_faultin_x87(NULL, NULL, rres, il, 0); #endif } else { vreg_faultin(NULL, NULL, lres, il, 0); vreg_faultin_protected(lres, NULL, NULL, rres, il, 0); } if (needprep) { backend->icode_prepare_op(&lres, &rres, op2, il); /* XXX .... as prepare may move stuff :( */ /* XXX this seems totally wrong... may trash preparations */ #if 0 vreg_faultin(NULL, NULL, lres, il, 0); vreg_faultin_protected(lres, NULL, NULL, rres, il, 0); #endif } } switch (op) { case TOK_OP_COPLUS: do_add_sub(&lres, &rres, TOK_OP_PLUS, optok, il, eval); /* XXX */ break; case TOK_OP_COMINUS: do_add_sub(&lres, &rres, TOK_OP_MINUS, optok, il, eval); /* XXX */ break; case TOK_OP_CODIVIDE: operator = &operators[LOOKUP_OP2(TOK_OP_DIVIDE)]; do_mul(&lres, rres, operator, optok, il, eval); break; case TOK_OP_COMULTI: operator = &operators[LOOKUP_OP2(TOK_OP_MULTI)]; do_mul(&lres, rres, operator, optok, il, eval); break; case TOK_OP_COMOD: operator = &operators[LOOKUP_OP2(TOK_OP_MOD)]; do_mul(&lres, rres, operator, optok, il, eval); break; case TOK_OP_COBAND: operator = &operators[LOOKUP_OP2(TOK_OP_BAND)]; do_bitwise(&lres, rres, operator, optok, il, eval); break; case TOK_OP_COBOR: operator = &operators[LOOKUP_OP2(TOK_OP_BOR)]; do_bitwise(&lres, rres, operator, optok, il, eval); break; case TOK_OP_COBXOR: operator = &operators[LOOKUP_OP2(TOK_OP_BXOR)]; do_bitwise(&lres, rres, operator, optok, il, eval); break; case TOK_OP_COBSHL: operator = &operators[LOOKUP_OP2(TOK_OP_BSHL)]; do_bitwise(&lres, rres, operator, optok, il, eval); break; case TOK_OP_COBSHR: operator = &operators[LOOKUP_OP2(TOK_OP_BSHR)]; do_bitwise(&lres, rres, operator, optok, il, eval); break; default: printf("%d is not compound assignment operator\n", op); abort(); } /* * Given ``char *p;'', ``*p += 4'' has type ``char'', so * promotions to int need to be reverted here */ if (eval) { lres = backend-> icode_make_cast(lres, desttype, il); /* * With x87 the item may not be in a register even * after icode_make_cast */ vreg_faultin_x87(NULL, NULL, lres, il, 0); destvr->pregs[0] = lres->pregs[0]; if (lres->is_multi_reg_obj) { destvr->pregs[1] = lres->pregs[1]; } #if 0 if (destvr->from_ptr) { vreg_faultin_protected(lres, NULL, NULL, destvr->from_ptr, il, 0); } #endif /* * 070802: This code only did: * * if (destvr->from_ptr) { faultin(destvr->from_ptr); * * That ignored parent structs which come from pointers. * Multi-register assignmenst were also ignored */ reg_set_unallocatable(lres->pregs[0]); if (lres->is_multi_reg_obj) { reg_set_unallocatable(lres->pregs[1]); } vreg_faultin_ptr(destvr, il); reg_set_allocatable(lres->pregs[0]); if (lres->is_multi_reg_obj) { reg_set_allocatable(lres->pregs[1]); } icode_make_store(NULL, lres, destvr, il); } else { vreg_set_new_type(lres, desttype); } return destvr; } /* * XXX not ``eval-clean'' (sizeof) * XXX I think we need to be more careful not to trash the right/left operand.. * Have to use reg_set_unallocatable() before faulting in the left operand * pointer if we are assigning through a pointer more carefully?!?!? */ static struct vreg * do_assign( struct vreg *lres, struct vreg *rres, struct expr *ex, struct icode_list *ilp, int level, int purpose, int eval) { int is_struct = 0; int is_x87 = 0; struct icode_instr *ii = NULL; struct decl *d; struct vreg *vr2; /* Need to do typechecking */ if (ex->op == TOK_OP_ASSIGN && check_types_assign(ex->tok, lres->type, rres, 0, 0) != 0) { return NULL; } if ((rres->type->code == TY_STRUCT || rres->type->code == TY_UNION) && rres->type->tlist == NULL) { is_struct = 1; if ((lres->type->code != TY_STRUCT && lres->type->code != TY_UNION) || lres->type->tlist != NULL) { errorfl(ex->tok, "Incompatible types in assignment"); return NULL; } } else { if (eval) { if (is_x87_trash(rres) || is_x87_trash(lres)) { is_x87 = 1; #if 0 if (ex->op == TOK_OP_ASSIGN) { vreg_faultin_x87(NULL, NULL, rres, ilp, 0); rres = vreg_disconnect(rres); } #endif } else { vreg_faultin(NULL, NULL, rres, ilp, 0); } } } if (ex->op != TOK_OP_ASSIGN) { /* Compound assignment operator */ if (eval && !is_x87) { vreg_faultin(NULL, NULL, rres, ilp, 0); vreg_faultin_protected(rres, NULL, NULL, lres, ilp, 0); } return do_comp_assign(lres, rres, ex->op, ex->tok, ilp, eval); } else { if (level == 1 && (purpose == TOK_KEY_IF || purpose == TOK_KEY_DO || purpose == TOK_KEY_WHILE || purpose == TOK_KEY_SWITCH)) { warningfl(ex->tok, "`=' operator used at top-" "level in conditional " "expression - perhaps you " "meant `=='?"); } } if (!eval) { /* Not evaluated - just set new type */ vreg_set_new_type(rres, lres->type); return rres; } if (lres->parent) { vr2 = get_parent_struct(lres); } else { vr2 = NULL; } if (ex->left->data->var_lvalue != NULL) { /* Store to variable */ d = ex->left->data->var_lvalue; if (is_struct) { struct vreg *vr3; if (rres->parent) { vr3 = get_parent_struct(rres); } else { vr3 = NULL; } if (rres->from_ptr) { free_preg(rres->from_ptr->pregs[0], ilp, 0, 0); } if (vr3 && vr3->from_ptr) { free_preg(vr3->from_ptr->pregs[0], ilp, 0, 0); } backend->invalidate_gprs(ilp, /*level==*/1); if (rres->from_ptr) { #if 0 rres->from_ptr->pregs[0]->used = 1; #endif vreg_map_preg(rres->from_ptr, rres->from_ptr->pregs[0]); } if (vr3 && vr3->from_ptr) { #if 0 vr3->from_ptr->pregs[0]->used = 1; #endif vreg_map_preg(vr3->from_ptr, vr3->from_ptr->pregs[0]); } if (lres->from_ptr) { vreg_faultin_protected(rres, NULL, NULL, lres->from_ptr, ilp, 0); } else if (vr2 && vr2->from_ptr) { vreg_faultin_protected(rres, NULL, NULL, vr2->from_ptr, ilp, 0); } icode_make_copystruct(lres, rres, ilp); if (rres->from_ptr) { free_preg(rres->from_ptr->pregs[0], ilp, 0, 0); } if (vr3 && vr3->from_ptr) { free_preg(vr3->from_ptr->pregs[0], ilp, 0, 0); } if (lres->from_ptr) { free_preg(lres->from_ptr->pregs[0], ilp, 0, 0); } if (vr2 && vr2->from_ptr) { free_preg(vr2->from_ptr->pregs[0], ilp, 0, 0); } } else { struct reg *r = NULL; #if 0 if (lres->from_ptr || (vr2 && vr2->from_ptr)) { r = vreg_faultin_protected(rres, NULL, NULL, lres->from_ptr? lres->from_ptr: vr2->from_ptr, ilp, 0); } #endif rres = backend-> icode_make_cast(rres, lres->type, ilp); /* * When casting to x87 fp, the result is not register * resident anymore! */ vreg_faultin_x87(NULL, NULL, rres, ilp, 0); /* * 070802: This was apparently done the wrong way * around; Possible left-handed pointers were loaded, * then the right side was cast to the left type, * then it was assigned. * * This caused an x86 sign-extension from int to * long long to trash eax (this must be done with eax * and edx), so the pointer was also trashed */ vreg_set_unallocatable(rres); r = vreg_faultin_ptr(lres, ilp); vreg_set_allocatable(rres); if (lres->parent) { if (ex->op == TOK_OP_ASSIGN) { lres->pregs[0] = rres->pregs[0]; if (rres->is_multi_reg_obj) { lres->pregs[1] = rres->pregs[1]; } } icode_make_store(NULL, rres, lres, ilp); ii = NULL; if (is_x87_trash(rres)) { lres->pregs[0] = NULL; } } else { struct vreg *vr; vr = n_xmemdup(d->vreg, sizeof *d->vreg); if (ex->op == TOK_OP_ASSIGN) { vr->pregs[0] = rres->pregs[0]; if (rres->is_multi_reg_obj) { vr->is_multi_reg_obj = 2; vr->pregs[1] = rres->pregs[1]; } } else { vr->pregs[0] = lres->pregs[0]; if (lres->is_multi_reg_obj) { vr->is_multi_reg_obj = 2; vr->pregs[1] = lres->pregs[1]; } } icode_make_store(NULL, rres, /*d->vreg*/vr, ilp); ii = NULL; if (is_x87_trash(rres)) { vr->pregs[0] = NULL; } } if (r != NULL) { free_preg(r, ilp, 1, 0); } } } else { struct type *ty; struct reg *r = NULL; if (!is_struct) { rres = backend-> icode_make_cast(rres, lres->type, ilp); /* * When casting to x87 fp, the result is not register * resident anymore! */ vreg_faultin_x87(NULL, NULL, rres, ilp, 0); } if (lres->from_ptr || (vr2 && vr2->from_ptr)) { r = vreg_faultin_protected(rres, NULL, NULL, lres->from_ptr? lres->from_ptr: vr2->from_ptr, ilp, 0); } ty = lres->type; if (is_struct) { /* XXXXXXXXXXXXXXXXXXXXXXXX need to faultin * rres if from ptr?!?! */ struct reg *r2; if (r) reg_set_unallocatable(r); r2 = vreg_faultin_ptr(rres, ilp); if (r) reg_set_allocatable(r); backend->invalidate_except(ilp, /*level==1*/1, r, r2, (struct reg *)NULL); if (r != NULL) { free_preg(r, ilp, 0, 1); } if (r2 != NULL) { free_preg(r2, ilp, 0, 1); } icode_make_copystruct(lres, rres, ilp); /* * 08/02/07: That invlidate_except() is only used * above because the registers shall not needlessly * be saved! However, the free_preg()s below were * missing the invalidate flag, which is still * necessary because copystruct may call memcpy() * * 08/03/07: Hmm ... without SAVING them too, some * pointers become unbacked. E.g. in * * foo->bar = foo->baz = foo->bam; * * ... where the members are all structures. Here * one of the frees below made a pointer invalid. * Presumably because the return value of the * assignment is the left vreg, not a new one! * XXX fix this! * * 08/03/07: Phew...Now the test structassbug.c * works. Saving pointers below is bogus because * they are already invalidated by the copystruct. * So now we have the FIRST CASE EVER where we can * save but not invalidate with free_preg() * legally... That is done above before the * copstruct. Maybe the invalidate_except() should * be changed instead. Not sure whether what we * have now is really correct */ if (r != NULL) { free_preg(r, ilp, 1, 0); } if (r2 != NULL) { free_preg(r2, ilp, 1, 0); } } else { lres->pregs[0] = rres->pregs[0]; lres->pregs[1] = rres->pregs[1]; ii = icode_make_store_indir(rres, lres); if (r != NULL) { free_preg(r, ilp, 0, 0); } if (is_x87_trash(rres)) { lres->pregs[0] = NULL; } backend->invalidate_except(ilp, /*level==1*/1, rres->pregs[0], (struct reg *)NULL); } } if (ii) append_icode_list(ilp, ii); if (lres->pregs[0] && lres->pregs[0] != rres->pregs[0]) { free_pregs_vreg(lres, ilp, 0, 0); } return rres; } static void do_branch( struct icode_list *ilp, struct icode_instr *label, int op, struct vreg *dest) { struct icode_instr *ii = NULL; if (op == TOK_OP_LEQU) { ii = icode_make_branch(label, INSTR_BR_NEQUAL, dest); } else if (op == TOK_OP_LNEQU) { ii = icode_make_branch(label, INSTR_BR_EQUAL, dest); } else if (op == TOK_OP_GREAT) { ii = icode_make_branch(label, INSTR_BR_SMALLEREQ, dest); } else if (op == TOK_OP_SMALL) { ii = icode_make_branch(label, INSTR_BR_GREATEREQ, dest); } else if (op == TOK_OP_GREATEQ) { ii = icode_make_branch(label, INSTR_BR_SMALLER, dest); } else if (op == TOK_OP_SMALLEQ) { ii = icode_make_branch(label, INSTR_BR_GREATER, dest); } else { printf("BAD OPERATOR FOR BRANCH: %d\n", op); abort(); } append_icode_list(ilp, ii); } struct icode_instr * compare_vreg_with_zero(struct vreg *vr, struct icode_list *ilp) { struct token *ztok; struct vreg *zvr; struct icode_instr *ii; if (IS_FLOATING(vr->type->code) && vr->type->tlist == NULL) { int is_x87 = 0; /* * On some or many or most architectures, we * cannot compare fp values with an immediate * zero. Therefore we explicitly construct an * fp zero token, load it into a register, and * compare with that */ /* if (!backend->has_zero_cmp) { */ ztok = fp_const_from_ascii("0.0", vr->type->code); zvr = vreg_alloc(NULL, ztok, NULL, NULL); vreg_faultin_x87(NULL, NULL, vr, ilp, 0); /* * 07/08/03: Was missing a faultin for vr. This * broke when x87 support was rewritten I guess */ reg_set_unallocatable(vr->pregs[0]); vreg_faultin_x87(NULL, NULL, zvr, ilp, 0); reg_set_unallocatable(vr->pregs[0]); if (is_x87_trash(vr)) { vreg_map_preg(vr, &x86_fprs[1]); is_x87 = 1; } ii = icode_make_cmp(vr, zvr); if (!is_x87) { free_pregs_vreg(zvr, ilp, 0, 0); } /* } */ return ii; } return icode_make_cmp(vr, NULL); } static struct icode_instr * branch_if_zero( struct vreg *vr, int branch_type, struct icode_instr *label0, struct icode_list *ilp) { struct icode_instr *ii; struct icode_instr *label; struct icode_instr *not_equal_zero; if (vr->is_multi_reg_obj) { not_equal_zero = icode_make_label(NULL); } else { not_equal_zero = NULL; } if (vr->is_multi_reg_obj && IS_FLOATING(vr->type->code)) { /* SPARC long double ?!?!??! */ unimpl(); } ii = compare_vreg_with_zero(vr, ilp); append_icode_list(ilp, ii); free_pregs_vreg(vr, ilp, 0, 0); if (label0 != NULL) { label = label0; } else { label = icode_make_label(NULL); } if (vr->is_multi_reg_obj) { ii = icode_make_branch(not_equal_zero, INSTR_BR_NEQUAL, vr); } else { ii = icode_make_branch(label, /*INSTR_BR_EQUAL*/branch_type, vr); } append_icode_list(ilp, ii); if (vr->is_multi_reg_obj) { ii = icode_make_cmp(vr, NULL); append_icode_list(ilp, ii); ii = icode_make_branch(label, /*INSTR_BR_EQUAL*/branch_type, vr); append_icode_list(ilp, ii); append_icode_list(ilp, not_equal_zero); } return label; } static struct vreg * do_cond_op(struct expr *ex, struct type **restype, struct vreg *lvalue, /* for structs */ struct icode_list *ilp, int eval) { struct vreg *ret = NULL; struct vreg *lres; struct vreg *rres; struct reg *r; struct type *lt; struct type *rt; struct icode_instr *label; struct icode_instr *left_end_jump; struct icode_instr *end_label; struct icode_list *left_list; struct icode_list *right_list; int is_void; int is_struct = 0; int ret_is_anon_struct = 0; if (ex->right->op != TOK_OP_COND2) { errorfl(ex->right->tok, "Parse error - expected second part of conditional operator"); return NULL; } lres = expr_to_icode(ex->left, NULL, ilp, 0, eval); if (lres == NULL) { return NULL; } if (!is_scalar_type(lres->type)) { errorfl(ex->left->tok, "First operand of conditional operator" " does not have scalar type"); return NULL; } if (eval) { left_list = alloc_icode_list(); right_list = alloc_icode_list(); backend->invalidate_gprs(ilp, 1); if (!is_x87_trash(lres)) { vreg_faultin(NULL, NULL, lres, ilp, 0); } label = branch_if_zero(lres, INSTR_BR_EQUAL, NULL, ilp); } /* * Now comes the part for (cond) != 0 ... * * 08/18/07: As per GNU C, this part may be empty, in which case * it is replaced with the condition itself */ if (ex->right->left != NULL) { lres = expr_to_icode(ex->right->left, NULL, left_list, 0, eval); } else { ; /* lres already is value of condition */ } if (lres == NULL) { return NULL; } if (lres->type->code == TY_VOID && lres->type->tlist == NULL) { is_void = 1; } else { is_void = 0; if ((lres->type->code == TY_STRUCT || lres->type->code == TY_UNION) && lres->type->tlist == NULL) { is_struct = 1; if (eval) { if (lvalue != NULL) { /* Result is being assigned */ icode_make_copystruct(lvalue, lres, left_list); } else { /* * Result is anonymous struct, e.g * in * (foo? bar: baz).xyz * * or * * func(foo? bar: baz) * * ... there is no lvalue to assign * to */ ret = vreg_stack_alloc(lres->type, ilp, 1, NULL); icode_make_copystruct(ret, lres, left_list); ret_is_anon_struct = 1; } } } } if (!is_void && !is_struct) { pro_mote(&lres, left_list, eval); } if (ret == NULL) { /* Not anonymous struct - vreg must still be allocated */ ret = vreg_alloc(NULL, NULL, NULL, lres->type); } if (eval) { end_label = icode_make_label(NULL); left_end_jump = icode_make_jump(end_label); backend->invalidate_gprs(left_list, 1); /* saves lres too */ } /* Now comes the part for (cond) == 0 ... */ rres = expr_to_icode(ex->right->right, NULL, right_list, 0, eval); if (rres == NULL) { return NULL; } if (!is_void && !is_struct) { pro_mote(&rres, right_list, eval); } else if (is_struct) { if (eval) { if (lvalue != NULL) { /* Result is assigned */ icode_make_copystruct(lvalue, rres, right_list); } else { /* Anonymous struct */ icode_make_copystruct(ret, rres, right_list); } } } /* Now it is FINALLY possible to determine the result type! */ lt = lres->type; rt = rres->type; if (lt->tlist == NULL && rt->tlist == NULL) { if (lt->code == TY_STRUCT || lt->code == TY_UNION || rt->code == TY_STRUCT || rt->code == TY_UNION) { if (rt->code != lt->code || rt->tstruc != lt->tstruc) { errorfl(ex->tok, "Result of conditional operator has " "variable type"); return NULL; } } else if (rt->code != lt->code) { if (!is_arithmetic_type(rt) || !is_arithmetic_type(lt)) { errorfl(ex->tok, "Result of conditional operator has " "variable type"); return NULL; } else { if (convert_operands(&lres, &rres, left_list, right_list, TOK_OP_COND, ex->tok, eval) != 0) { return NULL; } } } } else { int bad = 0; if (lt->tlist == NULL || rt->tlist == NULL) { /* Either one must be a null pointer constant */ if (lt->tlist == NULL && !lres->is_nullptr_const) { bad = 1; } else if (rt->tlist == NULL && !rres->is_nullptr_const) { bad = 1; } else { if (lt->tlist == NULL) { if (eval) { lres = backend-> icode_make_cast(lres,rt, left_list); } else { vreg_set_new_type(lres, rt); } lt = lres->type; } else { if (eval) { rres = backend-> icode_make_cast(rres,lt, right_list); } else { vreg_set_new_type(rres, lt); } rt = rres->type; } } } else { if (rres->is_nullptr_const) { if (eval) { rres = backend-> icode_make_cast(rres, lt, right_list); } else { vreg_set_new_type(rres, lt); } rt = rres->type; } else if (lres->is_nullptr_const) { if (eval) { lres = backend-> icode_make_cast(lres, rt, left_list); } else { vreg_set_new_type(lres, rt); } lt = lres->type; } else { /* Both are pointers */ if (compare_types(lres->type, rres->type, CMPTY_ALL| CMPTY_ARRAYPTR) != 0) { bad = 1; } } } if (bad) { errorfl(ex->tok, "Result of conditional operator has " "variable type"); return NULL; } } *restype = lres->type; if (!is_struct && !is_void) { ret->type = lres->type; ret->size = lres->size; ret->is_multi_reg_obj = lres->is_multi_reg_obj; if (eval) { vreg_faultin_x87(NULL, NULL, lres, left_list, 0); /* !!! */ r = lres->pregs[0]; vreg_map_preg(ret, r); if (lres->is_multi_reg_obj) { struct reg *r2; r2 = lres->pregs[1]; vreg_map_preg2(ret, r2); } } } else { r = NULL; ret->pregs[0] = NULL; } if (eval) { append_icode_list(left_list, left_end_jump); /* XXX */ append_icode_list(left_list, label); /* * As the type is known now, the code lists can be merged and * the unified ilp is used for finishing the processing */ merge_icode_lists(left_list, right_list); merge_icode_lists(ilp, left_list); } if (compare_types(lres->type, rres->type, CMPTY_ALL| CMPTY_ARRAYPTR) != 0) { errorfl(ex->tok, "Result of conditional operator has variable type"); return NULL; } if (!is_void && !is_struct && eval) { if (!is_x87_trash(rres)) { vreg_faultin(NULL, NULL, rres, ilp, 0); if (rres->pregs[0] != ret->pregs[0]) { icode_make_copyreg(ret->pregs[0], rres->pregs[0], lres->type, lres->type, ilp); free_pregs_vreg(rres, ilp, 0, 0); } } else { vreg_faultin_x87(ret->pregs[0], NULL, rres, ilp, 0); } if (rres->is_multi_reg_obj) { if (rres->pregs[1] != ret->pregs[1]) { icode_make_copyreg(ret->pregs[1], rres->pregs[1], lres->type, lres->type, ilp); } } } if (eval) { append_icode_list(ilp, end_label); backend->invalidate_except(ilp, 1, r, (struct reg *)NULL); if (r != NULL) { vreg_map_preg(ret, r); } } if (is_struct) { ret->struct_ret = 1; } else if (is_x87_trash(ret)) { /* * Don't keep stuff in x87 registers, ever!!! */ free_preg(ret->pregs[0], ilp, 1, 1); } return ret; } struct vreg * expr_to_icode( struct expr *ex, struct vreg *lvalue, struct icode_list *ilp, int purpose, int eval) { struct icode_instr *ii = NULL; struct icode_instr *label; struct icode_instr *label2; struct vreg *lres = NULL; struct vreg *rres = NULL; struct vreg *ret = NULL; struct type *restype = NULL; struct type *ltold; struct type *rtold; static int level; struct reg *r; if (level++ == 0 && eval) { /* Initialize allocator */ /* * XXX July 2007: This SUCKS! I wasn't aware it's still * here, but it broke inline asm because registers were * not saved but just marked unused. Saving them also * caused problems with multi-gpr long longs converted * to floating point variables... Maybe we should keep * this for some time to debug ``register leak'' * problems (i.e. assume something is wrong if the call * below ever does save anything to the stack), and * then get rid of it */ #if FEAT_DEBUG_DUMP_BOGUS_STORES backend_warn_inv = 1; #endif backend->invalidate_gprs(ilp, /*0*/ 1); #if FEAT_DEBUG_DUMP_BOGUS_STORES backend_warn_inv = 0; #endif } if (ex->op != 0) { struct operator *operator; operator = &operators[LOOKUP_OP2(ex->op)]; if (ex->op != TOK_OP_LAND && ex->op != TOK_OP_LOR && !IS_ASSIGN_OP(ex->op) && ex->op != TOK_OP_COND) { lres = expr_to_icode(ex->left, NULL, ilp, purpose, eval); #if 0 hmm this is ABSOLUE nonsense!?!? if (is_x87_trash(lres)) { free_pregs_vreg(lres, ilp, 1, 1); } #endif rres = expr_to_icode(ex->right, NULL, ilp, purpose, eval); if (is_x87_trash(rres)) { free_pregs_vreg(rres, ilp, 1, 1); } if (lres == NULL || rres == NULL) { if (ilp) { /* XXX free */ } --level; return NULL; } /* A promotion may be in order */ #if 0 if (eval) { #endif if (ex->op != TOK_OP_COMMA) { /* * Check whether we have a struct or union - * those cannot be promoted, or decay into * pointers */ ltold = lres->type; rtold = rres->type; if (((ltold->code == TY_STRUCT || ltold->code == TY_UNION) && ltold->tlist == NULL) || ((rtold->code == TY_STRUCT || rtold->code == TY_UNION) && rtold->tlist == NULL)) { errorfl(ex->tok, "Operator `%s' does not work " "with union/struct types!", ex->tok->ascii); return NULL; } if ((restype = promote(&lres, &rres, ex->op, ex->tok, ilp, eval)) == NULL) { --level; return NULL; } debug_print_conv(ltold, rtold, ex->op,restype); } else { restype = rres->type; } if (!eval) { /* XXX seems totally nonsense */ if (level == 1) { /* Save type! */ ex->type = restype; } lres->type = restype; lres->size = backend-> get_sizeof_type(lres->type, NULL); --level; return lres; } #if 0 } else if (!eval) { unimpl(); ret = vreg_alloc(NULL, NULL, NULL, NULL); if (ex->op == TOK_OP_LAND || ex->op == TOK_OP_LOR) { ret->type = make_basic_type(TY_INT); } else if (!IS_ASSIGN_OP(ex->op)) { /* Conditional operator */ } ret->size = backend->get_sizeof_type(ret->type, NULL); --level; return ret; #endif } switch (ex->op) { case TOK_OP_COMMA: /* Comma operator */ if (lres->pregs[0]) { free_pregs_vreg(lres, ilp, 0, 0); } ret = rres; /* XXX */ break; case TOK_OP_ASSIGN: case TOK_OP_COPLUS: case TOK_OP_COMINUS: case TOK_OP_CODIVIDE: case TOK_OP_COMULTI: case TOK_OP_COMOD: case TOK_OP_COBAND: case TOK_OP_COBOR: case TOK_OP_COBXOR: case TOK_OP_COBSHL: case TOK_OP_COBSHR: /* Assignment & compound assignment operators */ if (ex->left->op != 0) { errorfl(ex->left->tok, "Bad lvalue in assignment"); --level; return NULL; } if (expr_to_icode(ex->left, NULL, ilp, 0, eval) == NULL) { --level; return NULL; } if (!ex->left->data->is_lvalue) { errorfl(ex->left->data->meat, "Left operand in assignment is not an lvalue"); --level; return NULL; } lres = ex->left->data->res; rres = expr_to_icode(ex->right, lres, ilp, purpose, eval); if (rres == NULL) { --level; return NULL; } if (rres->struct_ret) { /* * Was returned by function call or * conditional operator - has already * been assigned */ rres->struct_ret = 0; /* * 08/02/07: This wronly returned rres * instead of lres as result!!! Thus when * the left side indirects through a pointer, * any vreg_faultins() working on the right * result will not load the pointer, and * a stale pointer may be used!!! * * foo = bar[0] = baz; * * ... if we wrongly do foo = baz, &bar[0] * may not be loaded correctly. Bombed in * GNU tar code */ ret = lres; } else { if ((ret = do_assign(lres, rres, ex, ilp, level, purpose, eval)) == NULL) { --level; return NULL; } } restype = ret->type; break; case TOK_OP_LAND: case TOK_OP_LOR: /* Short circuit operators */ /* * foo && bar * generates instruction lists for foo and bar, * creates a label at the end of the bar list * and connects both lists through a conditional * jump to that label */ lres = expr_to_icode(ex->left, NULL, ilp, purpose, eval); if (lres == NULL) { --level; return NULL; } label2 = icode_make_label(NULL); if (!is_x87_trash(lres)) { vreg_faultin(NULL, NULL, lres, ilp, 0); } if (ex->op == TOK_OP_LAND) { label = branch_if_zero(lres, INSTR_BR_EQUAL, NULL, ilp); } else { label = branch_if_zero(lres, INSTR_BR_NEQUAL, NULL, ilp); } free_pregs_vreg(lres, ilp, 0, 0); rres = expr_to_icode(ex->right, NULL, ilp, purpose, eval); if (rres == NULL) { --level; return NULL; } /* The result of these operators has type int */ ret = vreg_alloc(NULL, NULL, NULL, NULL); ret->type = make_basic_type(TY_INT); ret->size = backend->get_sizeof_type(ret->type, NULL); r = ALLOC_GPR(curfunc, ret->size, ilp, NULL); vreg_map_preg(ret, r); reg_set_unallocatable(r); if (!is_x87_trash(rres)) { vreg_faultin(NULL, NULL, rres, ilp, 0); } reg_set_allocatable(r); if (ex->op == TOK_OP_LAND) { branch_if_zero(rres, INSTR_BR_EQUAL, label, ilp); ii = icode_make_setreg(r, 1); } else { branch_if_zero(rres, INSTR_BR_NEQUAL, label, ilp); ii = icode_make_setreg(r, 0); } append_icode_list(ilp, ii); ii = icode_make_jump(label2); append_icode_list(ilp, ii); append_icode_list(ilp, label); if (ex->op == TOK_OP_LAND) { ii = icode_make_setreg(r, 0); } else { ii = icode_make_setreg(r, 1); } append_icode_list(ilp, ii); append_icode_list(ilp, label2); backend->invalidate_except(ilp, /*level == 1*/1, r, (struct reg *)NULL); vreg_map_preg(ret, r); break; case TOK_OP_MINUS: case TOK_OP_PLUS: case TOK_OP_MULTI: case TOK_OP_DIVIDE: case TOK_OP_MOD: case TOK_OP_BSHL: case TOK_OP_BSHR: case TOK_OP_BAND: case TOK_OP_BXOR: case TOK_OP_BOR: /* Arithmetic and bitwise operators */ /* WARNING: Order of faultins below matters */ if (is_x87_trash(lres)) { #if 0 vreg_faultin_x87(NULL, NULL, lres, ilp, ex->op); vreg_map_preg(lres, &x86_fprs[1]); vreg_faultin_x87(NULL, NULL, rres, ilp, ex->op); #endif } else { vreg_faultin(NULL, NULL, lres, ilp, ex->op); vreg_faultin_protected(lres, NULL, NULL, rres, ilp, 0); backend->icode_prepare_op(&lres, &rres, ex->op, ilp); } if (ex->op == TOK_OP_PLUS || ex->op == TOK_OP_MINUS) { do_add_sub(&lres, &rres, ex->op, ex->tok, ilp, eval); ii = NULL; } else if (ex->op == TOK_OP_MULTI || ex->op == TOK_OP_DIVIDE || ex->op == TOK_OP_MOD) { if (do_mul(&lres, rres, operator, ex->tok, ilp, eval)) { --level; return NULL; } ii = NULL; } else if (ex->op == TOK_OP_BSHL || ex->op == TOK_OP_BSHR || ex->op == TOK_OP_BAND || ex->op == TOK_OP_BOR || ex->op == TOK_OP_BXOR) { if (do_bitwise(&lres, rres, operator, ex->tok, ilp, eval)) { --level; return NULL; } ii = NULL; } if (ii != NULL) { append_icode_list(ilp, ii); } if (rres->pregs[0]) { /* * XXX free_pregs_vreg() causes unbacked * register problems with cpu_mips.c and * I do not understand why. The problem * is probably elsewhere */ free_pregs_vreg(rres, ilp, 1, 0); } ret = lres; if (is_x87_trash(ret)) { #if 0 vreg_map_preg(ret, &x86_fprs[0]); #endif } else { vreg_faultin(NULL, NULL, ret, ilp, 0); vreg_map_preg(ret, ret->pregs[0]); if (lres->pregs[1]) { vreg_map_preg2(ret, ret->pregs[1]); } } restype = ret->type; break; case TOK_OP_COND: /* Conditional operator */ ret = do_cond_op(ex, &restype, lvalue, ilp, eval); if (ret == NULL) { --level; return NULL; } break; case TOK_OP_LEQU: case TOK_OP_LNEQU: case TOK_OP_GREAT: case TOK_OP_SMALL: case TOK_OP_GREATEQ: case TOK_OP_SMALLEQ: /* Equality and relational operators */ r = NULL; ret = vreg_alloc(NULL,NULL,NULL,NULL); ret->type = make_basic_type(TY_INT); ret->size = backend->get_sizeof_type(ret->type, NULL); if (purpose != TOK_KEY_IF || level != 1) { /* * Need to allocate gpr so it isn't wiped out * by faultins below */ r = ALLOC_GPR(curfunc, ret->size, ilp, NULL); reg_set_unallocatable(r); } if (is_x87_trash(lres)) { vreg_faultin_x87(NULL, NULL, lres, ilp, 0); vreg_map_preg(lres, &x86_fprs[1]); vreg_faultin_x87(NULL, NULL, rres, ilp, 0); } else { vreg_faultin(NULL, NULL, lres, ilp, 0); vreg_faultin_protected(lres, NULL, NULL, rres, ilp, 0); } if (r != NULL) { reg_set_allocatable(r); } if (purpose == TOK_KEY_IF && level == 1) { /* * We want to generate the expected cmp + je * for ``if (stuff == stuff)'' so the caller * has to check for this logical operator and * generate the branch himself */ ii = icode_make_cmp(lres, rres); append_icode_list(ilp, ii); free_pregs_vreg(rres, ilp, 0, 0); } else { /* * Generate kludgy * cmp dest, src * mov res, 0 * jXX label * mov res, 1 * label: * ... for expressions where the * result of the operator is used by * subsequently applied operators * XXX Again, conditional mov would be * much better ... */ vreg_map_preg(ret, r); label = icode_make_label(NULL); ii = icode_make_setreg(r, 0); append_icode_list(ilp, ii); /* * Note that it's important to have the * cmp after the setreg because mov affects * the status register ... :( */ ii = icode_make_cmp(lres, rres); append_icode_list(ilp, ii); /* * The branch is only taken if the condition * is false - thus the result is 1 if true */ if (lres->is_multi_reg_obj) { struct icode_instr *mreg_label = 0; if (ex->op == TOK_OP_LNEQU) { /* If already different, skip * second cmp */ mreg_label = icode_make_label(0); do_branch(ilp, mreg_label, TOK_OP_LEQU, lres); } else if (ex->op == TOK_OP_SMALL || ex->op == TOK_OP_GREAT) { /* * The inverse of smaller/greater * is greateq/smalleq, but the * eq part is not allowed to hit * at this point because we're * only comparing the first part. * Thus we need to omit it */ if (ex->op == TOK_OP_SMALL) { do_branch(ilp, label, TOK_OP_SMALLEQ, lres); } else { do_branch(ilp, label, TOK_OP_GREATEQ, lres); } } else { do_branch(ilp, label, ex->op, lres); } ii = icode_make_cmp(lres, rres); append_icode_list(ilp, ii); do_branch(ilp, label, ex->op, lres); if (mreg_label != NULL) { append_icode_list(ilp, mreg_label); } } else { do_branch(ilp, label, ex->op, lres); } ii = icode_make_setreg(r, 1); append_icode_list(ilp, ii); append_icode_list(ilp, label); free_pregs_vreg(lres, ilp, 0, 0); free_pregs_vreg(rres, ilp, 0, 0); } restype = NULL; break; } } else if (ex->data != NULL) { ex->data->code = alloc_icode_list(); ret = s_expr_to_icode(ex->data, lvalue, ex->data->code, eval); if (eval) { if ((ilp->res = ret) == NULL) { --level; return NULL; } } if (eval) merge_icode_lists(ilp, ex->data->code); } else if (ex->stmt_as_expr != NULL) { /* * GNU statement-as-expression */ struct icode_list *tmp; ++doing_stmtexpr; if (!eval) { unimpl(); } else { tmp = xlate_to_icode(ex->stmt_as_expr->code, 0); if (tmp == NULL) { --level; return NULL; } merge_icode_lists(ilp, tmp); ret = ilp->res; } if (ret == NULL) { /* * Last statement in compound statement wasn't an * expression; treat like void */ ret = vreg_alloc(NULL, NULL, NULL, NULL); ret->type = make_basic_type(TY_VOID); } --doing_stmtexpr; } else { puts("BUG: Empty expression passed to expr_to_icode() :("); abort(); } if (eval && ret) ilp->res = ret; if (--level == 0 && eval) { if (ilp->res->pregs[0] != NULL && ilp->res->pregs[0]->vreg == ilp->res) { vreg_map_preg(ilp->res, ilp->res->pregs[0]); } } if (restype != NULL) { size_t newsize; /* XXX is check for only ``void'' sufficient!??! */ if (restype->code != TY_VOID || restype->tlist != NULL) { if (restype->is_vla && is_immediate_vla_type(restype)) { newsize = 0; } else { newsize = backend->get_sizeof_type(restype, NULL); } } else { newsize = 0; } ex->type = restype; if (ilp && ilp->res) { if (newsize != 0 && ((ilp->res->type->code != TY_STRUCT && ilp->res->type->code != TY_UNION) || ilp->res->type->tlist != NULL)) { if (is_x87_trash(ilp->res)) { #if 0 ilp->res = vreg_disconnect(ilp->res); #endif ilp->res = x87_anonymify(ilp->res, ilp); } else { vreg_anonymify(&ilp->res, NULL, NULL, ilp); } } ilp->res->type = restype; ilp->res->size = newsize; } else if (eval) { puts("BUG!!!"); printf("doing operator %d!\n", ex->op); abort(); } } if (eval && ret->type->tlist != NULL && ret->type->tlist->type == TN_ARRAY_OF) { struct type *ty; /* Array decays into pointer */ vreg_anonymify(&ret, NULL, NULL, ilp); ty = n_xmemdup(ret->type, sizeof *ret->type); copy_tlist(&ty->tlist, ret->type->tlist); ty->tlist->type = TN_POINTER_TO; ret->type = ty; ret->size = backend->get_sizeof_type(ret->type, NULL); ilp->res = ret; } else if (eval && level == 0) { #if 0 if (IS_FLOATING(ret->type->code) && ret->type->tlist == NULL) { if (ret->pregs[0] && STUPID_X87(ret->pregs[0])) { backend->free_preg(ret->pregs[0], ilp); } } #endif } return eval? ilp->res: ret; } static int do_cond( struct expr *cond, struct icode_list *il, struct control *ctrl) { struct icode_instr *ii; struct icode_instr *dest_label; struct icode_instr *lastinstr; struct icode_instr *multi_reg_label = NULL; struct vreg *res; struct vreg *lower_vreg = NULL; int positive; int btype = 0; int first_btype = 0; int is_multi_reg_obj; int saved_btype; if ((res = expr_to_icode(cond, NULL, il, TOK_KEY_IF, 1)) == NULL) { return -1; } lastinstr = il->tail; if (res->is_multi_reg_obj || (lastinstr && lastinstr->type == INSTR_CMP && lastinstr->dest_vreg->is_multi_reg_obj)) { /* * This complicates things greatly. If we compare * the registers in the right order (XXX big vs * little endian), relational * operators+branches in a row still work as * expected. However, with equality and inequality, * the first comparison cannot branch to the * target already, because the second register may * still differ. * * Thus instead of * cmp foo, bar * je label * ... we do * cmp foo[0], bar[0] * jne end * cmp foo[1], bar[1] * je label * end: */ is_multi_reg_obj = 1; } else { is_multi_reg_obj = 0; } if (ctrl->type == TOK_KEY_IF || ctrl->type == TOK_KEY_WHILE || ctrl->type == TOK_KEY_FOR) { /* * for/while loop or if statement - branch * if condition negative */ positive = 0; } else { /* * do-while loop - branch if condition * positive */ positive = 1; } /* * When dealing with relational operators that * occur at the top-level in an expression, * expr_to_icode() already does the branch- * determining cmp such that we can already * branch and get say for ``stuff == stuff'': * ``cmp stuff, stuff; jne end;'' rather than * computing 1 (true) or 0 (false) first and * comparing that against 0 */ if (cond->op == 0) { goto cmp_zero; } else if (doing_stmtexpr) { /* * expr_to_icode() uses a static variable ``level'' * to record the recursive call depth. That doesn't * work with GNU C statement-as-expression, where in * ({ if (x == 0) ; }) * the if controlling expression is parsed starting * with level = 1. * As a temporary workaround, we explicitly compare * with zero */ goto cmp_zero; } else if (cond->op == TOK_OP_LEQU) { if (positive) btype = INSTR_BR_EQUAL; else btype = INSTR_BR_NEQUAL; } else if (cond->op == TOK_OP_LNEQU) { if (positive) btype = INSTR_BR_NEQUAL; else btype = INSTR_BR_EQUAL; } else if (cond->op == TOK_OP_GREAT) { if (positive) btype = INSTR_BR_GREATER; else btype = INSTR_BR_SMALLEREQ; } else if (cond->op == TOK_OP_SMALL) { if (positive) btype = INSTR_BR_SMALLER; else btype = INSTR_BR_GREATEREQ; } else if (cond->op == TOK_OP_GREATEQ) { if (positive) btype = INSTR_BR_GREATEREQ; else btype = INSTR_BR_SMALLER; } else if (cond->op == TOK_OP_SMALLEQ) { if (positive) btype = INSTR_BR_SMALLEREQ; else btype = INSTR_BR_GREATER; } else { cmp_zero: /* * Branch if condition false (0) and we're * doing an if. Otherwise branch if condition * true in case of do-while(to top of loop.) */ #if 0 vreg_faultin_protected(res, NULL, NULL, res, il, 0); #endif #if 0 vreg_faultin(NULL, NULL, res, il, 0); ii = icode_make_cmp(res, NULL); lastinstr = ii; #endif vreg_faultin_x87(NULL, NULL, res, il, 0); lastinstr = ii = compare_vreg_with_zero(res, il); append_icode_list(il, ii); if (positive) btype = INSTR_BR_NEQUAL; else btype = INSTR_BR_EQUAL; } if (ctrl->type == TOK_KEY_DO) { dest_label = ctrl->startlabel; } else { dest_label = ctrl->endlabel; } first_btype = btype; /* * * negative: * for equality * * if (foo == bar) { * body; * } * * becomes * * if (foo[0] != bar[0]) { * goto no; * } * if (foo[1] != bar[1]) { * goto no; * } * body * no: * * * * inequality: * * if (foo != bar) { * body; * } * * becomes * * if (foo[0] != bar[0]) { * goto yes; * } * if (foo[1] != bar[1]) { * goto no; * } * yes: * body; * no: */ if (is_multi_reg_obj) { struct vreg *res_vr; if (lastinstr) { res_vr = lastinstr->dest_vreg; } else { res_vr = res; } if (IS_LLONG(res_vr->type->code)) { /* * Comparisons of the lower word have to be done * unsigned! Otherwise stuff like * if (1LL > 0xffffffff) { * goes wrong */ lower_vreg = n_xmemdup(res_vr, sizeof *res_vr); lower_vreg->type = n_xmemdup(res_vr->type, sizeof *res_vr->type); lower_vreg->type->sign = TOK_KEY_UNSIGNED; lower_vreg->type->code = TY_ULLONG; } if (btype == INSTR_BR_EQUAL || btype == INSTR_BR_NEQUAL) { /* * The first true comparison does not allow us to * jump to the target yet; Instead the first one * only determines whether the branch is already * known not to be taken (branch to label after * second cmp), or may be taken (fall through to * next cmp.) Thus the jump condition is reversed * if the ``positive'' flag is set, otherwise not. * * if (llong_value == 123ll) { * ... * } * * ... here ``positive'' is 0, meaning the branch * is NOT taken if the condition is true (the * flow of control ``falls through'' into the * statement body. Here both cmp+branch * instructions of a ``long long'' are allowed to * branch if they do not yield equality * * Yes, this is confusing. */ if (positive) { if (btype == INSTR_BR_EQUAL) { /*btype = INSTR_BR_NEQUAL;*/ btype = INSTR_BR_NEQUAL; multi_reg_label = icode_make_label(NULL); } else { /* is != */ /*btype = INSTR_BR_EQUAL;*/ } } else { /* * Branch if condition false. Note that the * branch type is already reversed, i.e. if * it's NEQUAL we really had a ``=='' */ if (btype == INSTR_BR_NEQUAL) { } else { /* equal, i.e. ``!='' */ /*btype = INSTR_BR_NEQUAL;*/ btype = INSTR_BR_NEQUAL; multi_reg_label = icode_make_label(NULL); } } } else if (btype == INSTR_BR_GREATEREQ || btype == INSTR_BR_SMALLEREQ) { /* * If we're looking for a greater/smaller-or-equal * relation, the first comparison has to omit the * ``equal'' part, because otherwise we'd get false * hits. Consider comparing the values 123 and 456; * The higher word is 0 in both cases, so the equal * part would trigger */ #if 0 if (btype == INSTR_BR_GREATEREQ) { btype = INSTR_BR_GREATER; } else { /* smalleq */ btype = INSTR_BR_SMALLER; } #endif multi_reg_label = icode_make_label(NULL); } else if (btype == INSTR_BR_GREATER || btype == INSTR_BR_SMALLER) { multi_reg_label = icode_make_label(NULL); } } saved_btype = btype; if (is_multi_reg_obj) { if (btype == INSTR_BR_GREATER) { btype = INSTR_BR_SMALLER /*EQ*/; } else if (btype == INSTR_BR_SMALLER) { btype = INSTR_BR_GREATER /*EQ*/; } else if (btype == INSTR_BR_SMALLEREQ) { #if 0 btype = INSTR_BR_GREATER; #endif btype = INSTR_BR_SMALLER; multi_reg_label = NULL; } else if (btype == INSTR_BR_GREATEREQ) { btype = INSTR_BR_SMALLER; /* XXX hmm is this right? */ #if 0 btype = INSTR_BR_GREATER; multi_reg_label = NULL; #endif } } if (cond->op != 0) { /* * If the comaparison has already been made, it is an * error to pass the expressions result type to * icode_make_branch. For instance, when comparing two * unsigned integers, the result has type ``signed * int''. This botch, which I'm not sure works in all * cases, fixes it for now. In general it would be * better to always implicitly use the type of the * last comparison, as is already done on e.g. MIPS */ if (lastinstr && lastinstr->type == INSTR_CMP) { ii = icode_make_branch( multi_reg_label? multi_reg_label: dest_label, btype, lastinstr->dest_vreg); } else { ii = icode_make_branch( multi_reg_label? multi_reg_label: dest_label, btype, res); } } else { ii = icode_make_branch( multi_reg_label? multi_reg_label: dest_label, btype, res); } btype = saved_btype; append_icode_list(il, ii); btype = first_btype; if (res->is_multi_reg_obj || (lastinstr && lastinstr->type == INSTR_CMP && lastinstr->dest_vreg->is_multi_reg_obj)) { #if 0 ii = icode_make_cmp(res, NULL); append_icode_list(il, ii); #endif ii = n_xmemdup(lastinstr, sizeof *lastinstr); append_icode_list(il, ii); if (multi_reg_label) { /* We have to reverse the test again */ #if 0 if (btype == INSTR_BR_EQUAL) { btype = INSTR_BR_NEQUAL; } else if (btype == INSTR_BR_NEQUAL) { btype = INSTR_BR_EQUAL; } else { unimpl(); } #endif } ii = icode_make_branch(dest_label, btype, lower_vreg? lower_vreg: res); append_icode_list(il, ii); /* * Now append label to which to jump if the first * comparison was false */ if (multi_reg_label != NULL) { append_icode_list(il, multi_reg_label); } } return 0; } static void do_body_labels(struct control *ctrl, struct icode_list *il) { if (ctrl->body_labels != NULL) { struct icode_instr *ii; for (ii = ctrl->body_labels->head; ii != NULL;) { struct icode_instr *botch = ii->next; ii->next = NULL; append_icode_list(il, ii); ii = botch; } } } struct icode_list * ctrl_to_icode(struct control *ctrl) { struct icode_list *il; struct icode_list *il2; struct icode_instr *ii; struct icode_instr *ii2; struct label *label; il = alloc_icode_list(); if (ctrl->type == TOK_KEY_DO || ctrl->type == TOK_KEY_WHILE || ctrl->type == TOK_KEY_FOR) { backend->invalidate_gprs(il, 1); } if (ctrl->type == TOK_KEY_IF) { /* * Generate * cmp res, 0; je label; * ... where label is returned (but not inserted * into the icode list.) */ if (do_cond(ctrl->cond, il, ctrl) != 0) { return NULL; } do_body_labels(ctrl, il); il2 = xlate_to_icode(ctrl->stmt, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } if (ctrl->next != NULL) { /* * End of if branch - jump across else * branch, then append else body */ ii2 = icode_make_jump(ctrl->next->endlabel); append_icode_list(il, ii2); append_icode_list(il, ctrl->endlabel); do_body_labels(ctrl->next, il); il2 = xlate_to_icode(ctrl->next->stmt, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } /* elsepart: ... code ... end: */ append_icode_list(il, ctrl->next->endlabel); } else { append_icode_list(il, ctrl->endlabel); } } else if (ctrl->type == TOK_KEY_WHILE) { append_icode_list(il, ctrl->startlabel); if (do_cond(ctrl->cond, il, ctrl) == -1) { return NULL; } do_body_labels(ctrl, il); il2 = xlate_to_icode(ctrl->stmt, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } ii = icode_make_jump(ctrl->startlabel); append_icode_list(il, ii); append_icode_list(il, ctrl->endlabel); } else if (ctrl->type == TOK_KEY_DO) { /* do-while loop */ append_icode_list(il, ctrl->startlabel); do_body_labels(ctrl, il); il2 = xlate_to_icode(ctrl->stmt, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } append_icode_list(il, ctrl->do_cond); (void) do_cond(ctrl->cond, il, ctrl); append_icode_list(il, ctrl->endlabel); } else if (ctrl->type == TOK_KEY_FOR) { struct statement tmpst; tmpst.type = ST_CODE; tmpst.next = NULL; if (ctrl->finit != NULL /* crude stuff to rule out empty ex - necessary? */ && (ctrl->finit->op || ctrl->finit->data)) { tmpst.data = ctrl->finit; il2 = xlate_to_icode(&tmpst, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } } append_icode_list(il, ctrl->startlabel); if (ctrl->cond != NULL && (ctrl->cond->op || ctrl->cond->data)) { if (do_cond(ctrl->cond, il, ctrl) != 0) { return NULL; } } do_body_labels(ctrl, il); il2 = xlate_to_icode(ctrl->stmt, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } if (ctrl->fcont != NULL /* crude stuff to rule out empty ex - necessary? */ && (ctrl->fcont->op || ctrl->fcont->data)) { append_icode_list(il, ctrl->fcont_label); tmpst.data = ctrl->fcont; il2 = xlate_to_icode(&tmpst, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } } ii = icode_make_jump(ctrl->startlabel); append_icode_list(il, ii); if (ctrl->endlabel != NULL) { append_icode_list(il, ctrl->endlabel); } } else if (ctrl->type == TOK_KEY_SWITCH) { struct token *cond; struct vreg *vr_cond; struct vreg *vr_case; struct label *default_case = NULL; vr_cond = expr_to_icode(ctrl->cond, NULL, il, 0, 1); if (vr_cond == NULL) { return NULL; } if (!is_integral_type(vr_cond->type)) { errorfl(ctrl->cond->tok, "Controlling switch expression doesn't have " "integral type"); return NULL; } do_body_labels(ctrl, il); for (label = ctrl->labels; label != NULL; label = label->next) { if (label->value == NULL) { default_case = label; continue; } /* * 08/22/07: This did usual arithmetic conversion * betwen condition and case, instead of converting * case to condition. Also, const_from_value was * called on the original case value, such that * * case ((char)1): * * would instruct the backend to load an ``immediate * char'', which if bogus. Now the condition is * instead promoted, and then the case is converted * to it. * * Another problem with that: * * switch (enum_type) { * case value: * * ... would convert value to an enum type, which is * also not handled by the backends. Thus the TY_INT * workaround below. */ (void) promote(&vr_cond, NULL, 0, NULL, il, 1); cross_do_conv(label->value->const_value, vr_cond->type->code); label->value->const_value->type->code = vr_cond->type->code == TY_ENUM? TY_INT: vr_cond->type->code; cond = const_from_value( label->value->const_value->value, label->value->const_value->type); vr_case = vreg_alloc(NULL, cond, NULL, NULL); vreg_faultin_protected(vr_cond, NULL, NULL, vr_case, il, 0); vreg_faultin_protected(vr_case, NULL, NULL, vr_cond, il, 0); #if 0 cond = const_from_value( label->value->const_value->value, label->value->const_value->type); vr_case = vreg_alloc(NULL, cond, NULL, NULL); vreg_faultin(NULL, NULL, vr_case, il, 0); vreg_faultin_protected(vr_case, NULL, NULL, vr_cond, il, 0); (void) promote(&vr_cond, &vr_case, TOK_OP_LEQU, NULL, il, 1); /* XXX .... as promote may move stuff :( */ vreg_faultin(NULL, NULL, vr_case, il, 0); vreg_faultin_protected(vr_case, NULL, NULL, vr_cond, il, 0); #endif ii = icode_make_cmp(vr_cond, vr_case); append_icode_list(il, ii); free_pregs_vreg(vr_case, il, 0, 0); ii = icode_make_branch(label->instr, INSTR_BR_EQUAL, vr_cond); append_icode_list(il, ii); if (vr_cond->is_multi_reg_obj) { ii = icode_make_cmp(vr_cond, vr_case); append_icode_list(il, ii); ii = icode_make_branch(label->instr, INSTR_BR_EQUAL, vr_cond); append_icode_list(il, ii); } } free_pregs_vreg(vr_cond, il, 0, 0); if (default_case != NULL) { ii = icode_make_jump(default_case->instr); } else { ii = icode_make_jump(ctrl->endlabel); } append_icode_list(il, ii); il2 = xlate_to_icode(ctrl->stmt, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } append_icode_list(il, ctrl->endlabel); } else if (ctrl->type == TOK_KEY_CASE || ctrl->type == TOK_KEY_DEFAULT) { label = ctrl->stmt->data; append_icode_list(il, label->instr); } else if (ctrl->type == TOK_KEY_RETURN) { struct vreg *vr = NULL; struct type *ret_type = NULL; struct type_node *rettn; if (ctrl->cond == NULL) { for (rettn = curfunc->proto->dtype->tlist; rettn != NULL; rettn = rettn->next) { if (rettn->type == TN_FUNCTION) { rettn = rettn->next; break; } } if (curfunc->proto->dtype->code != TY_VOID || rettn != NULL) { warningfl(ctrl->tok, "Return statement without a value in function not returning `void'"); } } else { if ((vr = expr_to_icode(ctrl->cond, NULL, il, TOK_KEY_RETURN, 1)) == NULL) { return NULL; } /* XXX warn if types incompatible */ } if (vr != NULL) { ret_type = curfunc->proto->dtype; rettn = ret_type->tlist; ret_type->tlist = ret_type->tlist->next; if (check_types_assign(ctrl->tok, ret_type, vr, 1, 0) != 0) { return NULL; } if ((ret_type->code != TY_STRUCT && ret_type->code != TY_UNION) || ret_type->tlist != NULL) { vr = backend->icode_make_cast(vr, ret_type, il); } else { if (vr->type->code != ret_type->code || vr->type->tlist != NULL || vr->type->tstruc != ret_type->tstruc) { errorfl(ctrl->tok, "Returned expression incompatible with function return type"); return NULL; } } if (ret_type->code == TY_VOID && ret_type->tlist == NULL) { warningfl(ctrl->tok, "void expressions as argument to " "`return' are only allowed in GNU C " "and C++"); } ret_type->tlist = rettn; } if (backend->icode_make_return(vr, il) != 0) { return NULL; } } else if (ctrl->type == TOK_KEY_BREAK) { ii = icode_make_jump(ctrl->endlabel); append_icode_list(il, ii); } else if (ctrl->type == TOK_KEY_CONTINUE) { ii = icode_make_jump(ctrl->startlabel); append_icode_list(il, ii); } else if (ctrl->type == TOK_KEY_GOTO) { struct label *l; struct token *dest = (struct token *)ctrl->stmt; ii = NULL; for (l = curfunc->labels_head; l != NULL; l = l->next) { #if 0 if (strcmp(l->instr->dat, dest->data) == 0) { #endif if (strcmp(l->name, dest->data) == 0) { ii = icode_make_jump(l->instr); break; } } if (ii == NULL) { errorfl(dest, "Undefined label `%s'", dest->ascii); } else { append_icode_list(il, ii); } } else { printf("UNKNOWN CONTROL STRUCTURE %d\n", ctrl->type); abort(); } return il; } /* * XXX this should only penalize initializers which really are not * constant!!!!!!!! */ static void varinit_to_icode(struct decl *dest, struct initializer *init, unsigned long *offset, struct icode_list *il) { unsigned long offset0 = 0; if (offset == NULL) { offset = &offset0; } for (; init != NULL; init = init->next) { size_t type_size; int remainder; int type_alignment; switch (init->type) { case INIT_EXPR: case INIT_STRUCTEXPR: /* Nothing to do */ break; case INIT_NESTED: varinit_to_icode(dest, init->data, offset, il); break; case INIT_NULL: if (init->varinit != NULL) { struct vreg *res; struct vreg *leftvr; struct vreg *tmpvr; struct token *tok; struct icode_instr *ii; struct vreg *indirvr; struct vreg *addrvr; int i; res = expr_to_icode(init->varinit, NULL, il, 0, 1); if (res == NULL) { break; } if (!is_basic_agg_type(init->left_type)) { res = backend->icode_make_cast(res, init->left_type, il); } leftvr = vreg_alloc(NULL, NULL, NULL, init->left_type); ii = icode_make_addrof(dest->vreg, il); append_icode_list(il, ii); addrvr = vreg_alloc(NULL, NULL, NULL, addrofify_type(leftvr->type)); vreg_map_preg(addrvr, ii->dat); /* * Now leftvr is the address of the left * hand struct or array. Now perform some * pointer arithmetic, then indirectly * assign the result. Yes this is kludged, * we need a more general way for offsets * in the long run */ reg_set_unallocatable(addrvr->pregs[0]); i = (int)*offset; tok = const_from_value(&i, make_basic_type(TY_INT)); tmpvr = vreg_alloc(NULL, tok, NULL, make_basic_type(TY_INT)); vreg_faultin(NULL, NULL, tmpvr, il, 0); ii = icode_make_add(addrvr, tmpvr); append_icode_list(il, ii); reg_set_allocatable(addrvr->pregs[0]); indirvr = vreg_alloc(NULL, NULL, addrvr, init->left_type); reg_set_unallocatable(indirvr->from_ptr->pregs[0]); vreg_faultin(NULL, NULL, res, il, 0); reg_set_allocatable(indirvr->from_ptr->pregs[0]); indirvr->pregs[0] = res->pregs[0]; ii = icode_make_store_indir(res, indirvr); append_icode_list(il, ii); } break; default: unimpl(); } if (init->left_type != NULL) { if (init->type == INIT_NESTED) { /* * Don't add up sizes for nested initializers - * that has already been done */ type_size = 0; } else { type_size = backend->get_sizeof_type(init->left_type, NULL); } } else { assert(init->type == INIT_NULL); type_size = *(size_t *)init->data; } *offset += type_size; /* * Align for next initializer, unless it is a genuine null * initializer (as opposed to a placeholder for a variable * initializer), in which case alignment is already handled */ if (init->next != NULL && (init->next->type != INIT_NULL || init->next->left_type != NULL)) { struct initializer *tmp = init->next; type_alignment = backend->get_align_type(tmp->left_type); remainder = *offset % type_alignment; if (remainder) { *offset += type_alignment - remainder; } } } } /* * Generate initializations for automatic variables */ void init_to_icode(struct decl *d, struct icode_list *il) { struct icode_instr *ii; struct initializer *init; if (is_basic_agg_type(d->dtype) && d->init->type != INIT_STRUCTEXPR) { /* Fill remaining elements/members with 0 */ d->init_name = backend->make_init_name(d->init); d->init_name->dec = d; backend->invalidate_gprs(il, 1); icode_make_copyinit(d, il); if (d->dtype->storage != TOK_KEY_STATIC && d->dtype->storage != TOK_KEY_EXTERN) { varinit_to_icode(d, d->init, NULL, il); #if 0 varinit_to_icode(d, d->init, 0, il); #endif } } else { struct vreg *vr; struct vreg *left = vreg_alloc(NULL, NULL, NULL, d->dtype); left->var_backed = d; init = d->init; if (init->next != NULL || (init->type != INIT_EXPR && init->type != INIT_STRUCTEXPR)) { puts("BAD INITIALIZER"); abort(); } /* * 08/16/07: This didn't pass the variable to be * initialized (left) to expr_to_icode(). Thus * functions returning structures by values didn't * work as initializers */ if ((vr = expr_to_icode(init->data, left, il, 0, 1)) == NULL) { return; } if (check_types_assign(d->tok, d->dtype, vr, 1, 0) == -1) { return; } if (init->type != INIT_STRUCTEXPR) { vr = backend->icode_make_cast(vr, d->dtype, il); vreg_faultin_x87(NULL, NULL, vr, il, 0); vreg_map_preg(d->vreg, vr->pregs[0]); if (vr->is_multi_reg_obj) { vreg_map_preg2(d->vreg, vr->pregs[1]); } icode_make_store(NULL, d->vreg, d->vreg, il); if (STUPID_X87(vr->pregs[0])) { #if 0 backend->free_preg(vr->pregs[0], il); #endif vr->pregs[0]->vreg = NULL; vr->pregs[0] = NULL; } ii = NULL; } else { /* * 08/16/07: This generated a bad struct copy if the * initializer was a function returning a structure * by value, in which case the callee does the copy, * not the caller */ if (!vr->struct_ret) { icode_make_copystruct(left, vr, il); } } } } /* * XXX Some constraints dealt with here are architecture-specific. That stuff * should be outsourced to the backend. o and i are missing :-( */ static void asm_to_icode(struct inline_asm_stmt *stmt, struct icode_list *il) { struct clobbered_reg *clob; struct inline_asm_io *io; char *p; struct reg *r; int i; for (clob = stmt->clobbered; clob; clob = clob->next) { if (clob->reg == NULL) { /* * Reg = NULL means "memory" is clobbered - * no values should be cached anymore * XXX really need invalidate_fprs() :( */ backend->invalidate_gprs(il, 1); break; } if (clob->reg->type == REG_GPR) { free_preg(clob->reg, il, 1, 1); /*reg_set_unallocatable(clob->reg);*/ clob->reg->used = 1; } else { puts("ERROR: Non-GPRS may not occur " "in the asm clobber list yet\n"); unimpl(); } } for (io = stmt->output, i = 1; io != NULL; io = io->next, ++i) { int earlyclobber = 0; io->vreg = expr_to_icode(io->expr, NULL, il, 0, 1); io->outreg = NULL; if (io->vreg == NULL) { return; } else if (io->expr->op != 0 || !io->expr->data->is_lvalue) { errorfl(io->expr->tok, "Output operand #%d isn't an lvalue", i); return; } for (p = io->constraints; *p != 0; ++p) { if (*p == '=') { ; } else if (*p == '+') { ; } else if (*p == '&') { earlyclobber = 1; } else if (strchr("rqQabcdSD", *p) != NULL) { /* XXX hmm Q is amd64?! */ r = backend->asmvreg_to_reg(&io->vreg, *p, io, il, 0); if (r == NULL) { return; } io->outreg = r; } } } /* XXX for some reason stuff below uses clobber registers :( */ for (io = stmt->input; io != NULL; io = io->next) { io->vreg = expr_to_icode(io->expr, NULL, il, 0, 1); if (io->vreg == NULL) { return; } } for (io = stmt->input, i = 1; io != NULL; io = io->next, ++i) { for (p = io->constraints; *p != 0; ++p) { if (strchr("rqabcdSD", *p) != NULL) { if (backend->asmvreg_to_reg(&io->vreg, *p, io, il, 1) == NULL) { return; } reg_set_unallocatable(io->vreg->pregs[0]); } else if (*p == 'm') { /* * XXX faultin below assumes it can always get * a register */ r = vreg_faultin_ptr(io->vreg, il); if (r != NULL) { /* Output is done through pointer */ reg_set_unallocatable(r); } } else if (isdigit((unsigned char)*p)) { int num = *p - '0'; struct inline_asm_io *tmp; if (num >= stmt->n_outputs) { errorfl(io->expr->tok, "Output operand %d doesn't exist", num+1); return; } for (tmp = stmt->output; num > 0; --num) { tmp = tmp->next; } if (tmp->outreg != NULL) { /* Must use same register */ static char kludge[2]; kludge[0] = tmp->outreg->name[1]; kludge[1] = 0; if (strcmp(tmp->outreg->name, "esi") == 0 || strcmp(tmp->outreg->name,"edi") == 0) { kludge[0] = toupper(kludge[0]); } p = kludge; /* :-( */ } else { /* XXX */ p = tmp->constraints; } } } } /* * At this point, input registers are setup and marked unallocatable, * so now the output regs can be assigned. If the first (write-) * access to an output register comes from a register, the * destination is that same register. That seems sort of bogus, but * then so does the idea of using "r" for output at all */ for (io = stmt->output; io != NULL; io = io->next) { for (p = io->constraints; *p != 0; ++p) { if (*p == '=') { ; } else if (*p == '+') { /* Used for both input and output */ if (io->outreg != NULL) { vreg_faultin(io->outreg, NULL, io->vreg, il, 0); } } else if (*p == 'm') { /* * XXX faultin below assumes it can always get * a register */ r = vreg_faultin_ptr(io->vreg, il); if (r != NULL) { /* Output is done through pointer */ reg_set_unallocatable(r); } } } } icode_make_asm(stmt, il); /* Write back output registers (memory operands need no writeback) */ for (io = stmt->output; io != NULL; io = io->next) { for (p = io->constraints; *p != 0; ++p) { if (*p == '=') { continue; } else if (strchr("qrabcdSD", *p) != NULL) { vreg_map_preg(io->vreg, io->outreg); icode_make_store(curfunc, /* XXX ?!*/ io->vreg, io->vreg, il); free_pregs_vreg(io->vreg, il, 0, 0); } } } for (io = stmt->input; io != NULL; io = io->next) { if (io->vreg->pregs[0] != NULL) { /*eg_set_allocatable(io->vreg->preg);*/ free_pregs_vreg(io->vreg, il, 0, 0); } } for (clob = stmt->clobbered; clob; clob = clob->next) { if (clob->reg && clob->reg->type == REG_GPR) { reg_set_allocatable(clob->reg); } } } struct stack_block * vla_decl_to_icode(struct type *ty, struct icode_list *il) { struct type_node *tn; int vlas_done = 0; int total_vla_dims = 0; struct stack_block *sb; for (tn = ty->tlist; tn != NULL; tn = tn->next) { ++total_vla_dims; } /* * Create block to store the VLA info in: * * struct vlainfo { * void *addr; * unsigned long total_size; * unsigned long var_dim_sizes[1]; * }; * * XXX for now we assume alignof(unsigned long) * = alignof(void *) */ sb = make_stack_block(0, backend->get_ptr_size() + (1 + total_vla_dims) * backend->get_sizeof_type( make_basic_type(TY_ULONG), NULL)); if (curfunc->vla_head == NULL) { curfunc->vla_head = curfunc->vla_tail = sb; } else { curfunc->vla_tail->next = sb; curfunc->vla_tail = sb; } ty->vla_addr = sb; vlas_done = 0; for (tn = ty->tlist; tn != NULL; tn = tn->next) { struct vreg *vexpr; if (tn->type != TN_VARARRAY_OF) { continue; } /* * Variable - execute variable * expression */ vexpr = expr_to_icode( tn->arrarg, NULL, il, 0, 1); if (vexpr == NULL) { return NULL; } vexpr = backend->icode_make_cast( vexpr, make_basic_type(TY_ULONG), il); /* * Store dimension size in VLA block */ vreg_faultin(NULL, NULL, vexpr, il, 0); icode_make_put_vla_size(vexpr->pregs[0], sb, vlas_done, il); ++vlas_done; } return sb; } struct icode_list * xlate_to_icode(struct statement *st, int inv_gprs_first) { struct icode_list *il; struct icode_list *il2; il = alloc_icode_list(); if (inv_gprs_first) { backend->invalidate_gprs(il, 0); } for (; st != NULL; st = st->next) { if (st->type == ST_CTRL) { il2 = ctrl_to_icode(st->data); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } /* Missed save flag */ backend->invalidate_gprs(il, 1); il->res = NULL; } else if (st->type == ST_LABEL) { struct label *l = st->data; append_icode_list(il, l->instr); /* Missed save flag */ backend->invalidate_gprs(il, 1); il->res = NULL; } else if (st->type == ST_CODE) { struct expr *ex = st->data; struct vreg *res; if (ex->op == 0 && ex->data == NULL) { /* Empty expression */ ; } else { icode_make_dbginfo_line(st, il); if ((res = expr_to_icode(ex, NULL, il, 0, 1)) == NULL) { /* XXX free stuff */ return NULL; } il->res = res; free_pregs_vreg(res, il, 0, 0); } } else if (st->type == ST_COMP || st->type == ST_EXPRSTMT) { struct scope *s = st->data; curscope = s; il2 = xlate_to_icode(s->code, 0); if (il2 != NULL) { merge_icode_lists(il, il2); free(il2); } if (st->type != ST_EXPRSTMT) { il->res = NULL; } else { if (il->res && il->res->pregs[0]) { /* * There's a free_pregs_vreg() * in xlate_to_icode(). That's * bad because the value may * still be used. So we have to * ensure that the item remains * associated with a register * if it's a scalar type */ if (is_arithmetic_type(il->res->type) || il->res->type->tlist) { if (il->res->pregs[0]->vreg == il->res) { vreg_map_preg(il->res, il->res->pregs[0]); if (il->res->is_multi_reg_obj) { vreg_map_preg( il->res, il->res->pregs[1]); } } } } } curscope = s->parent; /* restore scope */ } else if (st->type == ST_DECL) { struct decl *d = st->data; d->vreg = vreg_alloc(d, NULL, NULL, NULL); vreg_set_new_type(d->vreg, d->dtype); if (d->init != NULL && d->dtype->storage != TOK_KEY_STATIC && d->dtype->storage != TOK_KEY_EXTERN) { icode_make_dbginfo_line(st, il); init_to_icode(d, il); } else if (d->dtype->is_vla) { /* * This is a VLA declaration in some way. * It may e.g. be a one- or multi- * dimensional array, or a pointer to such * a thing */ struct type_node *tn; #if 0 int err = 0; int vlas_done = 0; int total_vla_dims = 0; int vla_index = 0; #endif struct stack_block *sb; sb = vla_decl_to_icode(d->dtype, il); if ( /*!err*/ sb != NULL) { for (tn = d->dtype->tlist; tn != NULL; tn = tn->next) { if (d->dtype->tlist->type == TN_POINTER_TO) { break; } else if (d->dtype->tlist->type == TN_VARARRAY_OF) { break; } } if (tn->type == TN_VARARRAY_OF) { /* * This really is an array, not * just e.g. a pointer to one - * allocate storage! */ struct vreg *size; size = backend-> get_sizeof_vla_type( d->dtype, il); icode_make_put_vla_whole_size( size->pregs[0], sb, il); icode_make_alloc_vla(sb, il); } } } il->res = NULL; } else if (st->type == ST_ASM) { asm_to_icode(st->data, il); il->res = NULL; } else { printf("UNKNOWN STATEMENT TYPE\n"); printf("%d\n", st->type); abort(); } } return il; }