/*
* 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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#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: "<unknown>");
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: "<unknown>");
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;
}
syntax highlighted by Code2HTML, v. 0.9.1