/* $Id: expr.c,v 1.171 2009/01/27 15:40:22 potyra Exp $ 
 *
 * Copyright (C) 2007-2009 FAUcc Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "identifier.h"
#include "type.h"
#include "declaration.h"
#include "scope.h"
#include "expr.h"
#include "stmt.h"
#include "simplify.h"

static int expr_change;


void
expr_rename_structunionenum(
	struct expr *e,
	enum type_type type,
	const char *old,
	const char *new
)
{
	if (e->type_name) {
		type_rename_structunionenum(e->type_name, type, old, new);
	}
}

struct type *
expr_typeof(struct scope *s, struct expr *e)
{
	struct declaration *dion;
	struct type *ts0;
	struct type *ts1;
	struct type *t;
	int ret;

	switch (e->type) {
	case EXPR_NONE:
		assert(0);
	case EXPR_BRACES:
		assert(0);

	case EXPR_INTEGER:
	case EXPR_REAL:
		return e->type_name;

	case EXPR_STRING:
		return type_const_char_pointer();

	case EXPR_IDENTIFIER:
		return e->declaration->type_name;

	case EXPR_SIZEOF_TYPE:
	case EXPR_SIZEOF_EXPR:
	case EXPR_BUILTIN_CONSTANT_P:
	case EXPR_BUILTIN_OFFSETOF:
		return type_int();

	case EXPR_BUILTIN_VA_ARG:
	case EXPR_TYPE_CONVERSION:
		return e->type_name;

	case EXPR_STAR:
		return type_star(expr_typeof(s, e->expr0));

	case EXPR_ARRAY:
		return type_array_access(expr_typeof(s, e->expr0));

	case EXPR_AMPHERSAND:
		return type_amphersand(expr_typeof(s, e->expr0));

	case EXPR_PRE_INC:
	case EXPR_PRE_DEC:
	case EXPR_POST_INC:
	case EXPR_POST_DEC:
		return expr_typeof(s, e->expr0);

	case EXPR_NEG:
	case EXPR_INV:
		return expr_typeof(s, e->expr0);

	case EXPR_NOT:
		return type_int();

	case EXPR_LEFT:
	case EXPR_RIGHT:
		t = expr_typeof(s, e->expr0);
		return type_arithmetic(t, t);

	case EXPR_EQUAL:
	case EXPR_NOT_EQUAL:
	case EXPR_LESS:
	case EXPR_GREATER:
	case EXPR_LESS_EQUAL:
	case EXPR_GREATER_EQUAL:
		return type_int();

	case EXPR_ADD:
	case EXPR_SUB:
	case EXPR_MUL:
	case EXPR_DIV:
	case EXPR_MOD:
	case EXPR_AND:
	case EXPR_OR:
	case EXPR_XOR:
		ts0 = expr_typeof(s, e->expr0);
		ts1 = expr_typeof(s, e->expr1);
		
		if (e->type == EXPR_ADD) {
			return type_add(ts0, ts1);
		} else if (e->type == EXPR_SUB) {
			return type_sub(ts0, ts1);
		} else {
			return type_arithmetic(ts0, ts1);
		}

	case EXPR_SHORT_OR:
	case EXPR_SHORT_AND:
		return type_int();

	case EXPR_LIST:
		return expr_typeof(s, e->last);

	case EXPR_DOT:
	case EXPR_ARROW:
		ts0 = expr_typeof(s, e->expr0);
		if (e->type == EXPR_ARROW) {
			ts0 = type_star(ts0);
		}
		assert(type_is_struct(ts0)
		    || type_is_union(ts0));
		if (! ts0->scope) {
			ret = scope_lookup_structunionenum(s,
					ts0->type, ts0->identifier, &ts1);
			assert(0 <= ret);

			ts0 = ts1;
		}
		assert(ts0->scope);

		ret = scope_lookup_one(ts0->scope, e->member, &dion);
		assert(0 <= ret);

		return dion->type_name;

	case EXPR_FUNC:
		t = expr_typeof(s, e->expr0);
		if (type_is_pointer(t)) {
			t = type_star(t);
		}
		return type_call(t);

	case EXPR_CONDITION:
		ts0 = expr_typeof(s, e->expr1);
		ts1 = expr_typeof(s, e->expr2);

		return type_arithmetic(ts0, ts1);

	case EXPR_ASSIGN:
	case EXPR_LEFT_ASSIGN:
	case EXPR_RIGHT_ASSIGN:
	case EXPR_ADD_ASSIGN:
	case EXPR_SUB_ASSIGN:
	case EXPR_MUL_ASSIGN:
	case EXPR_DIV_ASSIGN:
	case EXPR_MOD_ASSIGN:
	case EXPR_AND_ASSIGN:
	case EXPR_OR_ASSIGN:
	case EXPR_XOR_ASSIGN:
		return expr_typeof(s, e->expr0);
	}
	/*NOTREACHED*/
	assert(0);
	return NULL;
}

int
expr_is_constant(struct expr *e)
{
	switch (e->type) {
	case EXPR_INTEGER:
		return 1;
	case EXPR_REAL:
		return 1;
	case EXPR_TYPE_CONVERSION:
		if (e->expr0->type == EXPR_AMPHERSAND
		 && (e->expr0->expr0->declaration->storage == STORAGE_NONE
		  || e->expr0->expr0->declaration->storage == STORAGE_EXTERN
		  || e->expr0->expr0->declaration->storage == STORAGE_STATIC)) {
			/*
			 * (int) & global_var
			 */
			return 1;
		}
		if (e->expr0->type == EXPR_IDENTIFIER
		 && (e->expr0->declaration->storage == STORAGE_NONE
		  || e->expr0->declaration->storage == STORAGE_EXTERN
		  || e->expr0->declaration->storage == STORAGE_STATIC)) {
			/*
			 * (int) global_array
			 * (int) function
			 */
			return 1;
		}
		return 0;
	default:
		return 0;
	}
}

struct expr *
expr_new(void)
{
	struct expr *e;

	e = malloc(sizeof(*e));
	assert(e);

	memset(e, 0, sizeof(*e));

	return e;
}

struct expr *
expr_integer(int n)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_INTEGER;
	e->type_name = type_int();
	e->integer = n;

	return e;
}

struct expr *
expr_identifier(struct declaration *dion)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_IDENTIFIER;
	e->declaration = dion;

	return e;
}

struct expr *
expr_sizeof_expr(struct expr *e0)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_SIZEOF_EXPR;
	e->expr0 = e0;

	return e;
}

struct expr *
expr_sizeof_type(struct type *t)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_SIZEOF_TYPE;
	e->type_name = t;

	return e;
}

struct expr *
expr_offsetof(struct type *t, const char *name)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_BUILTIN_OFFSETOF;
	e->type_name = t;
	e->expr0 = expr_new();
	e->expr0->type = EXPR_ARROW;
	e->expr0->member = identifier_dup(name);

	return e;
}

struct expr *
expr_amphersand(struct expr *e0)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_AMPHERSAND;
	e->expr0 = e0;

	return e;
}

struct expr *
expr_star(struct expr *e0)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_STAR;
	e->expr0 = e0;

	return e;
}

struct expr *
expr_arrow(struct expr *e0, const char *mem)
{
	struct expr *e;

	assert(mem);

	e = expr_new();

	e->type = EXPR_ARROW;
	e->expr0 = e0;
	e->member = mem;

	return e;
}

struct expr *
expr_not(struct expr *e0)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_NOT;
	e->expr0 = e0;

	return e;
}

