/*
 * 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