struct expr *
expr_left(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_LEFT;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_right(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_RIGHT;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_add(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_ADD;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_sub(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_SUB;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_mul(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_MUL;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_div(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_DIV;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_mod(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_MOD;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_and(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_AND;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_or(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_OR;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_xor(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_XOR;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_short_and(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_SHORT_AND;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_short_or(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_SHORT_OR;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_equal(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_EQUAL;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_not_equal(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_NOT_EQUAL;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_less(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_LESS;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_less_equal(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_LESS_EQUAL;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_greater(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_GREATER;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_greater_equal(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_GREATER_EQUAL;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_assign(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_ASSIGN;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_add_assign(struct expr *e0, struct expr *e1)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_ADD_ASSIGN;
	e->expr0 = e0;
	e->expr1 = e1;

	return e;
}

struct expr *
expr_cast(struct type *t, struct expr *e0)
{
	struct expr *e;

	e = expr_new();

	e->type = EXPR_TYPE_CONVERSION;
	e->type_name = t;
	e->expr0 = e0;

	return e;
}

struct expr *
expr_cast_ptrdiff(struct expr *e0)
{
	struct type *t;

	t = type_ptrdiff();

	return expr_cast(t, e0);
}

struct expr *
expr_dup(struct expr *e)
{
	struct expr *ce;
	struct expr *ne;
	struct expr *nce;

	if (e == NULL) {
		return NULL;
	}

	ne = expr_new();
	ne->type = e->type;
	ne->integer = e->integer;
	ne->real = e->real;
	ne->string_len = e->string_len;
	ne->string = identifier_dup(e->string);
	ne->member = identifier_dup(e->member);
	if (e->declaration
	 && e->declaration->clone) {
		ne->declaration = e->declaration->clone;
	} else {
		ne->declaration = e->declaration;
	}
	ne->type_name = e->type_name;
	ne->expr0 = expr_dup(e->expr0);
	ne->expr1 = expr_dup(e->expr1);
	ne->expr2 = expr_dup(e->expr2);
	ne->first = NULL;
	ne->last = NULL;
	for (ce = e->first; ce; ce = ce->next) {
		nce = expr_dup(ce);
		nce->prev = ne->last;
		nce->next = NULL;
		if (nce->prev) {
			nce->prev->next = nce;
		} else {
			ne->first = nce;
		}
		ne->last = nce;
	}

	return ne;
}

void
expr_cp(struct expr *oe, struct expr *ne)
{
	struct expr *prev;
	struct expr *next;

	prev = oe->prev;
	next = oe->next;

	memcpy(oe, ne, sizeof *oe);

	oe->prev = prev;
	oe->next = next;
}

void
expr_free(struct expr *e)
{
	if (! e) {
		return;
	}

	/* ... */
}

/* ------------------------------------------------------------------------- */

static void
expr_simplify_sizeof(struct scope *scope, struct expr *e)
{
	struct type *t0;
	struct expr *e0;

	switch (e->type) {
	case EXPR_SIZEOF_TYPE:
		/*
		 * Replace
		 *		sizeof(type)
		 * by
		 *		constant
		 */
		t0 = e->type_name;
	sizeof_type:;
		e0 = expr_integer(type_sizeof(scope, t0));

		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_SIZEOF_EXPR:
		/*
		 * Replace
		 *		sizeof expr
		 * by
		 *		constant
		 */
		t0 = expr_typeof(scope, e->expr0);
		goto sizeof_type;

	default:
		assert(0);
	}
}

static void
_expr_simplify_offsetof(
	struct scope *scope,
	struct type *t,
	struct expr *e,
	struct type **rt,
	struct expr **re
)
{
	struct declaration *dion;
	struct expr *e0;
	struct expr *e1;
	struct expr *e2;
	int ret;

	switch (e->type) {
	case EXPR_ARROW:
		assert(type_is_struct(t)
		    || type_is_union(t));

		ret = scope_lookup_structunionenum(scope,
				t->type, t->identifier, &t);
		assert(0 <= ret);
		
		ret = scope_lookup_one(t->scope, e->member, &dion);
		assert(0 <= ret);

		/* Return type. */
		*rt = dion->type_name;

		/* Return expression. */
		if (t->type == TYPE_STRUCT) {
			e0 = expr_integer(type_offsetof(scope, t, e->member));
		} else {
			e0 = expr_integer(0);
		}
		*re = e0;
		break;

	case EXPR_DOT:
		_expr_simplify_offsetof(scope, t, e->expr0, &t, &e0);

		assert(type_is_struct(t)
		    || type_is_union(t));

		ret = scope_lookup_structunionenum(scope,
				t->type, t->identifier, &t);
		assert(0 <= ret);
		
		ret = scope_lookup_one(t->scope, e->member, &dion);
		assert(0 <= ret);

		/* Return type. */
		*rt = dion->type_name;

		/* Return expression. */
		if (t->type == TYPE_STRUCT) {
			e1 = expr_integer(type_offsetof(scope, t, e->member));
		} else {
			e1 = expr_integer(0);
		}
		*re = expr_add(e0, e1);
		break;

	case EXPR_ARRAY:
		_expr_simplify_offsetof(scope, t, e->expr0, &t, &e0);

		assert(type_is_array(t));
		t = type_array_access(t);

		/* Return type. */
		*rt = t;

		/* Return expression. */
		e1 = expr_integer(type_sizeof(scope, t));
		e2 = expr_dup(e->expr1);

		*re = expr_add(e0, expr_mul(e1, e2));
		break;

	default:
		assert(0);
	}
}

static void
expr_simplify_offsetof(struct scope *scope, struct expr *e)
{
	struct type *t0;
	struct expr *e0;

	_expr_simplify_offsetof(scope, e->type_name, e->expr0, &t0, &e0);

	expr_cp(e, e0);
	expr_change = 1;
}

static void
expr_simplify_in_init(struct scope *scope, struct expr *e)
{
	struct expr *ce;

	switch (e->type) {
	case EXPR_NONE:
		assert(0);

	case EXPR_INTEGER:
	case EXPR_REAL:
	case EXPR_STRING:
	case EXPR_IDENTIFIER:
		/* Already simple. */
		break;

	case EXPR_SIZEOF_TYPE:
	case EXPR_SIZEOF_EXPR:
		expr_simplify_sizeof(scope, e);
		break;

	case EXPR_BUILTIN_OFFSETOF:
		expr_simplify_offsetof(scope, e);
		break;

	case EXPR_BUILTIN_CONSTANT_P:
	case EXPR_BUILTIN_VA_ARG:
	case EXPR_STAR:
	case EXPR_DOT:
	case EXPR_ARROW:
	case EXPR_ARRAY:
	case EXPR_SHORT_AND:
	case EXPR_SHORT_OR:
	case EXPR_CONDITION:
	case EXPR_PRE_INC:
	case EXPR_PRE_DEC:
	case EXPR_POST_INC:
	case EXPR_POST_DEC:
	case EXPR_ASSIGN:
	case EXPR_LEFT_ASSIGN:
	case EXPR_RIGHT_ASSIGN:
	case EXPR_ADD_ASSIGN:
	case EXPR_SUB_ASSIGN:
	case EXPR_MUL_ASSIGN:
	case EXPR_DIV_ASSIGN:
	case EXPR_MOD_ASSIGN:
	case EXPR_AND_ASSIGN:
	case EXPR_OR_ASSIGN:
	case EXPR_XOR_ASSIGN:
	case EXPR_FUNC:
	case EXPR_LIST:
		/* Mustn't be part of constant initializer. */
		assert(0);

	case EXPR_TYPE_CONVERSION:
	case EXPR_AMPHERSAND:
	case EXPR_NEG:
	case EXPR_INV:
	case EXPR_NOT:
		expr_simplify_in_init(scope, e->expr0);
		break;

	case EXPR_LEFT:
	case EXPR_RIGHT:
	case EXPR_ADD:
	case EXPR_SUB:
	case EXPR_MUL:
	case EXPR_DIV:
	case EXPR_MOD:
	case EXPR_AND:
	case EXPR_OR:
	case EXPR_XOR:
	case EXPR_EQUAL:
	case EXPR_NOT_EQUAL:
	case EXPR_LESS:
	case EXPR_LESS_EQUAL:
	case EXPR_GREATER:
	case EXPR_GREATER_EQUAL:
		expr_simplify_in_init(scope, e->expr0);
		expr_simplify_in_init(scope, e->expr1);
		break;

	case EXPR_BRACES:
		for (ce = e->first; ce; ce = ce->next) {
			expr_simplify_in_init(scope, ce);
		}
		break;
	}
}

static void
expr_localize(struct stmt *block, struct stmt *s, struct expr *e)
{
	struct declaration *var;
	struct type *t0;
	struct expr *e0;
	struct stmt *s0;

	switch (e->type) {
	case EXPR_NONE:
		assert(0);
	case EXPR_BRACES:
		assert(0);
		
	case EXPR_INTEGER:
	case EXPR_REAL:
	case EXPR_STRING:
		break;

	case EXPR_IDENTIFIER:
		if (! identifier_is_tmp(e->declaration->identifier)) {
			goto localize;
		}
		break;

	case EXPR_AMPHERSAND:
		if (e->expr0->type != EXPR_IDENTIFIER) {
			goto localize;
		}
		break;

	default:
	localize:;
		t0 = expr_typeof(block->scope, e);
		t0 = type_pure(t0);

		var = simplify_declaration_add(block->scope,
				t0, identifier_tmp());

		e0 = expr_assign(expr_identifier(var), expr_dup(e));
		s0 = stmt_expr(e0);

		e0 = expr_identifier(var);

		stmt_prepend(block, s, s0);
		expr_cp(e, e0);
		expr_change = 1;
		break;
	}
}

static void
expr_simplify_in_expr(struct stmt *block, struct stmt *s, struct expr *e)
{
	struct declaration *var;
	struct label *label0;
	struct label *label1;
	struct type *t0;
	struct type *t1;
	int is_array;
	struct expr *e0;
	struct expr *e1;
	struct expr *e2;
	struct expr *e3;
	struct expr *e4;
	struct expr *e5;
	struct expr *e6;
	struct stmt *s0;
	struct stmt *s1;
	struct stmt *s2;
	struct stmt *s3;
	struct stmt *s4;
	struct stmt *s5;
	struct stmt *s6;

again:	;
	switch (e->type) {
	case EXPR_NONE:
		assert(0);
	case EXPR_BRACES:
		assert(0);

	case EXPR_INTEGER:
	case EXPR_REAL:
	case EXPR_STRING:
		/* Already simple. */
		break;

	case EXPR_SIZEOF_TYPE:
	case EXPR_SIZEOF_EXPR:
		expr_simplify_sizeof(block->scope, e);
		break;

	case EXPR_BUILTIN_CONSTANT_P:
		/*
		 * Replace
		 *		__builtin_constant_p(e)
		 * by
		 *		1 (if e is an integer/real/...)
		 *		0 (else)
		 */
		switch (e->expr0->type) {
		case EXPR_NONE:
			assert(0);
		case EXPR_BRACES:
			assert(0);
		case EXPR_INTEGER:
		case EXPR_REAL:
		case EXPR_STRING:
			e0 = expr_integer(1);
			break;
		default:
			e0 = expr_integer(0);
			break;
		}

		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_BUILTIN_OFFSETOF:
		expr_simplify_offsetof(block->scope, e);
		goto again; /* Split expression. */

	case EXPR_BUILTIN_VA_ARG:
	case EXPR_IDENTIFIER:
		/* Already simple. */
		break;

	case EXPR_STAR:
		if (e->expr0->type != EXPR_IDENTIFIER) {
			/*
			 * Replace
			 *	*e0
			 * by
			 *	var = e0;
			 *	*var
			 */
			t0 = expr_typeof(block->scope, e->expr0);

			var = simplify_declaration_add(block->scope,
					t0, identifier_tmp());

			e0 = expr_identifier(var);
			e1 = expr_dup(e->expr0);
			e2 = expr_assign(e0, e1);
			s0 = stmt_expr(e2);

			e0 = expr_identifier(var);

			stmt_prepend(block, s, s0);
			expr_cp(e->expr0, e0);
			expr_change = 1;
		}
		break;

	case EXPR_DOT:
		/*
		 * Replace
		 *	struct.mem
		 *
		 * by (if struct.mem is *not* an array):
		 * 	var = (typeof e0.mem *)
		 * 		((int) &struct + offsetof(typeof struct, mem))
		 * 	*var
		 *
		 * by (if struct.mem is an array):
		 *	var = (typeof struct.mem[0] *)
		 *		((int) &struct + offsetof(typeof(struct, mem))
		 *	var
		 */
		t0 = expr_typeof(block->scope, e);
		is_array = type_is_array(t0);
		t1 = expr_typeof(block->scope, e->expr0);
		assert(type_is_struct(t1)
		    || type_is_union(t1));

		e0 = expr_amphersand(expr_dup(e->expr0));
		e1 = expr_cast(type_ptrdiff(), e0);
		e2 = expr_offsetof(t1, e->member);
		e3 = expr_add(e1, e2);

		if (is_array) {
			t0 = type_array_access(t0);
		}
		t0 = type_amphersand(t0);
		t0 = type_pure(t0);

		var = simplify_declaration_add(block->scope,
				t0, identifier_tmp());

		e4 = expr_cast(t0, e3);

		s0 = stmt_expr(expr_assign(expr_identifier(var), e4));

		if (is_array) {
			e0 = expr_identifier(var);
		} else {
			e0 = expr_star(expr_identifier(var));
		}

		stmt_prepend(block, s, s0);
		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_ARROW:
		/*
		 * Replace
		 *	struct->mem
		 *
		 * by (if struct->mem is *not* an array):
		 *	var = *(typeof struct->mem *)
		 *		((int) struct + offsetof(typeof(struct, mem))
		 *	*var
		 *
		 * by (if struct->mem is an array):
		 *	var = (typeof struct->mem[0] *)
		 *		((int) struct + offsetof(typeof(struct, mem))
		 *	var
		 */
		t0 = expr_typeof(block->scope, e);
		is_array = type_is_array(t0);
		t1 = expr_typeof(block->scope, e->expr0);
		t1 = type_star(t1);
		assert(type_is_struct(t1)
		    || type_is_union(t1));

		e0 = expr_dup(e->expr0);
		e1 = expr_cast(type_ptrdiff(), e0);
		e2 = expr_offsetof(t1, e->member);
		e3 = expr_add(e1, e2);

		if (is_array) {
			t0 = type_array_access(t0);
		}
		t0 = type_amphersand(t0);
		t0 = type_pure(t0);

		var = simplify_declaration_add(block->scope,
				t0, identifier_tmp());

		e4 = expr_cast(t0, e3);

		s0 = stmt_expr(expr_assign(expr_identifier(var), e4));

		if (is_array) {
			e0 = expr_identifier(var);
		} else {
			e0 = expr_star(expr_identifier(var));
		}

		stmt_prepend(block, s, s0);
		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_ARRAY:
		/*
		 * Replace
		 *	array[index]
		 *
		 * by (if array[index] is *not* an array):
		 *	var = (typeof array[index] *)
		 *		((int) array + index * sizeof typeof array[index])
		 *	*var
		 *
		 * by (if array[index] is an array):
		 *	var = (typeof array[index][0] *)
		 *		((int) array + index * sizeof typeof array[index])
		 *	var
		 */
		t0 = expr_typeof(block->scope, e);
		is_array = type_is_array(t0);
		t1 = expr_typeof(block->scope, e->expr0);
		assert(type_is_pointer(t1)
		    || type_is_array(t1));

		e0 = expr_dup(e->expr0);
		e1 = expr_cast(type_ptrdiff(), e0);
		e2 = expr_dup(e->expr1);
		e3 = expr_sizeof_type(t0);
		e4 = expr_mul(e2, e3);
		e5 = expr_add(e1, e4);

		if (is_array) {
			t0 = type_array_access(t0);
		}
		t0 = type_amphersand(t0);
		t0 = type_pure(t0);

		var = simplify_declaration_add(block->scope,
				t0, identifier_tmp());

		e6 = expr_cast(t0, e5);

		s0 = stmt_expr(expr_assign(expr_identifier(var), e6));
		
		if (is_array) {
			e0 = expr_identifier(var);
		} else {
			e0 = expr_star(expr_identifier(var));
		}

		stmt_prepend(block, s, s0);
		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_AMPHERSAND:
		switch (e->expr0->type) {
		case EXPR_IDENTIFIER:
			/* Already simple. */
			break;

		case EXPR_STAR:
			/*
			 * Replace
			 *	&*e
			 * by
			 *	var = e;
			 *	var
			 */
			t0 = expr_typeof(block->scope, e->expr0->expr0);
			t0 = type_pure(t0);

			var = simplify_declaration_add(block->scope,
					t0, identifier_tmp());

			s0 = stmt_expr(expr_assign(expr_identifier(var),
					expr_dup(e->expr0->expr0)));

			e0 = expr_identifier(var);

			stmt_prepend(block, s, s0);
			expr_cp(e, e0);
			expr_change = 1;
			break;

		case EXPR_DOT:
			/*
			 * Replace
			 *	&struct.mem
			 * by
			 *	var = (typeof struct.mem *)
			 *		((int) &struct + offsetof(typeof struct, mem))
			 *	var
			 */
			t0 = expr_typeof(block->scope, e->expr0);
			t1 = expr_typeof(block->scope, e->expr0->expr0);
			assert(type_is_struct(t1)
			    || type_is_union(t1));

			e0 = expr_amphersand(expr_dup(e->expr0->expr0));
			e1 = expr_cast(type_ptrdiff(), e0);
			e2 = expr_offsetof(t1, e->expr0->member);
			e3 = expr_add(e1, e2);

			t0 = type_amphersand(t0);
			t0 = type_pure(t0);

			var = simplify_declaration_add(block->scope,
					t0, identifier_tmp());

			e4 = expr_cast(t0, e3);

			s0 = stmt_expr(expr_assign(expr_identifier(var), e4));

			e0 = expr_identifier(var);

			stmt_prepend(block, s, s0);
			expr_cp(e, e0);
			expr_change = 1;
			break;

		case EXPR_ARROW:
			/*
			 * Replace
			 *	&struct->mem
			 * by
			 *	var = (typeof struct.mem *)
			 *		((int) struct + offsetof(typeof struct, mem))
			 *	var
			 */
			t0 = expr_typeof(block->scope, e->expr0);
			t1 = expr_typeof(block->scope, e->expr0->expr0);
			t1 = type_star(t1);
			assert(type_is_struct(t1)
			    || type_is_union(t1));

			e0 = expr_dup(e->expr0->expr0);
			e1 = expr_cast(type_ptrdiff(), e0);
			e2 = expr_offsetof(t1, e->expr0->member);
			e3 = expr_add(e1, e2);

			t0 = type_amphersand(t0);
			t0 = type_pure(t0);

			var = simplify_declaration_add(block->scope,
					t0, identifier_tmp());

			e4 = expr_cast(t0, e3);

			s0 = stmt_expr(expr_assign(expr_identifier(var), e4));

			e0 = expr_identifier(var);

			stmt_prepend(block, s, s0);
			expr_cp(e, e0);
			expr_change = 1;
			break;

		case EXPR_ARRAY:
			/*
			 * Replace
			 * 	&array[index]
			 * by
			 *	var = (typeof array[index] *)
			 *		((ptrdiff_t) array + index * sizeof(typeof array[index]))
			 *	var
			 */
			t0 = expr_typeof(block->scope, e->expr0);
			t1 = expr_typeof(block->scope, e->expr0->expr0);
			assert(type_is_pointer(t1)
			    || type_is_array(t1));

			e0 = expr_dup(e->expr0->expr0);
			e1 = expr_cast(type_ptrdiff(), e0);
			e2 = expr_dup(e->expr0->expr1);
			e3 = expr_sizeof_type(t0);
			e4 = expr_mul(e2, e3);
			e5 = expr_add(e1, e4);

			t0 = type_amphersand(t0);
			t0 = type_pure(t0);

			var = simplify_declaration_add(block->scope, t0,
					identifier_tmp());

			e6 = expr_cast(t0, e5);

			s0 = stmt_expr(expr_assign(expr_identifier(var), e6));
			
			e0 = expr_identifier(var);

			stmt_prepend(block, s, s0);
			expr_cp(e, e0);
			expr_change = 1;
			break;

		default:
			assert(0);
		}
		break;

	case EXPR_TYPE_CONVERSION:
	case EXPR_INV:
	case EXPR_NEG:
	case EXPR_NOT:
        case EXPR_LEFT:
        case EXPR_RIGHT:
        case EXPR_EQUAL:
        case EXPR_NOT_EQUAL:
        case EXPR_LESS:
        case EXPR_LESS_EQUAL:
        case EXPR_GREATER:
        case EXPR_GREATER_EQUAL:
        case EXPR_ADD:
        case EXPR_SUB:
        case EXPR_MUL:
        case EXPR_DIV:
        case EXPR_MOD:
        case EXPR_AND:
        case EXPR_OR:
        case EXPR_XOR:
        case EXPR_FUNC:
		/*
		 * Replace
		 *		complex-expression
		 * by
		 *		var = complex-expression
		 *		var
		 */
		t0 = expr_typeof(block->scope, e);
		t0 = type_pure(t0);

		var = simplify_declaration_add(block->scope,
				t0, identifier_tmp());

		s0 = stmt_expr(expr_assign(expr_identifier(var),
				expr_dup(e)));
		e0 = expr_identifier(var);

		stmt_prepend(block, s, s0);
		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_SHORT_AND:
		/*
		 * Replace
		 *	 		e0 && e1
		 * by
		 *	0:		if (! e0) goto l0;
		 *	1:		if (! e1) goto l0;
		 *	2:		tmp = 1;
		 *	3:		goto l1;
		 *	4:	l0:	;
		 *	5:		tmp = 0;
		 *	6:	l1:	;
		 *			tmp
		 */
		label0 = label_new(identifier_tmp());
		label1 = label_new(identifier_tmp());

		var = simplify_declaration_add(block->scope,
				type_int(), identifier_tmp());

		e0 = expr_not(expr_dup(e->expr0));
		s0 = stmt_if(e0, stmt_goto(label0), NULL);

		e0 = expr_not(expr_dup(e->expr1));
		s1 = stmt_if(e0, stmt_goto(label0), NULL);

		e0 = expr_assign(expr_identifier(var),
				expr_integer(1));
		s2 = stmt_expr(e0);

		s3 = stmt_goto(label1);

		s4 = stmt_label(label0, stmt_null());

		e0 = expr_assign(expr_identifier(var),
				expr_integer(0));
		s5 = stmt_expr(e0);

		s6 = stmt_label(label1, stmt_null());

		stmt_prepend(block, s, s0);
		stmt_prepend(block, s, s1);
		stmt_prepend(block, s, s2);
		stmt_prepend(block, s, s3);
		stmt_prepend(block, s, s4);
		stmt_prepend(block, s, s5);
		stmt_prepend(block, s, s6);
		expr_cp(e, expr_identifier(var));
		expr_change = 1;
		break;

	case EXPR_SHORT_OR:
		/*
		 * Replace
		 * 			e0 || e1
		 * by
		 *	0:		if (e0) goto l0;
		 *	1:		if (e1) goto l0;
		 *	2:		tmp = 0;
		 *	3:		goto l1;
		 *	4:	l0:	;
		 *	5:		tmp = 1;
		 *	6:	l1:	;
		 *			tmp
		 */
		label0 = label_new(identifier_tmp());
		label1 = label_new(identifier_tmp());

		var = simplify_declaration_add(block->scope,
				type_int(), identifier_tmp());

		e0 = expr_dup(e->expr0);
		s0 = stmt_if(e0, stmt_goto(label0), NULL);

		e0 = expr_dup(e->expr1);
		s1 = stmt_if(e0, stmt_goto(label0), NULL);

		e0 = expr_assign(expr_identifier(var),
				expr_integer(0));
		s2 = stmt_expr(e0);

		s3 = stmt_goto(label1);

		s4 = stmt_label(label0, stmt_null());

		e0 = expr_assign(expr_identifier(var),
				expr_integer(1));
		s5 = stmt_expr(e0);

		s6 = stmt_label(label1, stmt_null());

		stmt_prepend(block, s, s0);
		stmt_prepend(block, s, s1);
		stmt_prepend(block, s, s2);
		stmt_prepend(block, s, s3);
		stmt_prepend(block, s, s4);
		stmt_prepend(block, s, s5);
		stmt_prepend(block, s, s6);
		expr_cp(e, expr_identifier(var));
		expr_change = 1;
		break;
		
	case EXPR_CONDITION:
		/*
		 * Replace
		 * 			e0 ? e1 : e2
		 * by
		 *	0:		if (e0) goto l0;
		 *	1:		tmp = e2;
		 *	2:		goto l1;
		 *	3:	l0:	;
		 *	4:		tmp = e1;
		 *	5:	l1:	;
		 *			tmp
		 */
		label0 = label_new(identifier_tmp());
		label1 = label_new(identifier_tmp());

		t0 = expr_typeof(block->scope, e);
		t0 = type_pure(t0);

		var = simplify_declaration_add(block->scope,
				t0, identifier_tmp());

		e0 = expr_dup(e->expr0);
		s0 = stmt_if(e0, stmt_goto(label0), NULL);

		e0 = expr_assign(expr_identifier(var),
				expr_dup(e->expr2));
		s1 = stmt_expr(e0);

		s2 = stmt_goto(label1);

		s3 = stmt_label(label0, stmt_null());

		e0 = expr_assign(expr_identifier(var),
				expr_dup(e->expr1));
		s4 = stmt_expr(e0);

		s5 = stmt_label(label1, stmt_null());

		stmt_prepend(block, s, s0);
		stmt_prepend(block, s, s1);
		stmt_prepend(block, s, s2);
		stmt_prepend(block, s, s3);
		stmt_prepend(block, s, s4);
		stmt_prepend(block, s, s5);
		expr_cp(e, expr_identifier(var));
		expr_change = 1;
		break;

	case EXPR_LIST:
		/*
		 * Replace
		 *		e0, e1, ..., eNm1, eN
		 * by
		 *		e0;
		 *		e1;
		 *		...
		 *		eNm1;
		 *		eN
		 */
		assert(e->first);
		while (e->first != e->last) {
			e0 = e->first;

			/* Remove first entry from list. */
			e->first = e0->next;
			e0->next->prev = NULL;

			/* Add as separate statement. */
			s0 = stmt_expr(e0);

			stmt_prepend(block, s, s0);
		}

		assert(e->first == e->last);

		e0 = expr_dup(e->first);

		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_PRE_INC:
	case EXPR_PRE_DEC:
		/*
		 * Replace
		 *	++e0		--e0
		 * by
		 *	e0 = e0 + 1;	e0 = e0 - 1;
		 *	e0		e0
		 */
		expr_simplify_in_expr(block, s, e->expr0);

		e0 = expr_dup(e->expr0);
		e1 = expr_dup(e->expr0);
		e2 = expr_integer(1);
		if (e->type == EXPR_PRE_INC) {
			e3 = expr_add(e1, e2);
		} else {
			e3 = expr_sub(e1, e2);
		}
		e4 = expr_assign(e0, e3);
		s0 = stmt_expr(e4);

		e0 = expr_dup(e->expr0);

		stmt_prepend(block, s, s0);
		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_POST_INC:
	case EXPR_POST_DEC:
		/*
		 * Replace
		 *	e0++		e0--
		 * by
		 *	var = e0;	var = e0;
		 *	e0 = e0 + 1;	e0 = e0 - 1;
		 *	var		var
		 */
		expr_simplify_in_expr(block, s, e->expr0);

		t0 = expr_typeof(block->scope, e->expr0);

		var = simplify_declaration_add(block->scope,
				t0, identifier_tmp());

		e0 = expr_identifier(var);
		e1 = expr_dup(e->expr0);
		e2 = expr_assign(e0, e1);
		s0 = stmt_expr(e2);

		e0 = expr_dup(e->expr0);
		e1 = expr_dup(e->expr0);
		e2 = expr_integer(1);
		if (e->type == EXPR_POST_INC) {
			e3 = expr_add(e1, e2);
		} else {
			e3 = expr_sub(e1, e2);
		}
		e4 = expr_assign(e0, e3);
		s1 = stmt_expr(e4);

		e0 = expr_identifier(var);

		stmt_prepend(block, s, s0);
		stmt_prepend(block, s, s1);
		expr_cp(e, e0);
		expr_change = 1;
		break;

	case EXPR_ASSIGN:
	case EXPR_LEFT_ASSIGN:
	case EXPR_RIGHT_ASSIGN:
	case EXPR_ADD_ASSIGN:
	case EXPR_SUB_ASSIGN:
	case EXPR_MUL_ASSIGN:
	case EXPR_DIV_ASSIGN:
	case EXPR_MOD_ASSIGN:
	case EXPR_AND_ASSIGN:
	case EXPR_OR_ASSIGN:
	case EXPR_XOR_ASSIGN:
		expr_simplify_in_expr(block, s, e->expr0);

		s0 = stmt_expr(expr_dup(e));
		e0 = expr_dup(e->expr0);

		stmt_prepend(block, s, s0);
		expr_cp(e, e0);
		expr_change = 1;
		break;
	}
}

static void
expr_simplify_in_stmt(struct stmt *block, struct stmt *s)
{
	struct label *label0;
	struct label *label1;
	struct label *label2;
	struct type *t0;
	struct type *t1;
	struct type *t2;
	struct declaration *f;
	struct expr *ce;
	struct expr *e0;
	struct expr *e1;
	struct expr *e2;
	struct expr *e3;
	struct expr *e4;
	struct expr *e5;
	struct expr *e6;
	struct stmt *s0;
	struct stmt *s1;
	struct stmt *s2;
	struct stmt *s3;
	struct stmt *s4;
	struct stmt *s5;
	struct constraint *c;

	switch (s->type) {
	case STMT_NONE:
		assert(0);
	case STMT_WHILE:
	case STMT_DO_WHILE:
	case STMT_FOR:
	case STMT_BLOCK:
	case STMT_CASE:
	case STMT_DEFAULT:
	case STMT_CONTINUE:
	case STMT_BREAK:
		assert(0);
	case STMT_LABEL:
	case STMT_NULL:
	case STMT_GOTO:
	case STMT_VA_START:
	case STMT_VA_END:
		/* Nothing to do... */
		break;

	case STMT_EXPR:
		switch (s->expr0->type) {
		case EXPR_NONE:
			assert(0);
		case EXPR_BRACES:
			assert(0);

		case EXPR_INTEGER:
		case EXPR_REAL:
		case EXPR_STRING:
		case EXPR_IDENTIFIER:
		case EXPR_SIZEOF_TYPE:
		case EXPR_SIZEOF_EXPR:
		case EXPR_BUILTIN_CONSTANT_P:
		case EXPR_BUILTIN_OFFSETOF:
			/*
			 * Remove expression with no effect.
			 */
			stmt_replace_1_by_0(block, s);
			expr_change = 1;
			break;

		case EXPR_BUILTIN_VA_ARG:
			/* Already simple. */
			break;

		case EXPR_TYPE_CONVERSION:
		case EXPR_STAR:
		case EXPR_AMPHERSAND:
		case EXPR_NEG:
		case EXPR_INV:
		case EXPR_NOT:
		case EXPR_DOT:
		case EXPR_ARROW:
			/*
			 * Unary expressions.
			 */
			e0 = expr_dup(s->expr0->expr0);

			expr_cp(s->expr0, e0);
			expr_change = 1;
			break;

		case EXPR_LEFT:
		case EXPR_RIGHT:
		case EXPR_ADD:
		case EXPR_SUB:
		case EXPR_MUL:
		case EXPR_DIV:
		case EXPR_MOD:
		case EXPR_AND:
		case EXPR_OR:
		case EXPR_XOR:
		case EXPR_EQUAL:
		case EXPR_NOT_EQUAL:
		case EXPR_LESS:
		case EXPR_LESS_EQUAL:
		case EXPR_GREATER:
		case EXPR_GREATER_EQUAL:
		case EXPR_ARRAY:
			/*
			 * Binary expressions.
			 */
			e0 = expr_dup(s->expr0->expr0);
			s0 = stmt_expr(e0);

			e0 = expr_dup(s->expr0->expr1);

			stmt_prepend(block, s, s0);
			expr_cp(s->expr0, e0);
			expr_change = 1;
			break;

		case EXPR_SHORT_AND:
			/*
			 * Replace
			 *			e0 && e1;
			 * by
			 *	0:		if (! e0) goto l0;
			 *	1:		e1;
			 *	2:	l0:	;
			 */
			label0 = label_new(identifier_tmp());
			
			e0 = expr_not(expr_dup(s->expr0->expr0));
			s0 = stmt_if(e0, stmt_goto(label0), NULL);

			e0 = expr_dup(s->expr0->expr1);
			s1 = stmt_expr(e0);

			s2 = stmt_label(label0, stmt_null());

			stmt_replace_1_by_3(block, s, s0, s1, s2);
			expr_change = 1;
			break;

		case EXPR_SHORT_OR:
			/*
			 * Replace
			 *			e0 || e1;
			 * by
			 *	0:		if (e0) goto l0;
			 *	1:		e1;
			 *	2:	l0:	;
			 */
			label0 = label_new(identifier_tmp());

			e0 = expr_dup(s->expr0->expr0);
			s0 = stmt_if(e0, stmt_goto(label0), NULL);

			e0 = expr_dup(s->expr0->expr1);
			s1 = stmt_expr(e0);

			s2 = stmt_label(label0, stmt_null());

			stmt_replace_1_by_3(block, s, s0, s1, s2);
			expr_change = 1;
			break;

		case EXPR_CONDITION:
			/*
			 * Replace
			 *			e0 ? e1 : e2;
			 * by
			 *	0:		if (e0) goto l0;
			 *	1:		e2;
			 *	2:		goto l1;
			 *	3:	l0:	;
			 *	4:		e1;
			 *	5:	l1:	;
			 */
			label0 = label_new(identifier_tmp());
			label1 = label_new(identifier_tmp());

			e0 = expr_dup(s->expr0->expr0);
			s0 = stmt_if(e0, stmt_goto(label0), NULL);

			e0 = expr_dup(s->expr0->expr2);
			s1 = stmt_expr(e0);

			s2 = stmt_goto(label1);

			s3 = stmt_label(label0, stmt_null());

			e0 = expr_dup(s->expr0->expr1);
			s4 = stmt_expr(e0);

			s5 = stmt_label(label1, stmt_null());

			stmt_replace_1_by_6(block, s, s0, s1, s2, s3, s4, s5);
			expr_change = 1;
			break;

		case EXPR_LIST:
			/*
			 * Replace
			 *		e0, e1, ..., eN;
			 * by
			 *		e0;
			 *		e1;
			 *		...
			 *		eN;
			 */
			assert(s->expr0->first);

			while (s->expr0->first != s->expr0->last) {
				e0 = s->expr0->first;

				/* Remove first entry from list. */
				s->expr0->first = e0->next;
				e0->next->prev = NULL;

				s0 = stmt_expr(e0);

				stmt_prepend(block, s, s0);
			}

			assert(s->expr0->first);
			assert(s->expr0->first == s->expr0->last);

			e0 = expr_dup(s->expr0->first);
			s0 = stmt_expr(e0);

			stmt_replace_1_by_1(block, s, s0);
			expr_change = 1;
			break;

		case EXPR_PRE_INC:
		case EXPR_PRE_DEC:
		case EXPR_POST_INC:
		case EXPR_POST_DEC:
			expr_simplify_in_expr(block, s, s->expr0->expr0);

			e0 = expr_dup(s->expr0->expr0);
			e1 = expr_dup(s->expr0->expr0);
			e2 = expr_integer(1);
			if (s->expr0->type == EXPR_PRE_INC
			 || s->expr0->type == EXPR_POST_INC) {
				e3 = expr_add(e1, e2);
			} else {
				e3 = expr_sub(e1, e2);
			}
			e4 = expr_assign(e0, e3);
			expr_cp(s->expr0, e4);
			expr_change = 1;
			break;

		case EXPR_ASSIGN:
			expr_simplify_in_expr(block, s, s->expr0->expr0);

			t0 = expr_typeof(block->scope, s->expr0->expr0);
			t1 = expr_typeof(block->scope, s->expr0->expr1);
			t1 = type_pure(t1);

			if (! type_equal(t0, t1)) {
				e0 = expr_dup(s->expr0->expr1);
				e1 = expr_cast(t0, e0);
				expr_cp(s->expr0->expr1, e1);
				expr_change = 1;
			}

			if (s->expr0->expr0->type != EXPR_IDENTIFIER
			 || ! identifier_is_tmp(s->expr0->expr0->declaration->identifier)) {
				/*
				 * Store operation.
				 * RHS must be simple.
				 */
				expr_localize(block, s, s->expr0->expr1);
				break;
			}

			switch (s->expr0->expr1->type) {
			case EXPR_NONE:
				assert(0);
			case EXPR_BRACES:
				assert(0);

			case EXPR_INTEGER:
			case EXPR_REAL:
			case EXPR_STRING:
			case EXPR_BUILTIN_VA_ARG:
			case EXPR_IDENTIFIER:
				/* Already simple. */
				break;

			case EXPR_SIZEOF_TYPE:
			case EXPR_SIZEOF_EXPR:
			case EXPR_BUILTIN_CONSTANT_P:
			case EXPR_BUILTIN_OFFSETOF:

			case EXPR_DOT:
			case EXPR_ARROW:
			case EXPR_ARRAY:
			case EXPR_AMPHERSAND:

			case EXPR_SHORT_OR:
			case EXPR_SHORT_AND:
			case EXPR_CONDITION:
			case EXPR_LIST:

			case EXPR_PRE_INC:
			case EXPR_PRE_DEC:
			case EXPR_POST_INC:
			case EXPR_POST_DEC:
			case EXPR_ASSIGN:
			case EXPR_LEFT_ASSIGN:
			case EXPR_RIGHT_ASSIGN:
			case EXPR_ADD_ASSIGN:
			case EXPR_SUB_ASSIGN:
			case EXPR_MUL_ASSIGN:
			case EXPR_DIV_ASSIGN:
			case EXPR_MOD_ASSIGN:
			case EXPR_AND_ASSIGN:
			case EXPR_OR_ASSIGN:
			case EXPR_XOR_ASSIGN:
				/* Special case. */
				expr_simplify_in_expr(block, s,
						s->expr0->expr1);
				expr_localize(block, s,
						s->expr0->expr1);
				break;

			case EXPR_STAR:
			case EXPR_TYPE_CONVERSION:
			case EXPR_NOT:
				expr_simplify_in_expr(block, s,
						s->expr0->expr1->expr0);
				expr_localize(block, s,
						s->expr0->expr1->expr0);
				break;

			case EXPR_NEG:
			case EXPR_INV:
				/* Unary expression. */
				t0 = expr_typeof(block->scope,
						s->expr0->expr1->expr0);

				t2 = type_arithmetic(t0, t0);

				if (! type_equal(t0, t2)) {
					e0 = expr_dup(s->expr0->expr1->expr0);
					e1 = expr_cast(t2, e0);

					expr_cp(s->expr0->expr1->expr0, e1);
					expr_change = 1;
				}

				expr_simplify_in_expr(block, s,
						s->expr0->expr1->expr0);
				expr_localize(block, s,
						s->expr0->expr1->expr0);
				break;

			case EXPR_EQUAL:
			case EXPR_NOT_EQUAL:
			case EXPR_LESS:
			case EXPR_LESS_EQUAL:
			case EXPR_GREATER:
			case EXPR_GREATER_EQUAL:
			case EXPR_LEFT:
			case EXPR_RIGHT:
			case EXPR_ADD:
			case EXPR_SUB:
			case EXPR_MUL:
			case EXPR_DIV:
			case EXPR_MOD:
			case EXPR_AND:
			case EXPR_OR:
			case EXPR_XOR:
				t0 = expr_typeof(block->scope,
						s->expr0->expr1->expr0);
				t1 = expr_typeof(block->scope,
						s->expr0->expr1->expr1);

				if (s->expr0->expr1->type == EXPR_ADD
				 && type_is_pointer(t0)
				 && type_is_integer(t1)) {
					/*
					 * Replace
					 * 	x + y
					 * by
					 *	(typeof x) ((int) x + y * sizeof(typeof *x))
					 */
					if (type_is_array(t0)) {
						t0 = type_array_access(t0);
						t0 = type_amphersand(t0);
					}
					e0 = expr_dup(s->expr0->expr1->expr0);
					e1 = expr_cast(type_ptrdiff(), e0);
					e2 = expr_dup(s->expr0->expr1->expr1);
					e3 = expr_sizeof_type(type_star(t0));
					e4 = expr_mul(e2, e3);
					e5 = expr_add(e1, e4);
					e6 = expr_cast(t0, e5);

					expr_cp(s->expr0->expr1, e6);
					expr_change = 1;

				} else if (s->expr0->expr1->type == EXPR_ADD
					&& type_is_integer(t0)
					&& type_is_pointer(t1)) {
					/*
					 * Replace
					 * 	x + y
					 * by
					 *	(typeof y) ((int) y + x * sizeof(typeof *y))
					 */
					if (type_is_array(t1)) {
						t1 = type_array_access(t1);
						t1 = type_amphersand(t1);
					}
					e0 = expr_dup(s->expr0->expr1->expr1);
					e1 = expr_cast(type_ptrdiff(), e0);
					e2 = expr_dup(s->expr0->expr1->expr0);
					e3 = expr_sizeof_type(type_star(t1));
					e4 = expr_mul(e2, e3);
					e5 = expr_add(e1, e4);
					e6 = expr_cast(t1, e5);

					expr_cp(s->expr0->expr1, e6);
					expr_change = 1;

				} else if (s->expr0->expr1->type == EXPR_SUB
					&& type_is_pointer(t0)
					&& type_is_integer(t1)) {
					/*
					 * Replace
					 * 	x - y
					 * by
					 *	(typeof x) ((int) x - y * sizeof(typeof *x))
					 */
					if (type_is_array(t0)) {
						t0 = type_array_access(t0);
						t0 = type_amphersand(t0);
					}
					e0 = expr_dup(s->expr0->expr1->expr0);
					e1 = expr_cast(type_ptrdiff(), e0);
					e2 = expr_dup(s->expr0->expr1->expr1);
					e3 = expr_sizeof_type(type_star(t0));
					e4 = expr_mul(e2, e3);
					e5 = expr_sub(e1, e4);
					e6 = expr_cast(t0, e5);

					expr_cp(s->expr0->expr1, e6);
					expr_change = 1;

				} else if (s->expr0->expr1->type == EXPR_SUB
					&& type_is_pointer(t0)
					&& type_is_pointer(t1)) {
					/*
					 * Replace
					 * 	x - y
					 * by
					 *	((int) x - (int) y) / sizeof(typeof *x))
					 */
					if (type_is_array(t0)) {
						t0 = type_array_access(t0);
						t0 = type_amphersand(t0);
					}
					if (type_is_array(t1)) {
						t1 = type_array_access(t1);
						t1 = type_amphersand(t1);
					}
					e0 = expr_dup(s->expr0->expr1->expr0);
					e1 = expr_cast(type_ptrdiff(), e0);
					e2 = expr_dup(s->expr0->expr1->expr1);
					e3 = expr_cast(type_ptrdiff(), e2);
					e4 = expr_sub(e1, e3);
					e5 = expr_sizeof_type(type_star(t0));
					e6 = expr_div(e4, e5);

					expr_cp(s->expr0->expr1, e6);
					expr_change = 1;

				} else {
					/*
					 * Standard binary operation.
					 */

					t2 = type_arithmetic(t0, t1);

					if (! type_equal(t0, t2)) {
						e0 = expr_dup(s->expr0->expr1->expr0);
						e1 = expr_cast(t2, e0);

						expr_cp(s->expr0->expr1->expr0, e1);
						expr_change = 1;
					}
					if (! type_equal(t1, t2)) {
						e0 = expr_dup(s->expr0->expr1->expr1);
						e1 = expr_cast(t2, e0);

						expr_cp(s->expr0->expr1->expr1, e1);
						expr_change = 1;
					}
					
					/* Binary expression. */
					expr_simplify_in_expr(block, s,
							s->expr0->expr1->expr0);
					expr_localize(block, s,
							s->expr0->expr1->expr0);
					expr_simplify_in_expr(block, s,
							s->expr0->expr1->expr1);
					expr_localize(block, s,
							s->expr0->expr1->expr1);
				}
				break;

			case EXPR_FUNC:
				t0 = expr_typeof(block->scope,
						s->expr0->expr1->expr0);
				assert(t0->type == TYPE_FUNCTION);

				expr_simplify_in_expr(block, s,
						s->expr0->expr1->expr0);
				/* expr_localize? FIXME */
				ce = s->expr0->expr1->expr1->first;
				f = t0->parameter->declaration_first;
				for (;;) {
					if (ce
					 && ! f) {
						assert(0); /* FIXME */
					}
					if (! ce
					 && f
					 && f->type_name->type != TYPE_ELIPSIS) {
						assert(0); /* FIXME */
					}
					if (! ce
					 && ! f) {
						break;
					}
					if (! ce
					 && f->type_name->type == TYPE_ELIPSIS) {
						break;
					}

					/* Formal Type */
					if (f->type_name->type == TYPE_ELIPSIS) {
						t0 = expr_typeof(block->scope, ce);
						t0 = type_pure(t0);
					} else {
						t0 = f->type_name;
						t0 = type_pure(t0);
					}
					t0 = type_arithmetic(t0, t0);

					/* Actual Type */
					t2 = expr_typeof(block->scope, ce);
					t2 = type_pure(t2);

					if (! type_equal(t0, t2)) {
						e0 = expr_dup(ce);
						e1 = expr_cast(t0, e0);

						expr_cp(ce, e1);
						expr_change = 1;
					}

					expr_simplify_in_expr(block, s, ce);
					expr_localize(block, s, ce);

					ce = ce->next;
					if (f->type_name->type != TYPE_ELIPSIS) {
						f = f->next;
					}
				}
				break;
			}
			break;

		case EXPR_LEFT_ASSIGN:
		case EXPR_RIGHT_ASSIGN:
		case EXPR_ADD_ASSIGN:
		case EXPR_SUB_ASSIGN:
		case EXPR_MUL_ASSIGN:
		case EXPR_DIV_ASSIGN:
		case EXPR_MOD_ASSIGN:
		case EXPR_AND_ASSIGN:
		case EXPR_OR_ASSIGN:
		case EXPR_XOR_ASSIGN:
			expr_simplify_in_expr(block, s, s->expr0->expr0);

			e0 = expr_dup(s->expr0->expr0);
			e1 = expr_dup(s->expr0->expr0);
			e2 = expr_dup(s->expr0->expr1);
			switch (s->expr0->type) {
			case EXPR_LEFT_ASSIGN: e3 = expr_left(e1, e2); break;
			case EXPR_RIGHT_ASSIGN: e3 = expr_right(e1, e2); break;
			case EXPR_ADD_ASSIGN: e3 = expr_add(e1, e2); break;
			case EXPR_SUB_ASSIGN: e3 = expr_sub(e1, e2); break;
			case EXPR_MUL_ASSIGN: e3 = expr_mul(e1, e2); break;
			case EXPR_DIV_ASSIGN: e3 = expr_div(e1, e2); break;
			case EXPR_MOD_ASSIGN: e3 = expr_mod(e1, e2); break;
			case EXPR_AND_ASSIGN: e3 = expr_and(e1, e2); break;
			case EXPR_OR_ASSIGN: e3 = expr_or(e1, e2); break;
			case EXPR_XOR_ASSIGN: e3 = expr_xor(e1, e2); break;
			default:
				assert(0);
			}
			e4 = expr_assign(e0, e3);

			expr_cp(s->expr0, e4);
			expr_change = 1;
			break;

		case EXPR_FUNC:
			t0 = expr_typeof(block->scope,
					s->expr0->expr0);
			assert(t0->type == TYPE_FUNCTION);

			expr_simplify_in_expr(block, s, s->expr0->expr0);
			/* expr_localize? FIXME */

			ce = s->expr0->expr1->first;
			f = t0->parameter->declaration_first;
			for (;;) {
				if (ce
				 && ! f) {
					assert(0); /* FIXME */
				}
				if (! ce
				 && f
				 && f->type_name->type != TYPE_ELIPSIS) {
					assert(0); /* FIXME */
				}
				if (! ce
				 && ! f) {
					break;
				}
				if (! ce
				 && f->type_name->type == TYPE_ELIPSIS) {
					break;
				}

				/* Formal Type */
				if (f->type_name->type == TYPE_ELIPSIS) {
					t0 = expr_typeof(block->scope, ce);
					t0 = type_pure(t0);
				} else {
					t0 = f->type_name;
					t0 = type_pure(t0);
				}
				t0 = type_arithmetic(t0, t0);

				/* Actual Type */
				t2 = expr_typeof(block->scope, ce);
				t2 = type_pure(t2);

				if (! type_equal(t0, t2)) {
					e0 = expr_dup(ce);
					e1 = expr_cast(t0, e0);

					expr_cp(ce, e1);
					expr_change = 1;
				}

				expr_simplify_in_expr(block, s, ce);
				expr_localize(block, s, ce);

				ce = ce->next;
				if (f->type_name->type != TYPE_ELIPSIS) {
					f = f->next;
				}
			}
			break;
		}
		break;

	case STMT_IF:
		switch (s->expr0->type) {
		case EXPR_NOT:
			switch (s->expr0->expr0->type) {
			case EXPR_NOT:
				/*
				 * Replace
				 *		if (! ! e0) goto l0;
				 * by
				 *		if (e0) goto l0;
				 */
				e0 = expr_dup(s->expr0->expr0->expr0);

				expr_cp(s->expr0, e0);
				expr_change = 1;
				break;

			case EXPR_EQUAL:
			case EXPR_NOT_EQUAL:
			case EXPR_LESS:
			case EXPR_LESS_EQUAL:
			case EXPR_GREATER:
			case EXPR_GREATER_EQUAL:
				/*
				 * Replace
				 *		if (! (e0 op e1)) goto l0;
				 * by
				 *		if (e0 !op e1) goto l0;
				 */
				/* Correct only for integer/pointer! */
				/* FIXME */
				e0 = expr_dup(s->expr0->expr0->expr0);
				e1 = expr_dup(s->expr0->expr0->expr1);
				switch (s->expr0->expr0->type) {
				case EXPR_EQUAL: e2 = expr_not_equal(e0, e1); break;
				case EXPR_NOT_EQUAL: e2 = expr_equal(e0, e1); break;
				case EXPR_LESS: e2 = expr_greater_equal(e0, e1); break;
				case EXPR_LESS_EQUAL: e2 = expr_greater(e0, e1); break;
				case EXPR_GREATER: e2 = expr_less_equal(e0, e1); break;
				case EXPR_GREATER_EQUAL: e2 = expr_less(e0, e1); break;
				default: assert(0);
				}
				expr_cp(s->expr0, e2);
				expr_change = 1;
				break;

			case EXPR_SHORT_AND:
				/*
				 * Replace
				 * 			if (! (e0 && e1)) goto l0;
				 * by
				 *	0:		if (! e0) goto l0;
				 *	1:		if (! e1) goto l0;
				 */
				e0 = expr_not(expr_dup(s->expr0->expr0->expr0));
				s0 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);

				e0 = expr_not(expr_dup(s->expr0->expr0->expr1));
				s1 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);
				
				stmt_replace_1_by_2(block, s, s0, s1);
				expr_change = 1;
				break;

			case EXPR_SHORT_OR:
				/*
				 * Replace
				 *			if (! (e0 || e1)) goto l0;
				 * by
				 *	0:		if (e0) goto l1;
				 *	1:		if (! e1) goto l0;
				 *	2:	l1:	;
				 */
				label1 = label_new(identifier_tmp());

				e0 = expr_dup(s->expr0->expr0->expr0);
				s0 = stmt_if(e0, stmt_goto(label1), NULL);

				e0 = expr_not(expr_dup(s->expr0->expr0->expr1));
				s1 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);
				
				s2 = stmt_label(label1, stmt_null());
				
				stmt_replace_1_by_3(block, s, s0, s1, s2);
				expr_change = 1;
				break;

			case EXPR_CONDITION:
				/*
				 * Replace
				 *			if (! (e0 ? e1 : e2)) goto l0;
				 * by
				 *	0:		if (e0) goto l1;
				 *	1:		if (! e2) goto l0;
				 *	2:		goto l2;
				 *	3:	l1:	;
				 *	4:		if (! e1) goto l0;
				 *	5:	l2:	;
				 */
				label1 = label_new(identifier_tmp());
				label2 = label_new(identifier_tmp());

				e0 = expr_dup(s->expr0->expr0->expr0);
				s0 = stmt_if(e0, stmt_goto(label1), NULL);

				e0 = expr_not(expr_dup(s->expr0->expr0->expr2));
				s1 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);

				s2 = stmt_goto(label2);

				s3 = stmt_label(label1, stmt_null());

				e0 = expr_not(expr_dup(s->expr0->expr0->expr1));
				s4 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);

				s5 = stmt_label(label2, stmt_null());
				
				stmt_replace_1_by_6(block, s,
						s0, s1, s2, s3, s4, s5);
				expr_change = 1;
				break;

			default:
				/*
				 * Replace
				 *		if (! e) goto l0;
				 * by
				 *		if (e == 0) goto l0;
				 */
				e0 = expr_equal(expr_dup(s->expr0->expr0),
						expr_integer(0));

				expr_cp(s->expr0, e0);
				expr_change = 1;
				break;
			}
			break;

		case EXPR_EQUAL:
		case EXPR_NOT_EQUAL:
		case EXPR_LESS:
		case EXPR_LESS_EQUAL:
		case EXPR_GREATER:
		case EXPR_GREATER_EQUAL:
			t0 = expr_typeof(block->scope, s->expr0->expr0);
			t0 = type_pure(t0);
			t1 = expr_typeof(block->scope, s->expr0->expr1);
			t1 = type_pure(t1);

			t2 = type_arithmetic(t0, t1);

			if (! type_equal(t0, t2)) {
				e0 = expr_dup(s->expr0->expr0);
				e1 = expr_cast(t2, e0);
				expr_cp(s->expr0->expr0, e1);
				expr_change = 1;
			}
			if (! type_equal(t1, t2)) {
				e0 = expr_dup(s->expr0->expr1);
				e1 = expr_cast(t2, e0);
				expr_cp(s->expr0->expr1, e1);
				expr_change = 1;
			}

			expr_simplify_in_expr(block, s, s->expr0->expr0);
			expr_localize(block, s, s->expr0->expr0);
			expr_simplify_in_expr(block, s, s->expr0->expr1);
			expr_localize(block, s, s->expr0->expr1);
			break;

		case EXPR_SHORT_AND:
			/*
			 * Replace
			 *			if (e0 && e1) goto l0;
			 * by
			 *	0:		if (! e0) goto l1;
			 *	1:		if (e1) goto l0;
			 *	2:	l1:	;
			 */
			label1 = label_new(identifier_tmp());

			e0 = expr_not(expr_dup(s->expr0->expr0));
			s0 = stmt_if(e0, stmt_goto(label1), NULL);

			e0 = expr_dup(s->expr0->expr1);
			s1 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);

			s2 = stmt_label(label1, stmt_null());

			stmt_replace_1_by_3(block, s, s0, s1, s2);
			expr_change = 1;
			break;

		case EXPR_SHORT_OR:
			/*
			 * Replace
			 *			if (e0 || e1) goto l0;
			 * by
			 *	0:		if (e0) goto l0;
			 *	1:		if (e1) goto l0;
			 */
			e0 = expr_dup(s->expr0->expr0);
			s0 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);

			e0 = expr_dup(s->expr0->expr1);
			s1 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);

			stmt_replace_1_by_2(block, s, s0, s1);
			expr_change = 1;
			break;

		case EXPR_CONDITION:
			/*
			 * Replace
			 *			if (e0 ? e1 : e2) goto l0;
			 * by
			 *	0:		if (e0) goto l1;
			 *	1:		if (e2) goto l0;
			 *	2:		goto l2;
			 *	3:	l1:	;
			 *	4:		if (e1) goto l0;
			 *	5:	l2:	;
			 */
			label1 = label_new(identifier_tmp());
			label2 = label_new(identifier_tmp());

			e0 = expr_dup(s->expr0->expr0);
			s0 = stmt_if(e0, stmt_goto(label1), NULL);

			e0 = expr_dup(s->expr0->expr2);
			s1 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);

			s2 = stmt_goto(label2);

			s3 = stmt_label(label1, stmt_null());

			e0 = expr_dup(s->expr0->expr1);
			s4 = stmt_if(e0, stmt_goto(s->stmt0->label), NULL);

			s5 = stmt_label(label2, stmt_null());

			stmt_replace_1_by_6(block, s, s0, s1, s2, s3, s4, s5);
			expr_change = 1;
			break;

		default:
			/*
			 * Replace
			 *		if (e) goto l0;
			 * by
			 *		if (e != 0) goto l0;
			 */
			e0 = expr_not_equal(expr_dup(s->expr0), expr_integer(0));

			expr_cp(s->expr0, e0);
			expr_change = 1;
			break;
		}
		break;

	case STMT_SWITCH:
		t0 = expr_typeof(block->scope, s->expr0);

		if (! type_equal(t0, type_int())
		 && ! type_equal(t0, type_unsigned_int())) {
			e0 = expr_dup(s->expr0);
			e1 = expr_cast(type_int(), e0);
			expr_cp(s->expr0, e1);
			expr_change = 1;
		}

		expr_simplify_in_expr(block, s, s->expr0);
		expr_localize(block, s, s->expr0);
		break;

	case STMT_RETURN:
		if (s->expr0) {
			expr_simplify_in_expr(block, s, s->expr0);
			expr_localize(block, s, s->expr0);
		}
		break;

	case STMT_ASM:
		if (s->output) {
			for (c = s->output->first; c; c = c->next) {
				expr_simplify_in_expr(block, s, c->expr);
				assert(c->expr->type == EXPR_IDENTIFIER
				    || c->expr->type == EXPR_STAR);
			}
		}
		if (s->input) {
			for (c = s->input->first; c; c = c->next) {
				expr_simplify_in_expr(block, s, c->expr);
				expr_localize(block, s, c->expr);
				assert(c->expr->type == EXPR_INTEGER
				    || c->expr->type == EXPR_REAL
				    || c->expr->type == EXPR_STRING
				    || c->expr->type == EXPR_AMPHERSAND
				    || c->expr->type == EXPR_IDENTIFIER);
			}
		}
		break;

	default:
		assert(0);
	}
}

int
expr_simplify(struct scope *s, struct declaration *dion)
{
	struct stmt *cs;
	struct declaration *cd;

	expr_change = 0;

	if (dion->initializer) {
		expr_simplify_in_init(s, dion->initializer);

	} else if (dion->stmt) {
		assert(dion->stmt->type == STMT_BLOCK);

		for (cd = dion->stmt->scope->declaration_first;
		    cd;
		    cd = cd->next) {
			if (cd->initializer) {
				expr_simplify_in_init(dion->stmt->scope,
						cd->initializer);
			}
		}

		for (cs = dion->stmt->stmt_first; cs; ) {
			struct stmt *next;

			next = cs->next;

			expr_simplify_in_stmt(dion->stmt, cs);

			cs = next;
		}
	}

	return expr_change;
}

/* ------------------------------------------------------------------------- */

static void
expr_stat_in_expr(struct expr *e)
{
	struct expr *ce;

	switch (e->type) {
	case EXPR_IDENTIFIER:
		e->declaration->rcount++;
		break;

	case EXPR_AMPHERSAND:
		switch (e->expr0->type) {
		case EXPR_IDENTIFIER:
			e->expr0->declaration->acount++;
			break;

		case EXPR_STAR:
			expr_stat_in_expr(e->expr0);
			break;

		default:
			assert(0);
		}
		break;

	case EXPR_FUNC:
		expr_stat_in_expr(e->expr0);
		for (ce = e->expr1->first; ce; ce = ce->next) {
			expr_stat_in_expr(ce);
		}
		break;

	default:
		if (e->expr0) {
			expr_stat_in_expr(e->expr0);
		}
		if (e->expr1) {
			expr_stat_in_expr(e->expr1);
		}
		assert(! e->expr2);
	}
}

static void
expr_stat_in_func(struct stmt *fs)
{
	struct stmt *cs;
	struct expr *ce;
	struct constraint *c;

	for (cs = fs->stmt_first; cs; cs = cs->next) {
		switch (cs->type) {
		case STMT_NONE:
			assert(0);

		case STMT_CASE:
		case STMT_DEFAULT:
		case STMT_WHILE:
		case STMT_DO_WHILE:
		case STMT_FOR:
		case STMT_CONTINUE:
		case STMT_BREAK:
		case STMT_BLOCK:
			/* Should have been replaced by stmt_simplify. */
			assert(0);

		case STMT_NULL:
		case STMT_LABEL:
		case STMT_GOTO:
			break;

		case STMT_EXPR:
			switch (cs->expr0->type) {
			case EXPR_ASSIGN:
				if (cs->expr0->expr0->type == EXPR_IDENTIFIER) {
					cs->expr0->expr0->declaration->assign_expr
							= cs->expr0->expr1;
					cs->expr0->expr0->declaration->wcount++;
				} else if (cs->expr0->expr0->type == EXPR_STAR) {
					expr_stat_in_expr(cs->expr0->expr0);
				}
				expr_stat_in_expr(cs->expr0->expr1);
				break;

			case EXPR_FUNC:
				expr_stat_in_expr(cs->expr0->expr0);
				for (ce = cs->expr0->expr1->first;
				    ce;
				    ce = ce->next) {
					expr_stat_in_expr(ce);
				}
				break;

			default:
				/* Expression not used. */
				expr_stat_in_expr(cs->expr0);
				break;
			}
			break;

		case STMT_IF:
		case STMT_SWITCH:
			expr_stat_in_expr(cs->expr0);
			break;

		case STMT_RETURN:
			if (cs->expr0) {
				expr_stat_in_expr(cs->expr0);
			}
			break;

		case STMT_VA_START:
		case STMT_VA_END:
			/* FIXME */
			break;

		case STMT_ASM:
			if (cs->output) {
				for (c = cs->output->first; c; c = c->next) {
					if (c->expr->type == EXPR_IDENTIFIER) {
						c->expr->declaration->wcount++;
						c->expr->declaration->rcount++;
					}
				}
			}
			if (cs->input) {
				for (c = cs->input->first; c; c = c->next) {
					expr_stat_in_expr(c->expr);
				}
			}
			break;

		default:
			assert(0);
		}
	}
}

static int
expr_ssa_check(struct scope *scope, struct expr *e)
{
	switch (e->type) {
	case EXPR_NONE:
		assert(0);
	case EXPR_BRACES:
		assert(0);
	case EXPR_SIZEOF_TYPE:
	case EXPR_SIZEOF_EXPR:
	case EXPR_BUILTIN_CONSTANT_P:
	case EXPR_BUILTIN_OFFSETOF:
	case EXPR_DOT:
	case EXPR_ARROW:
	case EXPR_ARRAY:
	case EXPR_SHORT_AND:
	case EXPR_SHORT_OR:
	case EXPR_CONDITION:
	case EXPR_LIST:
	case EXPR_PRE_INC:
	case EXPR_PRE_DEC:
	case EXPR_POST_INC:
	case EXPR_POST_DEC:
	case EXPR_ASSIGN:
	case EXPR_LEFT_ASSIGN:
	case EXPR_RIGHT_ASSIGN:
	case EXPR_ADD_ASSIGN:
	case EXPR_SUB_ASSIGN:
	case EXPR_MUL_ASSIGN:
	case EXPR_DIV_ASSIGN:
	case EXPR_MOD_ASSIGN:
	case EXPR_AND_ASSIGN:
	case EXPR_OR_ASSIGN:
	case EXPR_XOR_ASSIGN:
		assert(0);

	case EXPR_INTEGER:
	case EXPR_REAL:
	case EXPR_STRING:
		return 1;

	case EXPR_AMPHERSAND:
		assert(e->expr0->type == EXPR_IDENTIFIER);
		return 1;

	case EXPR_IDENTIFIER:
		if (e->declaration->storage != STORAGE_PARAM
		 && e->declaration->storage != STORAGE_AUTO
		 && e->declaration->storage != STORAGE_REGISTER) {
			/* Not local. */
			return 0;
		}
		if (2 <= e->declaration->wcount) {
			/* Many writes exists. */
			return 0;
		}
		if (1 <= e->declaration->acount) {
			/* Alias exists. */
			return 0;
		}
		return 1;

	case EXPR_STAR:
	case EXPR_TYPE_CONVERSION:
	case EXPR_NEG:
	case EXPR_INV:
	case EXPR_NOT:
		return expr_ssa_check(scope, e->expr0);

	case EXPR_EQUAL:
	case EXPR_NOT_EQUAL:
	case EXPR_LESS:
	case EXPR_LESS_EQUAL:
	case EXPR_GREATER:
	case EXPR_GREATER_EQUAL:
	case EXPR_LEFT:
	case EXPR_RIGHT:
	case EXPR_ADD:
	case EXPR_SUB:
	case EXPR_MUL:
	case EXPR_DIV:
	case EXPR_MOD:
	case EXPR_AND:
	case EXPR_OR:
	case EXPR_XOR:
		return expr_ssa_check(scope, e->expr0)
		    && expr_ssa_check(scope, e->expr0);

	case EXPR_BUILTIN_VA_ARG:
	case EXPR_FUNC:
		return 0;
	}
	assert(0);
	return 0;
}

static void
expr_ssa(struct stmt *fs)
{
	struct declaration *dion;
	struct declaration *next;

	/*
	 * Initialize counters.
	 */
	for (dion = fs->scope->function->type_name->parameter->declaration_first;
	    dion;
	    dion = dion->next) {
		assert(dion->storage == STORAGE_PARAM);

		dion->assign_expr = NULL;
		dion->acount = 0;
		dion->wcount = 1; /* Set by caller. */
		dion->rcount = 0;
	}
	for (dion = fs->scope->declaration_first;
	    dion;
	    dion = dion->next) {
		if (dion->storage == STORAGE_TYPEDEF
		 || ! dion->identifier) {
			continue;
		}

		if (dion->storage == STORAGE_NONE) {
			dion->storage = STORAGE_AUTO;
		}

		dion->assign_expr = NULL;
		dion->acount = 0;
		dion->wcount = 0;
		dion->rcount = 0;
	}

	/*
	 * Do counting.
	 */
	expr_stat_in_func(fs);

	for (dion = fs->scope->function->type_name->parameter->declaration_first;
	    dion;
	    dion = dion->next) {
		dion->assign_expr = NULL;
	}
	for (dion = fs->scope->declaration_first; dion; dion = dion->next) {
		if (dion->storage == STORAGE_TYPEDEF
		 || ! dion->identifier) {
			continue;
		}

#if 0
		fprintf(stderr, "ssa: %s: %d %d %d\n", dion->identifier,
				dion->acount, dion->wcount, dion->rcount);
#endif

		if (1 <= dion->acount
		 || 2 <= dion->wcount
		 || ! dion->assign_expr
		 || ! expr_ssa_check(fs->scope, dion->assign_expr)) {
			dion->assign_expr = NULL;
		}
	}

	/*
	 * Remove variables not used.
	 */
	for (dion = fs->scope->declaration_first; dion; dion = next) {
		next = dion->next;

		if (dion->storage == STORAGE_TYPEDEF
		 || ! dion->identifier) {
			continue;
		}

		if (dion->acount == 0
		 && dion->wcount == 0
		 && dion->rcount == 0) {
			/* Variable not used. */
			if (dion->prev) {
				dion->prev->next = dion->next;
			} else {
				fs->scope->declaration_first = dion->next;
			}
			if (dion->next) {
				dion->next->prev = dion->prev;
			} else {
				fs->scope->declaration_last = dion->prev;
			}

			/* Free declaration. */
			/* FIXME */
		}
	}
}

static struct expr *
simplify_expr_lookup(struct scope *scope, struct expr *e)
{
	assert(e->type == EXPR_IDENTIFIER);

	if (e->declaration->storage != STORAGE_PARAM
	 && e->declaration->storage != STORAGE_AUTO
	 && e->declaration->storage != STORAGE_REGISTER) {
		return NULL;
	} else {
		return e->declaration->assign_expr;
	}
}

static void
expr_optimize_in_expr(struct scope *scope, struct expr *e)
{
	struct expr *ce;
	struct expr *ce0;
	struct expr *e0;
	struct expr *e1;
	struct expr *e2;
	struct type *ts0;
	struct type *ts1;
	struct type *ts2;
	unsigned long long val;
	long double fval;

	if (e->expr0) {
		expr_optimize_in_expr(scope, e->expr0);
	}
	if (e->expr1) {
		if (e->type == EXPR_FUNC) {
			for (ce = e->first; ce; ce = ce->next) {
				expr_optimize_in_expr(scope, ce);
			}
		} else {
			expr_optimize_in_expr(scope, e->expr1);
		}
	}
	assert(! e->expr2);
	if (e->type == EXPR_BRACES) {
		for (ce = e->first; ce; ce = ce->next) {
			expr_optimize_in_expr(scope, ce);
		}
	}

	switch (e->type) {
	case EXPR_NONE:
		assert(0);

	case EXPR_SIZEOF_TYPE:
	case EXPR_SIZEOF_EXPR:
	case EXPR_BUILTIN_CONSTANT_P:
	case EXPR_BUILTIN_OFFSETOF:
	case EXPR_DOT:
	case EXPR_ARROW:
	case EXPR_ARRAY:
	case EXPR_PRE_INC:
	case EXPR_PRE_DEC:
	case EXPR_POST_INC:
	case EXPR_POST_DEC:
	case EXPR_LEFT_ASSIGN:
	case EXPR_RIGHT_ASSIGN:
	case EXPR_ADD_ASSIGN:
	case EXPR_SUB_ASSIGN:
	case EXPR_MUL_ASSIGN:
	case EXPR_DIV_ASSIGN:
	case EXPR_MOD_ASSIGN:
	case EXPR_AND_ASSIGN:
	case EXPR_OR_ASSIGN:
	case EXPR_XOR_ASSIGN:
	case EXPR_SHORT_AND:
	case EXPR_SHORT_OR:
	case EXPR_CONDITION:
	case EXPR_LIST:
		/* Should have been replaced by expr_simplify. */
		assert(0);

	case EXPR_INTEGER:
	case EXPR_REAL:
	case EXPR_STRING:
	case EXPR_BUILTIN_VA_ARG:
		/* Already simple. */
		break;

	case EXPR_IDENTIFIER:
		ce0 = simplify_expr_lookup(scope, e);
		if (ce0) {
			switch (ce0->type) {
			case EXPR_INTEGER:
			case EXPR_REAL:
			case EXPR_STRING:
			case EXPR_IDENTIFIER:
				/* Use constant/var instead of var. */
				expr_cp(e, expr_dup(ce0));
				expr_change = 1;
				break;
			default:
				break;
			}
		}
		break;

	case EXPR_TYPE_CONVERSION:
		switch (e->expr0->type) {
		case EXPR_INTEGER:
			switch (e->type_name->type) {
			case TYPE_NONE:
			case TYPE_ENUM:
			case TYPE_MAX:
				assert(0);

			case TYPE_VOID:
				assert(0);

			case TYPE_INT8:
			case TYPE_INT16:
			case TYPE_INT32:
			case TYPE_INT64:
				switch (e->expr0->type_name->type) {
				case TYPE_INT8:
					val = (int64_t) (int8_t) e->expr0->integer;
					break;
				case TYPE_INT16:
					val = (int64_t) (int16_t) e->expr0->integer;
					break;
				case TYPE_INT32:
					val = (int64_t) (int32_t) e->expr0->integer;
					break;
				case TYPE_INT64:
					val = (int64_t) (int64_t) e->expr0->integer;
					break;
				case TYPE_UINT8:
					val = (int64_t) (uint8_t) e->expr0->integer;
					break;
				case TYPE_UINT16:
					val = (int64_t) (uint16_t) e->expr0->integer;
					break;
				case TYPE_UINT32:
					val = (int64_t) (uint32_t) e->expr0->integer;
					break;
				case TYPE_UINT64:
					val = (int64_t) (uint64_t) e->expr0->integer;
					break;

				default:
					assert(0);
				}

				e0 = expr_new();
				e0->type = EXPR_INTEGER;
				e0->integer = val;
				e0->type_name = e->type_name;

				expr_cp(e, e0);
				expr_change = 1;
				break;

			case TYPE_UINT8:
			case TYPE_UINT16:
			case TYPE_UINT32:
			case TYPE_UINT64:
				switch (e->expr0->type_name->type) {
				case TYPE_INT8:
					val = (uint64_t) (int8_t) e->expr0->integer;
					break;
				case TYPE_INT16:
					val = (uint64_t) (int16_t) e->expr0->integer;
					break;
				case TYPE_INT32:
					val = (uint64_t) (int32_t) e->expr0->integer;
					break;
				case TYPE_INT64:
					val = (uint64_t) (int64_t) e->expr0->integer;
					break;
				case TYPE_UINT8:
					val = (uint64_t) (uint8_t) e->expr0->integer;
					break;
				case TYPE_UINT16:
					val = (uint64_t) (uint16_t) e->expr0->integer;
					break;
				case TYPE_UINT32:
					val = (uint64_t) (uint32_t) e->expr0->integer;
					break;
				case TYPE_UINT64:
					val = (uint64_t) (uint64_t) e->expr0->integer;
					break;

				default:
					assert(0);
				}

				e0 = expr_new();
				e0->type = EXPR_INTEGER;
				e0->integer = val;
				e0->type_name = e->type_name;

				expr_cp(e, e0);
				expr_change = 1;
				break;

			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				switch (e->expr0->type_name->type) {
				case TYPE_INT8:
					fval = (long double) (int8_t) e->expr0->integer;
					break;
				case TYPE_INT16:
					fval = (long double) (int16_t) e->expr0->integer;
					break;
				case TYPE_INT32:
					fval = (long double) (int32_t) e->expr0->integer;
					break;
				case TYPE_INT64:
					fval = (long double) (int64_t) e->expr0->integer;
					break;
				case TYPE_UINT8:
					fval = (long double) (uint8_t) e->expr0->integer;
					break;
				case TYPE_UINT16:
					fval = (long double) (uint16_t) e->expr0->integer;
					break;
				case TYPE_UINT32:
					fval = (long double) (uint32_t) e->expr0->integer;
					break;
				case TYPE_UINT64:
					fval = (long double) (long double) e->expr0->integer;
					break;

				default:
					assert(0);
				}

				e0 = expr_new();
				e0->type = EXPR_REAL;
				e0->real = fval;
				e0->type_name = e->type_name;

				expr_cp(e, e0);
				expr_change = 1;
				break;

			case TYPE_VA_LIST:
			case TYPE_POINTER:
				break;

			default:
				assert(0);
			}
			break;

		case EXPR_REAL:
			switch (e->type_name->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
			case TYPE_INT16:
			case TYPE_UINT16:
			case TYPE_INT32:
			case TYPE_UINT32:
			case TYPE_INT64:
			case TYPE_UINT64:
				e0 = expr_new();
				e0->type = EXPR_INTEGER;
				e0->integer = (int64_t) e->real;
				e0->type_name = e->type_name;

				expr_cp(e, e0);
				expr_change = 1;
				break;

			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				switch (e->expr0->type_name->type) {
				case TYPE_FLOAT32:
					fval = (long double) (float) e->expr0->real;
					break;
				case TYPE_FLOAT64:
					fval = (long double) (double) e->expr0->real;
					break;
				case TYPE_FLOAT80:
					fval = (long double) (long double) e->expr0->real;
					break;
				default:
					assert(0);
				}
				e0 = expr_new();
				e0->type = EXPR_REAL;
				e0->real = fval;
				e0->type_name = e->type_name;

				expr_cp(e, e0);
				expr_change = 1;
				break;

			default:
				assert(0);
			}
			break;

		case EXPR_STRING:
			/* FIXME */
			break;

		case EXPR_TYPE_CONVERSION:
			ts0 = e->type_name;

			ts1 = e->expr0->type_name;

			ts2 = expr_typeof(scope, e->expr0->expr0);

			if (type_is_pointer(ts0)
			 && type_is_ptrdiff(ts1)
			 && type_is_pointer(ts2)) {
				/*
				 * Replace
				 *	(type *) (ptrdiff_t) &e0
				 * by
				 *	(type *) &e0
				 */
				goto type_conversion_replace;
				
			} else if (type_equal(ts0, ts1)) {
				/*
				 * Replace
				 *	(type) (type) e0
				 * by
				 *	(type) e0
				 */
				goto type_conversion_replace;

			} else if (type_is_pointer(ts0)
				&& type_is_pointer(ts1)) {
				/*
				 * Replace
				 *	(type0 *) (type1 *) e0
				 * by
				 *	(type0 *) e0
				 */
				goto type_conversion_replace;

			} else if (type_is_ptrdiff(ts0)
				&& type_is_pointer(ts1)) {
				/*
				 * Replace
				 *	(ptrdiff_t) (type1 *) e0
				 * by
				 *	(ptrdiff_t) e0
				 */
			type_conversion_replace:;
				e0 = expr_dup(e->expr0->expr0);

				expr_cp(e->expr0, e0);
				expr_change = 1;
			}
			break;

		default:
			ts0 = e->type_name;

			ts1 = expr_typeof(scope, e->expr0);

			if (type_equal(ts0, ts1)) {
				/*
				 * Replace
				 *	(type) e0
				 * by
				 *	e0
				 */
				e0 = expr_dup(e->expr0);

				expr_cp(e, e0);
				expr_change = 1;
			}
			break;
		}
		break;

	case EXPR_STAR:
		assert(e->expr0->type == EXPR_IDENTIFIER
		    || (e->expr0->type == EXPR_TYPE_CONVERSION
		     && e->expr0->expr0->type == EXPR_IDENTIFIER)
		    || (e->expr0->type == EXPR_TYPE_CONVERSION
		     && e->expr0->expr0->type == EXPR_INTEGER));

		/* Nothing to optimize. */
		break;

	case EXPR_AMPHERSAND:
		switch (e->expr0->type) {
		case EXPR_DOT:
		case EXPR_ARROW:
		case EXPR_ARRAY:
			/* Should have been replaced by expr_simplify. */
			assert(0);

		case EXPR_IDENTIFIER:
			/* Is already simple. */
			break;

		case EXPR_STAR:
			/*
			 * Replace
			 *	&*e0
			 * by
			 *	e0
			 */
			e0 = expr_dup(e->expr0->expr0);

			expr_cp(e, e0);
			expr_change = 1;
			break;

		default:
			assert(0);
		}
		break;

	case EXPR_NEG:
		switch (e->expr0->type) {
		case EXPR_INTEGER:
			/*
			 * Replace
			 *	-num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = e->expr0->type_name;
			e0->integer = -e->expr0->integer;
			expr_cp(e, e0);
			expr_change = 1;
			break;

		case EXPR_REAL:
			/*
			 * Replace
			 *	-num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_REAL;
			e0->type_name = e->expr0->type_name;
			e0->real = -e->expr0->real;
			expr_cp(e, e0);
			expr_change = 1;
			break;

		case EXPR_SUB:
			/*
			 * Replace
			 *	-(e0 - e1)
			 * by
			 *	e1 - e0
			 */
			e0 = expr_dup(e->expr0->expr0);
			e1 = expr_dup(e->expr0->expr1);
			e2 = expr_sub(e1, e0);
			expr_cp(e, e2);
			expr_change = 1;
			break;

		default:
			/* Cannot simplify. */
			break;
		}
		break;

	case EXPR_INV:
		if (e->expr0->type == EXPR_INTEGER) {
			/*
			 * Replace
			 *	~num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = e->expr0->type_name;
			e0->integer = ~ e->expr0->integer;
			expr_cp(e, e0);
			expr_change = 1;
		}
		break;

	case EXPR_NOT:
		switch (e->expr0->type) {
		case EXPR_NONE:
			assert(0);
		case EXPR_BRACES:
			assert(0);

		case EXPR_SIZEOF_TYPE:
		case EXPR_SIZEOF_EXPR:
		case EXPR_BUILTIN_CONSTANT_P:
		case EXPR_BUILTIN_OFFSETOF:
		case EXPR_DOT:
		case EXPR_ARROW:
		case EXPR_ARRAY:
		case EXPR_ASSIGN:
		case EXPR_LEFT_ASSIGN:
		case EXPR_RIGHT_ASSIGN:
		case EXPR_ADD_ASSIGN:
		case EXPR_SUB_ASSIGN:
		case EXPR_MUL_ASSIGN:
		case EXPR_DIV_ASSIGN:
		case EXPR_MOD_ASSIGN:
		case EXPR_AND_ASSIGN:
		case EXPR_OR_ASSIGN:
		case EXPR_XOR_ASSIGN:
		case EXPR_SHORT_AND:
		case EXPR_SHORT_OR:
		case EXPR_CONDITION:
		case EXPR_LIST:
			/* Should have been simplified by op_assigns. */
			assert(0);

		case EXPR_INTEGER:
			/*
			 * Replace
			 *	! num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_int();
			e0->integer = ! e->expr0->integer;
			expr_cp(e, e0);
			expr_change = 1;
			break;

		case EXPR_EQUAL:
		case EXPR_NOT_EQUAL:
		case EXPR_LESS:
		case EXPR_GREATER:
		case EXPR_LESS_EQUAL:
		case EXPR_GREATER_EQUAL:
			ts0 = expr_typeof(scope, e->expr0->expr0);
			ts1 = expr_typeof(scope, e->expr0->expr1);

			if (((TYPE_INT8 <= ts0->type
			   && ts0->type <= TYPE_UINT64)
			  || type_is_pointer(ts0))
			 && ((TYPE_INT8 <= ts1->type
			   && ts1->type <= TYPE_UINT64)
			  || type_is_pointer(ts1))) {
				/*
				 * Replace
				 *      ! (e0 == e1)    ! (e0 < e1)     ...
				 * by
				 *      e0 != e1        e0 >= e1        ...
				 */

				e0 = expr_dup(e->expr0->expr0);
				e1 = expr_dup(e->expr0->expr1);

				e2 = expr_new();
				switch (e->expr0->type) {
				case EXPR_EQUAL:
					e2->type = EXPR_NOT_EQUAL;
					break;
				case EXPR_NOT_EQUAL:
					e2->type = EXPR_EQUAL;
					break;
				case EXPR_LESS:
					e2->type = EXPR_GREATER_EQUAL;
					break;
				case EXPR_GREATER:
					e2->type = EXPR_LESS_EQUAL;
					break;
				case EXPR_LESS_EQUAL:
					e2->type = EXPR_GREATER;
					break;
				case EXPR_GREATER_EQUAL:
					e2->type = EXPR_LESS;
					break;
				default:
					assert(0);
				}
				e2->expr0 = e0;
				e2->expr1 = e1;

				expr_cp(e, e2);
				expr_change = 1;
			}
			break;

		default:
			/*
			 * Replace
			 *      ! e0
			 * by
			 *      e0 == 0
			 */
			e0 = expr_dup(e->expr0);
			e1 = expr_integer(0);
			e2 = expr_new();
			e2->type = EXPR_EQUAL;
			e2->expr0 = e0;
			e2->expr1 = e1;
			expr_cp(e, e2);
			expr_change = 1;
			break;
		}
		break;

	case EXPR_LEFT:
	case EXPR_RIGHT:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = e->expr0->type_name;
			switch (e->type) {
			case EXPR_LEFT:
				val = e->expr0->integer << e->expr1->integer;
				break;
			case EXPR_RIGHT:
				val = e->expr0->integer >> e->expr1->integer;
				break;
			default:
				assert(0);
			}
			e0->integer = val;
			expr_cp(e, e0);
			expr_change = 1;
		}
		break;

	case EXPR_EQUAL:
	case EXPR_NOT_EQUAL:
	case EXPR_LESS:
	case EXPR_GREATER:
	case EXPR_LESS_EQUAL:
	case EXPR_GREATER_EQUAL:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_int();
			switch (e->type) {
			case EXPR_EQUAL:
				val = e->expr0->integer == e->expr1->integer;
				break;
			case EXPR_NOT_EQUAL:
				val = e->expr0->integer != e->expr1->integer;
				break;
			case EXPR_LESS:
				val = e->expr0->integer < e->expr1->integer;
				break;
			case EXPR_GREATER:
				val = e->expr0->integer > e->expr1->integer;
				break;
			case EXPR_LESS_EQUAL:
				val = e->expr0->integer <= e->expr1->integer;
				break;
			case EXPR_GREATER_EQUAL:
				val = e->expr0->integer >= e->expr1->integer;
				break;
			default:
				assert(0);
			}
			e0->integer = val;
			expr_cp(e, e0);
			expr_change = 1;
		}
		break;

	case EXPR_ADD:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			/*
			 * Replace
			 *	num + num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->integer = e->expr0->integer + e->expr1->integer;
			expr_cp(e, e0);
			expr_change = 1;

		} else if (e->expr0->type == EXPR_REAL
			&& e->expr1->type == EXPR_REAL) {
			/*
			 * Replace
			 *	num + num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_REAL;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->real = e->expr0->real + e->expr1->real;
			expr_cp(e, e0);
			expr_change = 1;

		} else if ((e->expr0->type == EXPR_INTEGER
			 && e->expr0->integer == 0)
			|| (e->expr0->type == EXPR_REAL
			 && e->expr0->real == 0.0)) {
			/*
			 * Replace
			 *      0 + e0
			 * by
			 *      e0
			 */
			expr_cp(e, expr_dup(e->expr1));
			expr_change = 1;

		} else if ((e->expr1->type == EXPR_INTEGER
			 && e->expr1->integer == 0)
			|| (e->expr1->type == EXPR_REAL
			 && e->expr1->real == 0.0)) {
			/*
			 * Replace
			 *      e0 + 0
			 * by
			 *      e0
			 */
			expr_cp(e, expr_dup(e->expr0));
			expr_change = 1;

		} else if (e->expr0->type == EXPR_ADD
			&& (e->expr0->expr1->type == EXPR_INTEGER
			 || e->expr0->expr1->type == EXPR_REAL)
			&& (e->expr1->type == EXPR_INTEGER
			 || e->expr1->type == EXPR_REAL)) {
			/*
			 * Replace
			 *	(e0 + c0) + c1
			 * by
			 *	e0 + (c0 + c1)
			 */
			expr_cp(e, expr_add(
				expr_dup(e->expr0->expr0),
				expr_add(
					expr_dup(e->expr0->expr1),
					expr_dup(e->expr1))));
			expr_change = 1;
		}
		break;

	case EXPR_SUB:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			/*
			 * Replace
			 *	num - num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->integer = e->expr0->integer - e->expr1->integer;
			expr_cp(e, e0);
			expr_change = 1;

		} else if (e->expr0->type == EXPR_REAL
			&& e->expr1->type == EXPR_REAL) {
			/*
			 * Replace
			 *	num - num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_REAL;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->real = e->expr0->real - e->expr1->real;
			expr_cp(e, e0);
			expr_change = 1;

		} else if ((e->expr0->type == EXPR_INTEGER
			 && e->expr0->integer == 0)
			|| (e->expr0->type == EXPR_REAL
			 && e->expr0->real == 0.0)) {
			/*
			 * Replace
			 *      0 - e0
			 * by
			 *      -e0
			 */
			e0 = expr_dup(e->expr1);

			e1 = expr_new();
			e1->type = EXPR_NEG;
			e1->expr0 = e0;

			expr_cp(e, e1);

			expr_change = 1;

		} else if ((e->expr1->type == EXPR_INTEGER
			 && e->expr1->integer == 0)
			|| (e->expr1->type == EXPR_REAL
			 && e->expr1->real == 0.0)) {
			/*
			 * Replace
			 *      e0 - 0
			 * by
			 *      e0
			 */
			expr_cp(e, expr_dup(e->expr0));
			expr_change = 1;
		}
		break;

	case EXPR_MUL:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			/*
			 * Replace
			 * 	num * num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->integer = e->expr0->integer * e->expr1->integer;
			expr_cp(e, e0);
			expr_change = 1;

		} else if ((e->expr0->type == EXPR_INTEGER
			 && e->expr0->integer == 1)
			|| (e->expr0->type == EXPR_REAL
			 && e->expr0->real == 1.0)) {
			/*
			 * Replace
			 *	1 * e1
			 * by
			 * 	e1
			 */
			expr_cp(e, expr_dup(e->expr1));
			expr_change = 1;

		} else if (e->expr0->type == EXPR_INTEGER
			&& (e->expr0->integer == (1ULL << 1)
			 || e->expr0->integer == (1ULL << 2)
			 || e->expr0->integer == (1ULL << 3)
			 || e->expr0->integer == (1ULL << 4)
			 || e->expr0->integer == (1ULL << 5)
			 || e->expr0->integer == (1ULL << 6)
			 || e->expr0->integer == (1ULL << 7)
			 || e->expr0->integer == (1ULL << 8)
			 || e->expr0->integer == (1ULL << 9)
			 || e->expr0->integer == (1ULL << 10)
			 || e->expr0->integer == (1ULL << 11)
			 || e->expr0->integer == (1ULL << 12)
			 || e->expr0->integer == (1ULL << 13)
			 || e->expr0->integer == (1ULL << 14)
			 || e->expr0->integer == (1ULL << 15)
			 || e->expr0->integer == (1ULL << 16)
			 || e->expr0->integer == (1ULL << 17)
			 || e->expr0->integer == (1ULL << 18)
			 || e->expr0->integer == (1ULL << 19)
			 || e->expr0->integer == (1ULL << 20)
			 || e->expr0->integer == (1ULL << 21)
			 || e->expr0->integer == (1ULL << 22)
			 || e->expr0->integer == (1ULL << 23)
			 || e->expr0->integer == (1ULL << 24)
			 || e->expr0->integer == (1ULL << 25)
			 || e->expr0->integer == (1ULL << 26)
			 || e->expr0->integer == (1ULL << 27)
			 || e->expr0->integer == (1ULL << 28)
			 || e->expr0->integer == (1ULL << 29)
			 || e->expr0->integer == (1ULL << 30)
			 || e->expr0->integer == (1ULL << 31)
			 || e->expr0->integer == (1ULL << 32)
			 || e->expr0->integer == (1ULL << 33)
			 || e->expr0->integer == (1ULL << 34)
			 || e->expr0->integer == (1ULL << 35)
			 || e->expr0->integer == (1ULL << 36)
			 || e->expr0->integer == (1ULL << 37)
			 || e->expr0->integer == (1ULL << 38)
			 || e->expr0->integer == (1ULL << 39)
			 || e->expr0->integer == (1ULL << 40)
			 || e->expr0->integer == (1ULL << 41)
			 || e->expr0->integer == (1ULL << 42)
			 || e->expr0->integer == (1ULL << 43)
			 || e->expr0->integer == (1ULL << 44)
			 || e->expr0->integer == (1ULL << 45)
			 || e->expr0->integer == (1ULL << 46)
			 || e->expr0->integer == (1ULL << 47)
			 || e->expr0->integer == (1ULL << 48)
			 || e->expr0->integer == (1ULL << 49)
			 || e->expr0->integer == (1ULL << 50)
			 || e->expr0->integer == (1ULL << 51)
			 || e->expr0->integer == (1ULL << 52)
			 || e->expr0->integer == (1ULL << 53)
			 || e->expr0->integer == (1ULL << 54)
			 || e->expr0->integer == (1ULL << 55)
			 || e->expr0->integer == (1ULL << 56)
			 || e->expr0->integer == (1ULL << 57)
			 || e->expr0->integer == (1ULL << 58)
			 || e->expr0->integer == (1ULL << 59)
			 || e->expr0->integer == (1ULL << 60)
			 || e->expr0->integer == (1ULL << 61)
			 || e->expr0->integer == (1ULL << 62)
			 || e->expr0->integer == (1ULL << 63))) {
			/*
			 * Replace
			 *	(1 << val) * e0
			 * by
			 * 	e0 << val
			 */
			for (val = 0; ; val++) {
				assert(val < 64);
				if (e->expr0->integer == (1ULL << val)) {
					break;
				}
			}

			ce = e->expr0;
			e->expr0 = e->expr1;
			e->expr1 = ce;

			e->type = EXPR_LEFT;
			e->expr1->integer = val;

		} else if ((e->expr1->type == EXPR_INTEGER
			 && e->expr1->integer == 1)
			|| (e->expr1->type == EXPR_REAL
			 && e->expr1->real == 1.0)) {
			/*
			 * Replace
			 *	e0 * 1
			 * by
			 * 	e0
			 */
			expr_cp(e, expr_dup(e->expr0));
			expr_change = 1;

		} else if (e->expr1->type == EXPR_INTEGER
			&& (e->expr1->integer == (1ULL << 1)
			 || e->expr1->integer == (1ULL << 2)
			 || e->expr1->integer == (1ULL << 3)
			 || e->expr1->integer == (1ULL << 4)
			 || e->expr1->integer == (1ULL << 5)
			 || e->expr1->integer == (1ULL << 6)
			 || e->expr1->integer == (1ULL << 7)
			 || e->expr1->integer == (1ULL << 8)
			 || e->expr1->integer == (1ULL << 9)
			 || e->expr1->integer == (1ULL << 10)
			 || e->expr1->integer == (1ULL << 11)
			 || e->expr1->integer == (1ULL << 12)
			 || e->expr1->integer == (1ULL << 13)
			 || e->expr1->integer == (1ULL << 14)
			 || e->expr1->integer == (1ULL << 15)
			 || e->expr1->integer == (1ULL << 16)
			 || e->expr1->integer == (1ULL << 17)
			 || e->expr1->integer == (1ULL << 18)
			 || e->expr1->integer == (1ULL << 19)
			 || e->expr1->integer == (1ULL << 20)
			 || e->expr1->integer == (1ULL << 21)
			 || e->expr1->integer == (1ULL << 22)
			 || e->expr1->integer == (1ULL << 23)
			 || e->expr1->integer == (1ULL << 24)
			 || e->expr1->integer == (1ULL << 25)
			 || e->expr1->integer == (1ULL << 26)
			 || e->expr1->integer == (1ULL << 27)
			 || e->expr1->integer == (1ULL << 28)
			 || e->expr1->integer == (1ULL << 29)
			 || e->expr1->integer == (1ULL << 30)
			 || e->expr1->integer == (1ULL << 31)
			 || e->expr1->integer == (1ULL << 32)
			 || e->expr1->integer == (1ULL << 33)
			 || e->expr1->integer == (1ULL << 34)
			 || e->expr1->integer == (1ULL << 35)
			 || e->expr1->integer == (1ULL << 36)
			 || e->expr1->integer == (1ULL << 37)
			 || e->expr1->integer == (1ULL << 38)
			 || e->expr1->integer == (1ULL << 39)
			 || e->expr1->integer == (1ULL << 40)
			 || e->expr1->integer == (1ULL << 41)
			 || e->expr1->integer == (1ULL << 42)
			 || e->expr1->integer == (1ULL << 43)
			 || e->expr1->integer == (1ULL << 44)
			 || e->expr1->integer == (1ULL << 45)
			 || e->expr1->integer == (1ULL << 46)
			 || e->expr1->integer == (1ULL << 47)
			 || e->expr1->integer == (1ULL << 48)
			 || e->expr1->integer == (1ULL << 49)
			 || e->expr1->integer == (1ULL << 50)
			 || e->expr1->integer == (1ULL << 51)
			 || e->expr1->integer == (1ULL << 52)
			 || e->expr1->integer == (1ULL << 53)
			 || e->expr1->integer == (1ULL << 54)
			 || e->expr1->integer == (1ULL << 55)
			 || e->expr1->integer == (1ULL << 56)
			 || e->expr1->integer == (1ULL << 57)
			 || e->expr1->integer == (1ULL << 58)
			 || e->expr1->integer == (1ULL << 59)
			 || e->expr1->integer == (1ULL << 60)
			 || e->expr1->integer == (1ULL << 61)
			 || e->expr1->integer == (1ULL << 62)
			 || e->expr1->integer == (1ULL << 63))) {
			/*
			 * Replace
			 *	e1 * (1 << val)
			 * by
			 * 	e1 << val
			 */
			for (val = 0; ; val++) {
				assert(val < 64);
				if (e->expr1->integer == (1ULL << val)) {
					break;
				}
			}
			e->type = EXPR_LEFT;
			e->expr1->integer = val;
		}
		break;

	case EXPR_DIV:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			/*
			 * Replace
			 * 	num / num
			 * by
			 * 	num
			 */
			assert(e->expr1->integer != 0);

			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->integer = e->expr0->integer / e->expr1->integer;
			expr_cp(e, e0);
			expr_change = 1;

		} else if ((e->expr1->type == EXPR_INTEGER
			 && e->expr1->integer == 1)
			|| (e->expr1->type == EXPR_REAL
			 && e->expr1->real == 1.0)) {
			/*
			 * Replace
			 *	e1 / 1
			 * by
			 * 	e1
			 */
			expr_cp(e, expr_dup(e->expr0));
			expr_change = 1;

		} else if (e->expr1->type == EXPR_INTEGER
			&& (e->expr1->integer == (1ULL << 1)
			 || e->expr1->integer == (1ULL << 2)
			 || e->expr1->integer == (1ULL << 3)
			 || e->expr1->integer == (1ULL << 4)
			 || e->expr1->integer == (1ULL << 5)
			 || e->expr1->integer == (1ULL << 6)
			 || e->expr1->integer == (1ULL << 7)
			 || e->expr1->integer == (1ULL << 8)
			 || e->expr1->integer == (1ULL << 9)
			 || e->expr1->integer == (1ULL << 10)
			 || e->expr1->integer == (1ULL << 11)
			 || e->expr1->integer == (1ULL << 12)
			 || e->expr1->integer == (1ULL << 13)
			 || e->expr1->integer == (1ULL << 14)
			 || e->expr1->integer == (1ULL << 15)
			 || e->expr1->integer == (1ULL << 16)
			 || e->expr1->integer == (1ULL << 17)
			 || e->expr1->integer == (1ULL << 18)
			 || e->expr1->integer == (1ULL << 19)
			 || e->expr1->integer == (1ULL << 20)
			 || e->expr1->integer == (1ULL << 21)
			 || e->expr1->integer == (1ULL << 22)
			 || e->expr1->integer == (1ULL << 23)
			 || e->expr1->integer == (1ULL << 24)
			 || e->expr1->integer == (1ULL << 25)
			 || e->expr1->integer == (1ULL << 26)
			 || e->expr1->integer == (1ULL << 27)
			 || e->expr1->integer == (1ULL << 28)
			 || e->expr1->integer == (1ULL << 29)
			 || e->expr1->integer == (1ULL << 30)
			 || e->expr1->integer == (1ULL << 31)
			 || e->expr1->integer == (1ULL << 32)
			 || e->expr1->integer == (1ULL << 33)
			 || e->expr1->integer == (1ULL << 34)
			 || e->expr1->integer == (1ULL << 35)
			 || e->expr1->integer == (1ULL << 36)
			 || e->expr1->integer == (1ULL << 37)
			 || e->expr1->integer == (1ULL << 38)
			 || e->expr1->integer == (1ULL << 39)
			 || e->expr1->integer == (1ULL << 40)
			 || e->expr1->integer == (1ULL << 41)
			 || e->expr1->integer == (1ULL << 42)
			 || e->expr1->integer == (1ULL << 43)
			 || e->expr1->integer == (1ULL << 44)
			 || e->expr1->integer == (1ULL << 45)
			 || e->expr1->integer == (1ULL << 46)
			 || e->expr1->integer == (1ULL << 47)
			 || e->expr1->integer == (1ULL << 48)
			 || e->expr1->integer == (1ULL << 49)
			 || e->expr1->integer == (1ULL << 50)
			 || e->expr1->integer == (1ULL << 51)
			 || e->expr1->integer == (1ULL << 52)
			 || e->expr1->integer == (1ULL << 53)
			 || e->expr1->integer == (1ULL << 54)
			 || e->expr1->integer == (1ULL << 55)
			 || e->expr1->integer == (1ULL << 56)
			 || e->expr1->integer == (1ULL << 57)
			 || e->expr1->integer == (1ULL << 58)
			 || e->expr1->integer == (1ULL << 59)
			 || e->expr1->integer == (1ULL << 60)
			 || e->expr1->integer == (1ULL << 61)
			 || e->expr1->integer == (1ULL << 62)
			 || e->expr1->integer == (1ULL << 63))) {
			/*
			 * Replace
			 *	e1 / (1 << val)
			 * by
			 * 	e1 >> val
			 */
			for (val = 0; ; val++) {
				assert(val < 64);
				if (e->expr1->integer == (1ULL << val)) {
					break;
				}
			}
			e->type = EXPR_RIGHT;
			e->expr1->integer = val;
			expr_change = 1;
		}
		break;

	case EXPR_MOD:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			/*
			 * Replace
			 * 	num % num
			 * by
			 * 	num
			 */
			assert(e->expr1->integer != 0);

			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->integer = e->expr0->integer % e->expr1->integer;
			expr_cp(e, e0);
			expr_change = 1;

		} else if ((e->expr1->type == EXPR_INTEGER
			 && e->expr1->integer == 1)
			|| (e->expr1->type == EXPR_REAL
			 && e->expr1->real == 1.0)) {
			/*
			 * Replace
			 *	e1 % 1
			 * by
			 * 	0
			 */
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->integer = 0;
			expr_change = 1;

		} else if (e->expr1->type == EXPR_INTEGER
			&& (e->expr1->integer == (1ULL << 1)
			 || e->expr1->integer == (1ULL << 2)
			 || e->expr1->integer == (1ULL << 3)
			 || e->expr1->integer == (1ULL << 4)
			 || e->expr1->integer == (1ULL << 5)
			 || e->expr1->integer == (1ULL << 6)
			 || e->expr1->integer == (1ULL << 7)
			 || e->expr1->integer == (1ULL << 8)
			 || e->expr1->integer == (1ULL << 9)
			 || e->expr1->integer == (1ULL << 10)
			 || e->expr1->integer == (1ULL << 11)
			 || e->expr1->integer == (1ULL << 12)
			 || e->expr1->integer == (1ULL << 13)
			 || e->expr1->integer == (1ULL << 14)
			 || e->expr1->integer == (1ULL << 15)
			 || e->expr1->integer == (1ULL << 16)
			 || e->expr1->integer == (1ULL << 17)
			 || e->expr1->integer == (1ULL << 18)
			 || e->expr1->integer == (1ULL << 19)
			 || e->expr1->integer == (1ULL << 20)
			 || e->expr1->integer == (1ULL << 21)
			 || e->expr1->integer == (1ULL << 22)
			 || e->expr1->integer == (1ULL << 23)
			 || e->expr1->integer == (1ULL << 24)
			 || e->expr1->integer == (1ULL << 25)
			 || e->expr1->integer == (1ULL << 26)
			 || e->expr1->integer == (1ULL << 27)
			 || e->expr1->integer == (1ULL << 28)
			 || e->expr1->integer == (1ULL << 29)
			 || e->expr1->integer == (1ULL << 30)
			 || e->expr1->integer == (1ULL << 31)
			 || e->expr1->integer == (1ULL << 32)
			 || e->expr1->integer == (1ULL << 33)
			 || e->expr1->integer == (1ULL << 34)
			 || e->expr1->integer == (1ULL << 35)
			 || e->expr1->integer == (1ULL << 36)
			 || e->expr1->integer == (1ULL << 37)
			 || e->expr1->integer == (1ULL << 38)
			 || e->expr1->integer == (1ULL << 39)
			 || e->expr1->integer == (1ULL << 40)
			 || e->expr1->integer == (1ULL << 41)
			 || e->expr1->integer == (1ULL << 42)
			 || e->expr1->integer == (1ULL << 43)
			 || e->expr1->integer == (1ULL << 44)
			 || e->expr1->integer == (1ULL << 45)
			 || e->expr1->integer == (1ULL << 46)
			 || e->expr1->integer == (1ULL << 47)
			 || e->expr1->integer == (1ULL << 48)
			 || e->expr1->integer == (1ULL << 49)
			 || e->expr1->integer == (1ULL << 50)
			 || e->expr1->integer == (1ULL << 51)
			 || e->expr1->integer == (1ULL << 52)
			 || e->expr1->integer == (1ULL << 53)
			 || e->expr1->integer == (1ULL << 54)
			 || e->expr1->integer == (1ULL << 55)
			 || e->expr1->integer == (1ULL << 56)
			 || e->expr1->integer == (1ULL << 57)
			 || e->expr1->integer == (1ULL << 58)
			 || e->expr1->integer == (1ULL << 59)
			 || e->expr1->integer == (1ULL << 60)
			 || e->expr1->integer == (1ULL << 61)
			 || e->expr1->integer == (1ULL << 62)
			 || e->expr1->integer == (1ULL << 63))) {
			/*
			 * Replace
			 *	e0 % (1 << val)
			 * by
			 * 	e0 & (val - 1)
			 */
			for (val = 0; ; val++) {
				assert(val < 64);
				if (e->expr1->integer == (1ULL << val)) {
					break;
				}
			}
			e->type = EXPR_AND;
			e->expr1->integer = (1ULL << val) - 1;
			expr_change = 1;
		}
		break;

	case EXPR_AND:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			/*
			 * Replace
			 * 	num & num
			 * by
			 *	num
			 */
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			e0->integer = e->expr0->integer & e->expr1->integer;
			expr_cp(e, e0);
			expr_change = 1;

		} else if (e->expr0->type == EXPR_INTEGER
			&& e->expr0->integer == 0xffffffff) {
			/*
			 * Replace
			 * 	-1 & e1
			 * by
			 *	e1
			 */
			expr_cp(e, expr_dup(e->expr1));
			expr_change = 1;

		} else if (e->expr1->type == EXPR_INTEGER
			&& e->expr1->integer == 0xffffffff) {
			/*
			 * Replace
			 * 	e0 & -1
			 * by
			 *	e0
			 */
			expr_cp(e, expr_dup(e->expr0));
			expr_change = 1;
		}
		break;

	case EXPR_OR:
	case EXPR_XOR:
		if (e->expr0->type == EXPR_INTEGER
		 && e->expr1->type == EXPR_INTEGER) {
			e0 = expr_new();
			e0->type = EXPR_INTEGER;
			e0->type_name = type_arithmetic(e->expr0->type_name,
					e->expr1->type_name);
			switch (e->type) {
			case EXPR_OR:
				val = e->expr0->integer | e->expr1->integer;
				break;
			case EXPR_XOR:
				val = e->expr0->integer ^ e->expr1->integer;
				break;
			default:
				assert(0);
			}
			e0->integer = val;
			expr_cp(e, e0);
			expr_change = 1;
		}
		break;

	case EXPR_FUNC:
		/* expr_optimize_in_expr(scope, ce); Done above. */
		for (ce = e->expr1->first; ce; ce = ce->next) {
			expr_optimize_in_expr(scope, ce);
		}
		break;

	case EXPR_ASSIGN:
		/* No simplification possible. */
		break;

	case EXPR_BRACES:
		/* Simplification of elements done by loop above. */
		break;
	}
}

static void
expr_optimize_in_stmt(struct stmt *block, struct stmt *s)
{
	struct expr *ce;
	struct constraint *c;
	struct declaration *dion;
	struct stmt *s0;
	struct stmt *s1;

	switch (s->type) {
	case STMT_NONE:
		assert(0);
	case STMT_CASE:
	case STMT_DEFAULT:
	case STMT_WHILE:
	case STMT_DO_WHILE:
	case STMT_FOR:
	case STMT_BREAK:
	case STMT_CONTINUE:
	case STMT_BLOCK:
		assert(0);

	case STMT_NULL:
	case STMT_LABEL:
	case STMT_GOTO:
	case STMT_VA_START:
	case STMT_VA_END:
		/* No expressions; nothing to optimize. */
		break;

	case STMT_EXPR:
		switch (s->expr0->type) {
		case EXPR_NONE:
			assert(0);
		case EXPR_BRACES:
			assert(0);

		case EXPR_SIZEOF_TYPE:
		case EXPR_SIZEOF_EXPR:
		case EXPR_BUILTIN_CONSTANT_P:
		case EXPR_BUILTIN_OFFSETOF:

		case EXPR_DOT:
		case EXPR_ARROW:
		case EXPR_ARRAY:

		case EXPR_PRE_INC:
		case EXPR_PRE_DEC:
		case EXPR_POST_INC:
		case EXPR_POST_DEC:
		case EXPR_LEFT_ASSIGN:
		case EXPR_RIGHT_ASSIGN:
		case EXPR_ADD_ASSIGN:
		case EXPR_SUB_ASSIGN:
		case EXPR_MUL_ASSIGN:
		case EXPR_DIV_ASSIGN:
		case EXPR_MOD_ASSIGN:
		case EXPR_AND_ASSIGN:
		case EXPR_OR_ASSIGN:
		case EXPR_XOR_ASSIGN:

		case EXPR_SHORT_AND:
		case EXPR_SHORT_OR:
		case EXPR_CONDITION:
		case EXPR_LIST:
			/* Should have been removed by expr_simplify. */
			assert(0);

		case EXPR_INTEGER:
		case EXPR_REAL:
		case EXPR_STRING:
		case EXPR_IDENTIFIER:
		case EXPR_AMPHERSAND:
			stmt_replace_1_by_0(block, s);
			expr_change = 1;
			break;

		case EXPR_TYPE_CONVERSION:
		case EXPR_STAR:
		case EXPR_NEG:
		case EXPR_INV:
		case EXPR_NOT:
			/* Unary expressions. */
			s0 = stmt_expr(expr_dup(s->expr0->expr0));

			stmt_replace_1_by_1(block, s, s0);
			expr_change = 1;
			break;

		case EXPR_LEFT:
		case EXPR_RIGHT:
		case EXPR_EQUAL:
		case EXPR_NOT_EQUAL:
		case EXPR_LESS:
		case EXPR_GREATER:
		case EXPR_LESS_EQUAL:
		case EXPR_GREATER_EQUAL:
		case EXPR_ADD:
		case EXPR_SUB:
		case EXPR_MUL:
		case EXPR_DIV:
		case EXPR_MOD:
		case EXPR_AND:
		case EXPR_OR:
		case EXPR_XOR:
			/* Binary expressions. */
			s0 = stmt_expr(expr_dup(s->expr0->expr0));
			s1 = stmt_expr(expr_dup(s->expr0->expr1));

			stmt_replace_1_by_2(block, s, s0, s1);
			expr_change = 1;
			break;

		case EXPR_ASSIGN:
			switch (s->expr0->expr0->type) {
			case EXPR_IDENTIFIER:
				dion = s->expr0->expr0->declaration;

				if ((dion->storage == STORAGE_PARAM
				  || dion->storage == STORAGE_AUTO
				  || dion->storage == STORAGE_REGISTER)
				 && dion->rcount == 0
				 && dion->acount == 0) {
					/*
					 * Variable not used.
					 * Remove LHS.
					 */
					expr_cp(s->expr0,
						expr_dup(s->expr0->expr1));
					expr_change = 1;

				} else {
					/*
					 * Optimize RHS.
					 */
					expr_optimize_in_expr(block->scope,
							s->expr0->expr1);
				}
				break;
			case EXPR_STAR:
				expr_optimize_in_expr(block->scope,
						s->expr0->expr0);
				expr_optimize_in_expr(block->scope,
						s->expr0->expr1);
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_BUILTIN_VA_ARG:
			/* Must leave as is. */
			break;

		case EXPR_FUNC:
			expr_optimize_in_expr(block->scope, s->expr0);
			for (ce = s->expr0->expr1->first; ce; ce = ce->next) {
				expr_optimize_in_expr(block->scope, ce);
			}
			break;
		}
		break;
	
	case STMT_IF:
	case STMT_SWITCH:
		expr_optimize_in_expr(block->scope, s->expr0);
		break;

	case STMT_RETURN:
		if (s->expr0) {
			expr_optimize_in_expr(block->scope, s->expr0);
		}
		break;

	case STMT_ASM:
		if (s->output) {
			for (c = s->output->first; c; c = c->next) {
				expr_optimize_in_expr(block->scope, c->expr);
			}
		}
		if (s->input) {
			for (c = s->input->first; c; c = c->next) {
				expr_optimize_in_expr(block->scope, c->expr);
			}
		}
		break;

	default:
		assert(0);
	}
}

int
expr_optimize(struct scope *s, struct declaration *dion)
{
	struct stmt *cs;
	struct declaration *cd;

	expr_change = 0;

	if (dion->initializer) {
		expr_optimize_in_expr(s, dion->initializer);

	} else if (dion->stmt) {
		assert(dion->stmt->type == STMT_BLOCK);

		for (cd = dion->stmt->scope->declaration_first;
		    cd;
		    cd = cd->next) {
			if (cd->initializer) {
				expr_optimize_in_expr(dion->stmt->scope,
						cd->initializer);
			}
		}

		expr_ssa(dion->stmt);

		for (cs = dion->stmt->stmt_first; cs; ) {
			struct stmt *next;

			next = cs->next;

			expr_optimize_in_stmt(dion->stmt, cs);

			cs = next;
		}
	}

	return expr_change;
}
