/* $Id: simplify.c,v 1.395 2012/02/17 14:17:00 vrsieh 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cc1.h"
#include "identifier.h"
#include "declaration.h"
#include "stmt.h"
#include "expr.h"
#include "print.h"
#include "visitor.h"
#include "simplify.h"

static int simplify_change;


static void
simplify_rename_all_structunionenums_in_stmt(struct stmt *s)
{
	struct stmt *cs;

	if (s->stmt0) {
		simplify_rename_all_structunionenums_in_stmt(s->stmt0);
	}
	if (s->stmt1) {
		simplify_rename_all_structunionenums_in_stmt(s->stmt1);
	}
	for (cs = s->stmt_first; cs; cs = cs->next) {
		simplify_rename_all_structunionenums_in_stmt(cs);
	}

	if (s->type == STMT_BLOCK) {
		struct declaration *dion;

		for (dion = s->scope->declaration_first; dion; dion = dion->next) {
			if (dion->type_name
			 && (dion->type_name->type == TYPE_STRUCT
			  || dion->type_name->type == TYPE_UNION
			  || dion->type_name->type == TYPE_ENUM)
			 && dion->type_name->scope) {
				const char *old;
				const char *new;

				old = dion->type_name->identifier;
				new = identifier_tmp();

				stmt_rename_structunionenum(s, dion->type_name->type, old, new);
			}
		}
	}
}

static void
simplify_rename_all_structunionenums_in_func(struct stmt *fs)
{
	simplify_rename_all_structunionenums_in_stmt(fs);
}

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

	assert(fs->scope->function);
	assert(fs->scope->function->type_name->parameter);

	for (dion = fs->scope->function->type_name->parameter->declaration_first;
	    dion;
	    dion = dion->next) {
		if (declaration_name_get(dion)) {
			declaration_name_set(dion, identifier_tmp());
		}
	}

	for (dion = fs->scope->declaration_first; dion; dion = dion->next) {
		if (declaration_name_get(dion)) {
			declaration_name_set(dion, identifier_tmp());
		}
	}
}

struct declaration *
simplify_declaration_add(
	struct scope *scope,
	struct type *ts,
	const char *name
)
{
	struct declaration *dion;

	dion = declaration_identifier(name);
	dion->type_name = ts;

	dion->prev = scope->declaration_last;
	dion->next = NULL;
	if (dion->prev) {
		dion->prev->next = dion;
	} else {
		scope->declaration_first = dion;
	}
	scope->declaration_last = dion;

	return dion;
}

/* ----------------------------------------------------------------- */
/* Scope-related Simplifications                                     */
/* ----------------------------------------------------------------- */

static int
simplify_fix_dimensions(struct scope *s)
{
	struct scope *cs;

	for (cs = s->child_first; cs; cs = cs->next) {
		simplify_fix_dimensions(cs);
	}

	if (s->type == SCOPE_GLOBAL
	 || s->type == SCOPE_FUNCTION
	 || s->type == SCOPE_BLOCK) {
		struct declaration *dion;

		for (dion = s->declaration_first; dion; dion = dion->next) {
			if (dion->type_name
			 && type_is_array(dion->type_name)
			 && ! type_dimension_get(dion->type_name)
			 && declaration_initializer_get(dion)) {
				struct expr *e;
				int count;

				count = 0;
				for (e = dion->initializer->first;
				    e;
				    e = e->next) {
					count++;
				}
				dion->type_name->dimension
						= expr_integer(count);

				simplify_change = 1;
			}
		}
	}

	return 0;
}

static int
simplify_split_vardecl_init(struct stmt *s)
{
	struct stmt *cs;
	int retval;

	retval = 0;

	if (s->stmt0) {
		retval |= simplify_split_vardecl_init(s->stmt0);
	}
	if (s->stmt1) {
		retval |= simplify_split_vardecl_init(s->stmt1);
	}
	for (cs = s->stmt_first; cs; cs = cs->next) {
		retval |= simplify_split_vardecl_init(cs);
	}

	if (s->type == STMT_BLOCK) {
		struct declaration *dion;

		for (dion = s->scope->declaration_last;
		    dion;
		    dion = dion->prev) {
			if (dion->storage == STORAGE_NONE
			 || dion->storage == STORAGE_AUTO
			 || dion->storage == STORAGE_REGISTER) {
				struct expr *initializer;

				initializer = declaration_initializer_get(dion);

				if (initializer) {
					struct stmt *s0;

					declaration_initializer_set(dion,
							NULL);

					/* FIXME */
					dion->type_name->mod_const = 0;

					s0 = stmt_expr(expr_assign(
							expr_identifier(dion),
							initializer));

					stmt_prepend(s, s->stmt_first, s0);
					retval |= 1;
				}
			}
		}
	}

	return retval;
}

static int
simplify_merge_declarations_of_blocks_check(struct stmt *s)
{
	struct declaration *dion;

	assert(s->type == STMT_BLOCK);

	for (dion = s->scope->declaration_first; dion; dion = dion->next) {
		assert(dion->type_name);
		if (dion->type_name->scope) {
			assert(! dion->identifier);
		}
		if (dion->storage != STORAGE_STATIC
		 && declaration_initializer_get(dion)) {
			return 0;
		}
	}

	return 1;
}

static int
simplify_merge_declarations_to_func(struct stmt *fb, struct stmt *s)
{
	struct stmt *cs;
	struct declaration *dion;
	int retval;

	retval = 0;

	if (s->stmt0) {
		retval |= simplify_merge_declarations_to_func(fb, s->stmt0);
	}
	if (s->stmt1) {
		retval |= simplify_merge_declarations_to_func(fb, s->stmt1);
	}
	for (cs = s->stmt_first; cs; cs = cs->next) {
		retval |= simplify_merge_declarations_to_func(fb, cs);
	}

	assert(fb->scope->type == SCOPE_FUNCTION);

	if (s->type == STMT_BLOCK
	 && simplify_merge_declarations_of_blocks_check(s)) {
		/*
		 * Move all type and variable definitions from
		 * inner block to outer block. Rename types/variables.
		 */
		for (dion = s->scope->declaration_first; dion; ) {
			struct declaration *next;
			const char *new_name;
			const char *old_name;
			
			next = dion->next;

			/* Remove declaration from inner scope. */
			if (dion->prev) {
				dion->prev->next = dion->next;
			} else {
				s->scope->declaration_first = dion->next;
			}
			if (dion->next) {
				dion->next->prev = dion->prev;
			} else {
				s->scope->declaration_last = dion->prev;
			}

			/* Add declaration to outer scope. */
			dion->prev = fb->scope->declaration_last;
			dion->next = NULL;
			if (dion->prev) {
				dion->prev->next = dion;
			} else {
				fb->scope->declaration_first = dion;
			}
			fb->scope->declaration_last = dion;

			/* Rename declarator. */
			old_name = declaration_name_get(dion);
			if (old_name) {
				new_name = identifier_tmp();
				declaration_name_set(dion, new_name);
			}

			dion = next;

			retval |= 1;
		}
	}

	return retval;
}

static int
simplify_merge_declarations_of_blocks_in_func(struct stmt *fb)
{
	struct stmt *cs;
	int retval;

	retval = 0;

	for (cs = fb->stmt_first; cs; cs = cs->next) {
		retval |= simplify_merge_declarations_to_func(fb, cs);
	}

	return retval;
}

static int
simplify_merge_declarations_of_blocks(struct scope *s)
{
	struct declaration *dion;
	int retval;

	retval = 0;

	for (dion = s->declaration_first; dion; dion = dion->next) {
		if (dion->stmt) {
			retval |= simplify_merge_declarations_of_blocks_in_func(
					dion->stmt);
		}
	}

	return retval;
}

static void
cleanup_func_scope_in_expr(
	struct scope *gs,
	struct scope *fs,
	struct type *tt,
	struct expr *e
)
{
	struct expr *ce;
	struct type *ft;

	if (e->type == EXPR_BRACES
	 || e->type == EXPR_STRING) {
		ft = NULL;
	} else {
		ft = expr_typeof(fs, e);
	}

	switch (e->type) {
	case EXPR_BRACES:
		if (tt->type == TYPE_STRUCT) {
			struct declaration *dion;
			int ret;

			ret = scope_lookup_structunionenum(fs,
					tt->type, tt->identifier, &tt);
			assert(0 <= ret);
			assert(tt);
			assert(tt->scope);

			for (dion = tt->scope->declaration_first, ce = e->first;
			    dion && ce;
			    dion = dion->next, ce = ce->next) {
				cleanup_func_scope_in_expr(gs, fs,
						dion->type_name, ce);
			}

		} else if (tt->type == TYPE_ARRAY) {
			for (ce = e->first; ce; ce = ce->next) {
				cleanup_func_scope_in_expr(gs, fs,
						type_array_access(tt), ce);
			}
		} else {
			assert(0);
		}
		break;

	case EXPR_STRING:
		if (tt->type == TYPE_ARRAY) {
			/* Leave as is. */

		} else {
			struct declaration *dion;

			dion = declaration_identifier(identifier_tmp());
			dion->storage = STORAGE_STATIC;
			dion->type_name = type_const_char_array(e->string_len + 1);
			dion->initializer = expr_dup(e);

			scope_declaration_prepend_first(gs, dion);
			expr_cp(e, expr_identifier(dion));
			simplify_change = 1;
		}
		break;

	case EXPR_INTEGER:
	case EXPR_REAL:
	case EXPR_IDENTIFIER:
	case EXPR_BUILTIN_VA_ARG:
	case EXPR_STAR:
	case EXPR_AMPHERSAND:
		break;

	case EXPR_TYPE_CONVERSION:
	case EXPR_NEG:
	case EXPR_INV:
		ft = expr_typeof(fs, e->expr0);
		ft = type_pure(ft);
		cleanup_func_scope_in_expr(gs, fs, ft, e->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:
	case EXPR_ASSIGN:
		ft = expr_typeof(fs, e->expr0);
		ft = type_pure(ft);
		cleanup_func_scope_in_expr(gs, fs, ft, e->expr0);
		ft = expr_typeof(fs, e->expr1);
		ft = type_pure(ft);
		cleanup_func_scope_in_expr(gs, fs, ft, e->expr1);
		break;

	case EXPR_FUNC:
		for (ce = e->expr1->first; ce; ce = ce->next) {
			ft = expr_typeof(fs, ce);
			ft = type_pure(ft);
			cleanup_func_scope_in_expr(gs, fs, ft, ce);
		}
		break;

	default:
		assert(0);
	}
}

static void
cleanup_func_scope_in_func(struct scope *gs, struct declaration *fd)
{
	struct scope *fs;
	struct declaration *dion;
	struct stmt *cs;

	fs = fd->stmt->scope;

	for (dion = fs->declaration_first; dion; dion = dion->next) {
		if (dion->initializer) {
			cleanup_func_scope_in_expr(gs, fs,
					dion->type_name, dion->initializer);
		}
	}

	for (dion = fs->declaration_first; dion;) {
		struct declaration *next;

		next = dion->next;

		if (dion->storage == STORAGE_STATIC
		 || dion->storage == STORAGE_TYPEDEF
		 || dion->identifier == NULL) {
			if (dion->identifier) {
				/* Rename static variable/type definition. */
				dion->identifier = identifier_tmp();

			} else if (dion->type_name->scope) {
				/* Rename struct/union/enum definition. */
				const char *old;
				const char *new;

				assert(dion->type_name->type == TYPE_STRUCT
				    || dion->type_name->type == TYPE_UNION
				    || dion->type_name->type == TYPE_ENUM);

				old = dion->type_name->identifier;
				new = identifier_tmp();

				stmt_rename_structunionenum(fd->stmt,
						dion->type_name->type,
						old, new);

				dion->type_name->identifier = new;
			}

			/* Remove it from functions scope. */
			if (dion->prev) {
				dion->prev->next = dion->next;
			} else {
				fs->declaration_first = dion->next;
			}
			if (dion->next) {
				dion->next->prev = dion->prev;
			} else {
				fs->declaration_last = dion->prev;
			}

			/* Add it to global scope. */
			dion->prev = fd->prev;
			dion->next = fd;
			if (dion->prev) {
				dion->prev->next = dion;
			} else {
				gs->declaration_first = dion;
			}
			dion->next->prev = dion;

			simplify_change = 1;
		}

		dion = next;
	}

	for (cs = fd->stmt->stmt_first; cs; cs = cs->next) {
		struct constraint *c;
		struct type *t;

		if (cs->expr0) {
			t = expr_typeof(fd->stmt->scope, cs->expr0);
			t = type_pure(t);
			cleanup_func_scope_in_expr(gs, fs, t, cs->expr0);
		}
		if (cs->output) {
			for (c = cs->output->first; c; c = c->next) {
				t = expr_typeof(fd->stmt->scope, c->expr);
				t = type_pure(t);
				cleanup_func_scope_in_expr(gs, fs, t, c->expr);
			}
		}
		if (cs->input) {
			for (c = cs->input->first; c; c = c->next) {
				t = expr_typeof(fd->stmt->scope, c->expr);
				t = type_pure(t);
				cleanup_func_scope_in_expr(gs, fs, t, c->expr);
			}
		}
	}
}

static int
cleanup_func_scope(struct scope *scope, struct declaration *dion)
{
	if (dion->initializer) {
		cleanup_func_scope_in_expr(scope, scope,
				dion->type_name, dion->initializer);
	} else if (dion->stmt) {
		cleanup_func_scope_in_func(scope, dion);
	}

	return 0;
}

/* ----------------------------------------------------------------- */
/* Expression-related Simplifications                                */
/* ----------------------------------------------------------------- */

static void
simplify_inline_in_expr(struct stmt *block, struct stmt *s, struct expr *e)
{
	struct expr *ce;
	struct declaration *dor0;
	int retval;

	if (e->expr0) {
		simplify_inline_in_expr(block, s, e->expr0);
	}
	if (e->expr1) {
		simplify_inline_in_expr(block, s, e->expr1);
	}
	if (e->expr2) {
		simplify_inline_in_expr(block, s, e->expr2);
	}
	for (ce = e->first; ce; ce = ce->next) {
		simplify_inline_in_expr(block, s, ce);
	}

	if (e->type == EXPR_FUNC
	 && e->expr0->type == EXPR_IDENTIFIER) {
		dor0 = e->expr0->declaration;
		assert(dor0);

		if (dor0->mod_inline) {
			/*
			 * Replace
			 * 	inline func(p0, ..., pN) {
			 * 		dion0;
			 * 		...
			 * 		dionM;
			 *
			 * 		s0;
			 * 		...;
			 * 		return r;
			 * 	}
			 * 	...
			 *	func(a0, ..., aN)
			 * by
			 * 		...
			 * 		p0;
			 * 		...
			 * 		pN;
			 * 		dion0;
			 * 		...
			 * 		dionM;
			 *
			 *		p0 = e0;
			 *		...
			 *		pN = eN;
			 *		s0;
			 *		...
			 *		r
			 *
			 * Rename all struct/union/enums, types, vars and labels.
			 */
#if 1
			struct type *ts1;
			struct scope *param;
			struct declaration *p;
			struct expr *a;
			struct stmt *cs;
			struct stmt *s0;
			struct expr *e0;

			assert(dor0->stmt);
			simplify_rename_all_structunionenums_in_func(dor0->stmt);
			simplify_rename_all_typevars_in_func(dor0->stmt);

			assert(dor0->type_name->type == TYPE_FUNCTION);
			assert(dor0->type_name->parameter);

			param = dor0->type_name->parameter;

			/* Clone parameters. */
			for (p = param->declaration_first;
			    p;
			    p = p->next) {
				const char *pn;

				pn = declaration_name_get(p);

				if (! pn) {
					continue;
				}

				declaration_type_get(&ts1, p);
				ts1 = type_pure(ts1);

				p->clone = simplify_declaration_add(
						block->scope, ts1, pn);
			}

			/* Clone local variables. */
			for (p = dor0->stmt->scope->declaration_first;
			    p;
			    p = p->next) {
				const char *pn;
				struct expr *pi;

				pn = declaration_name_get(p);
				pi = declaration_initializer_get(p);

				declaration_type_get(&ts1, p);

				p->clone = simplify_declaration_add(
						block->scope, ts1, pn);
				p->clone->storage = p->storage;
				p->clone->initializer = pi;
			}

			/* Clone local labels. */
			for (cs = dor0->stmt->stmt_first;
			    cs && cs->type != STMT_RETURN;
			    cs = cs->next) {
				if (cs->type == STMT_LABEL) {
					cs->label->clone = label_new(identifier_tmp());
				}
			}

			/* Copy actual values to formal parameters. */
			a = e->expr1->first;
			for (p = param->declaration_first; p; p = p->next) {
				const char *pn;

				assert(p);
				pn = declaration_name_get(p);
				if (! pn) {
					/* void parameter */
					continue;
				}
				assert(a);

				e0 = expr_assign(
					expr_identifier(p->clone),
					expr_dup(a));
				s0 = stmt_expr(e0);

				stmt_prepend(block, s, s0);

				a = a->next;
			}

			/* Copy statements. */
			for (cs = dor0->stmt->stmt_first;
			    cs && cs->type != STMT_RETURN;
			    cs = cs->next) {
				s0 = stmt_dup(cs);

				stmt_prepend(block, s, s0);
			}
			assert(! cs
			    || (cs->type == STMT_RETURN && ! cs->next));

			/* Use return value (if present). */
			if (cs
			 && cs->expr0) {
				e0 = expr_dup(cs->expr0);
			} else {
				e0 = expr_integer(0);
			}
			expr_cp(e, e0);

			/* Reset clone info. */
			for (cs = dor0->stmt->stmt_first;
			    cs && cs->type != STMT_RETURN;
			    cs = cs->next) {
				if (cs->type == STMT_LABEL) {
					cs->label->clone = NULL;
				}
			}

			for (p = param->declaration_first;
			    p;
			    p = p->next) {
				p->clone = NULL;
			}
			for (p = dor0->stmt->scope->declaration_first;
			    p;
			    p = p->next) {
				p->clone = NULL;
			}

			retval = 1;
#else
			retval = 0;
#endif

		} else {
			retval = 0;
		}

		if (retval) {
			simplify_change = 1;
		}
	}
}

static struct stmt *
simplify_inline_in_stmt(struct stmt *fs, struct stmt *s)
{
	struct stmt *next;
	struct constraint *c;

	switch (s->type) {
	case STMT_NONE:
		assert(0);

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

	case STMT_NULL:
	case STMT_LABEL:
	case STMT_GOTO:
	case STMT_VA_START:
	case STMT_VA_END:
		/* No function calls. */
		next = s->next;
		break;

	case STMT_EXPR:
	case STMT_IF:
	case STMT_SWITCH:
	case STMT_RETURN:
		next = s->next;
		if (s->expr0) {
			simplify_inline_in_expr(fs, s, s->expr0);
		}
		break;

	case STMT_ASM:
		next = s->next;
		for (c = s->output->first; c; c = c->next) {
			simplify_inline_in_expr(fs, s, c->expr);
		}
		for (c = s->input->first; c; c = c->next) {
			simplify_inline_in_expr(fs, s, c->expr);
		}
		break;

	default:
		assert(0);
	}

	return next;
}

static int
simplify_inline(struct stmt *fs)
{
	struct stmt *cs;

	assert(fs->type == STMT_BLOCK);

	if (fs->scope->function->mod_inline) {
		/* Don't do inlining in inline functions. */
		return 0;
	}

	simplify_change = 0;

	for (cs = fs->stmt_first; cs; ) {
		cs = simplify_inline_in_stmt(fs, cs);
	}

	return simplify_change;
}

static void
simplify_pointer_to_int_in_expr_1(
	struct scope *scope,
	struct type *tt,
	struct expr *e
)
{
	struct expr *ce;
	struct expr *e0;
	struct expr *e1;
	struct expr *e2;
	struct type *ft;

	if (e->type != EXPR_BRACES
	 && e->type != EXPR_STRING) {
		ft = expr_typeof(scope, e);
	} else {
		ft = NULL;
	}

	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:
		assert(0);

	case EXPR_BRACES:
		if (tt->type == TYPE_ARRAY) {
			for (ce = e->first; ce; ce = ce->next) {
				simplify_pointer_to_int_in_expr_1(scope,
						type_array_access(tt), ce);
			}
		} else if (tt->type == TYPE_STRUCT
			|| tt->type == TYPE_UNION) {
			struct declaration *dion;

			if (! tt->scope) {
				int ret;

				ret = scope_lookup_structunionenum(scope,
						tt->type, tt->identifier, &tt);
				assert(0 <= ret);
			}
			assert(tt);
			assert(tt->scope);

			for (dion = tt->scope->declaration_first, ce = e->first;
			    ;
			    dion = dion->next, ce = ce->next) {
				if (! dion || ! ce) {
					break;
				}
				simplify_pointer_to_int_in_expr_1(scope,
						dion->type_name, ce);
			}
			break;

		} else {
			assert(0);
		}
		break;

	case EXPR_INTEGER:
	case EXPR_REAL:
		break;

	case EXPR_STRING:
		if (tt->type == TYPE_POINTER) {
			/*
			 * Replace
			 * 	"..."
			 * by
			 * 	(int) "..."
			 */
			e0 = expr_dup(e);
			e1 = expr_cast(type_ptrdiff(), e0);

			expr_cp(e, e1);
			simplify_change = 1;

		} else if (tt->type == TYPE_ARRAY) {
			/* Nothing to do... */

		} else {
			assert(0);
		}
		break;

	case EXPR_IDENTIFIER:
		if (ft->type == TYPE_ARRAY
		 || ft->type == TYPE_FUNCTION) {
			/*
			 * Replace
			 *	array		func
			 * by
			 *	(int) array	(int) func
			 */
			e0 = expr_dup(e);
			e1 = expr_cast(type_ptrdiff(), e0);

			expr_cp(e, e1);
			simplify_change = 1;

		} else if (ft->type == TYPE_POINTER) {
			/* Will replaced by second step. */
		}
		break;

	case EXPR_AMPHERSAND:
		/*
		 * Replace
		 *	&x
		 * by
		 *	(int) &x
		 */
		assert(e->expr0->type == EXPR_IDENTIFIER);

		e0 = expr_dup(e);
		e1 = expr_cast(type_ptrdiff(), e0);

		expr_cp(e, e1);
		simplify_change = 1;
		break;

	case EXPR_BUILTIN_VA_ARG:
		if (ft->type == TYPE_POINTER) {
			/*
			 * Replace
			 *	va_arg(list, type *)
			 * by
			 * 	va_arg(list, ptrdiff)
			 */
			e0 = expr_dup(e);
			e0->type_name = type_ptrdiff();

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

	case EXPR_STAR:
		if (expr_typeof(scope, e->expr0)->type == TYPE_ARRAY) {
			/*
			 * Leave
			 * 	*array
			 * as is.
			 */
			break;

		} else if (ft->type == TYPE_POINTER) {
			/*
			 * Replace
			 * 	*x
			 * by
			 * 	*(int *) x
			 */
			e0 = expr_dup(e->expr0);
			e1 = expr_cast(type_amphersand(type_ptrdiff()), e0);
			e2 = expr_star(e1);

		} else {
			/*
			 * Replace
			 * 	*x
			 * by
			 * 	*(typeof *x) x
			 */
			e0 = expr_dup(e->expr0);
			e1 = expr_cast(type_amphersand(ft), e0);
			e2 = expr_star(e1);
		}

		expr_cp(e, e2);
		simplify_change = 1;
		break;

	case EXPR_TYPE_CONVERSION:
		if (ft->type == TYPE_POINTER) {
			/*
			 * Replace
			 *	(typeof *x) x
			 * by
			 * 	(int) x
			 */
			e0 = expr_dup(e->expr0);
			e1 = expr_cast(type_ptrdiff(), e0);

			expr_cp(e, e1);
			simplify_change = 1;
		}
		break;

	case EXPR_NEG:
	case EXPR_INV:
		simplify_pointer_to_int_in_expr_1(scope,
				expr_typeof(scope, e->expr0), e->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_RIGHT:
	case EXPR_LEFT:
	case EXPR_ADD:
	case EXPR_SUB:
	case EXPR_MUL:
	case EXPR_DIV:
	case EXPR_MOD:
	case EXPR_AND:
	case EXPR_OR:
	case EXPR_XOR:
		simplify_pointer_to_int_in_expr_1(scope,
				expr_typeof(scope, e->expr0), e->expr0);
		simplify_pointer_to_int_in_expr_1(scope,
				expr_typeof(scope, e->expr1), e->expr1);
		break;

	case EXPR_FUNC:
		/*
		 * Replace function call.
		 */
		if (e->expr0->type == EXPR_STAR) {
			/* FIXME */
		}

		/*
		 * Replace function parameters.
		 */
		for (ce = e->expr1->first; ce; ce = ce->next) {
			struct type *pt;

			pt = expr_typeof(scope, ce);
			pt = type_pure(pt);
			simplify_pointer_to_int_in_expr_1(scope, pt, ce);
		}
		break;

	default:
		assert(0);
	}
}

static void
simplify_pointer_to_int_in_init_1(
	struct scope *scope,
	struct type *t,
	struct expr *e
)
{
	simplify_pointer_to_int_in_expr_1(scope, t, e);
}

static void
simplify_pointer_to_int_in_func_1(struct stmt *fs)
{
	struct declaration *dion;
	struct stmt *cs;
	struct constraint *c;
	struct type *t;

	for (dion = fs->scope->declaration_first; dion; dion = dion->next) {
		if (dion->initializer) {
			simplify_pointer_to_int_in_init_1(fs->scope,
					dion->type_name,
					dion->initializer);
		}
	}

	for (cs = fs->stmt_first; cs; cs = cs->next) {
		switch (cs->type) {
		case STMT_LABEL:
			break;
		case STMT_EXPR:
			switch (cs->expr0->type) {
			case EXPR_ASSIGN:
				t = expr_typeof(fs->scope, cs->expr0->expr0);
				simplify_pointer_to_int_in_expr_1(fs->scope, t,
						cs->expr0->expr0);
				simplify_pointer_to_int_in_expr_1(fs->scope, t,
						cs->expr0->expr1);
				break;
			case EXPR_FUNC:
				t = expr_typeof(fs->scope, cs->expr0);
				simplify_pointer_to_int_in_expr_1(fs->scope, t,
						cs->expr0);
				break;
			default:
				assert(0);
			}
			break;
		case STMT_IF:
			switch (cs->expr0->type) {
			case EXPR_EQUAL:
			case EXPR_NOT_EQUAL:
			case EXPR_LESS:
			case EXPR_LESS_EQUAL:
			case EXPR_GREATER:
			case EXPR_GREATER_EQUAL:
				t = expr_typeof(fs->scope, cs->expr0);
				simplify_pointer_to_int_in_expr_1(fs->scope, t,
						cs->expr0);
				break;
			default:
				assert(0);
			}
			break;
		case STMT_SWITCH:
			switch (cs->expr0->type) {
			case EXPR_IDENTIFIER:
				t = expr_typeof(fs->scope, cs->expr0);
				simplify_pointer_to_int_in_expr_1(fs->scope, t,
						cs->expr0);
				break;
			default:
				assert(0);
			}
			break;
		case STMT_GOTO:
			break;
		case STMT_RETURN:
			if (cs->expr0) {
				switch (cs->expr0->type) {
				case EXPR_INTEGER:
					break;
				case EXPR_IDENTIFIER:
					t = expr_typeof(fs->scope, cs->expr0);
					simplify_pointer_to_int_in_expr_1(fs->scope, t,
							cs->expr0);
					break;
				default:
					assert(0);
				}
			}
			break;
		case STMT_ASM:
			if (cs->output) {
				for (c = cs->output->first; c; c = c->next) {
					simplify_pointer_to_int_in_expr_1(fs->scope,
							expr_typeof(fs->scope,
								c->expr),
							c->expr);
				}
			}
			if (cs->input) {
				for (c = cs->input->first; c; c = c->next) {
					simplify_pointer_to_int_in_expr_1(fs->scope,
							expr_typeof(fs->scope,
								c->expr),
							c->expr);
				}
			}
			break;
		case STMT_VA_START:
			break;
		case STMT_VA_END:
			break;
		default:
			assert(0);
		}
	}
}

static void
simplify_pointer_to_int_in_type_2(struct type **tp)
{
	struct type *t0;
	struct type *t1;

	t0 = *tp;
	t1 = type_pointer_to_ptrdiff(t0);
	if (t1 != t0) {
		*tp = t1;
		simplify_change = 1;
	}
}

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

	for (dion = fs->scope->function->type_name->parameter->declaration_first;
	    dion;
	    dion = dion->next) {
		simplify_pointer_to_int_in_type_2(&dion->type_name);
	}
	for (dion = fs->scope->declaration_first;
	    dion;
	    dion = dion->next) {
		simplify_pointer_to_int_in_type_2(&dion->type_name);
	}
}

static int
simplify_pointer_to_int(struct scope *scope)
{
	struct declaration *dion;

	/*
	 * First step:
	 * Replace all expressions
	 */
	for (dion = scope->declaration_first; dion; dion = dion->next) {
		if (dion->initializer) {
			simplify_pointer_to_int_in_init_1(scope,
					dion->type_name,
					dion->initializer);
		}
		if (dion->stmt) {
			simplify_pointer_to_int_in_func_1(dion->stmt);
		}
	}

	/*
	 * Second step:
	 * Replace types of variables.
	 */
	for (dion = scope->declaration_first; dion; dion = dion->next) {
		if (dion->type_name) {
			simplify_pointer_to_int_in_type_2(&dion->type_name);
		}
		if (dion->stmt) {
			simplify_pointer_to_int_in_func_2(dion->stmt);
		}
	}

	return 0;
}

static int
simplify_rename_labels_in_stmt(
	struct stmt *top,
	struct stmt *s
)
{
	struct stmt *cs;
	int retval;

	retval = 0;

	if (s->stmt0) {
		retval |= simplify_rename_labels_in_stmt(top, s->stmt0);
	}
	if (s->stmt1) {
		retval |= simplify_rename_labels_in_stmt(top, s->stmt1);
	}
	for (cs = s->stmt_first; cs; cs = cs->next) {
		retval |= simplify_rename_labels_in_stmt(top, cs);
	}

	if (s->type == STMT_LABEL
	 && strncmp(s->label->identifier, "__", 2) != 0) {
		s->label->identifier = identifier_tmp();

		retval |= 1;
	}

	return retval;
}

static int
simplify_rename_labels(struct scope *s)
{
	struct declaration *dion;
	int retval;

	retval = 0;

	for (dion = s->declaration_first; dion; dion = dion->next) {
		if (dion->stmt) {
			retval |= simplify_rename_labels_in_stmt(
				dion->stmt, dion->stmt);
		}
	}

	return retval;
}

static int
simplify_inline_remove(struct scope *s)
{
	struct declaration *dion;

	for (dion = s->declaration_first; dion; ) {
		struct declaration *next;

		next = dion->next;

		if (dion->mod_inline) {
			/* Remove inline function from declaration list. */
			if (dion->prev) {
				dion->prev->next = dion->next;
			} else {
				s->declaration_first = dion->next;
			}
			if (dion->next) {
				dion->next->prev = dion->prev;
			} else {
				s->declaration_last = dion->prev;
			}

			/* Free inline function. */
			/* FIXME */
		}

		dion = next;
	}

	return 0;
}

static void
simplify_debug(struct scope *scope, const char *f)
{
	if (opt_d) {
		static int count = 0;
		char path[1024];
		FILE *fp;
		int ret;

		fprintf(stderr, "%s\n", f);

		sprintf(path, "step%04d", count++);
		fp = fopen(path, "w");
		assert(fp);

		ret = setvbuf(fp, NULL, _IONBF, 0);
		assert(ret == 0);

		fprintf(fp, "%s\n", f);
		fprintf(fp, "******************************\n");
		fprintf(fp, "\n");

		print(fp, scope);

		ret = fclose(fp);
		assert(0 <= ret);
	}
}

static int
simplify_once(const char *n, int (*f)(struct scope *), struct scope *s)
{
	int retval;
	int val;

	retval = 0;

	simplify_change = 0;
	val = (*f)(s);
	val |= simplify_change;

	if (val) {
		retval = 1;
		simplify_debug(s, n);
	}

	return retval;
}

static int
simplify_repeat(const char *n, int (*f)(struct scope *), struct scope *s)
{
	int retval;
	int val;

	retval = 0;
restart:;
	simplify_change = 0;
	val = (*f)(s);
	val |= simplify_change;

	if (val) {
		retval = 1;
		simplify_debug(s, n);
		goto restart;
	}

	return retval;
}

static int
simplify_once_dion(
	const char *n,
	int (*f)(struct scope *, struct declaration *),
	struct scope *s
)
{
	struct declaration *dion;
	int retval;
	int val;

	retval = 0;

	for (dion = s->declaration_first; dion; dion = dion->next) {
		simplify_change = 0;
		val = (*f)(s, dion);
		val |= simplify_change;

		if (val) {
			retval = 1;
			simplify_debug(s, n);
		}
	}

	return retval;
}

static int
simplify_repeat_dion(
	const char *n,
	int (*f)(struct scope *, struct declaration *),
	struct scope *s
)
{
	struct declaration *dion;
	int retval;
	int val;

	retval = 0;

	for (dion = s->declaration_first; dion; dion = dion->next) {
	restart:;
		simplify_change = 0;
		val = (*f)(s, dion);
		val |= simplify_change;

		if (val) {
			retval = 1;
			simplify_debug(s, n);
			goto restart;
		}
	}

	return retval;
}

static int
simplify_once_func(const char *n, int (*f)(struct stmt *), struct scope *s)
{
	struct declaration *dion;
	int retval;
	int val;

	retval = 0;

	for (dion = s->declaration_first; dion; dion = dion->next) {
		if (dion->stmt) {
			simplify_change = 0;
			val = (*f)(dion->stmt);
			val |= simplify_change;

			if (val) {
				retval = 1;
				simplify_debug(s, n);
			}
		}
	}

	return retval;
}

static int
simplify_repeat_func(const char *n, int (*f)(struct stmt *), struct scope *s)
{
	struct declaration *dion;
	int retval;
	int val;

	retval = 0;

	for (dion = s->declaration_first; dion; dion = dion->next) {
		if (dion->stmt) {
		restart:;
			simplify_change = 0;
			val = (*f)(dion->stmt);
			val |= simplify_change;

			if (val) {
				retval = 1;
				simplify_debug(s, n);
				goto restart;
			}
		}
	}

	return retval;
}

void
simplify(struct scope *scope)
{
	int retval;

	simplify_debug(scope, "parse");

#define ONCE(x, s)		simplify_once(#x, x, s)
#define REPEAT(x, s)		simplify_repeat(#x, x, s)
#define ONCE_DION(x, s)		simplify_once_dion(#x, x, s)
#define REPEAT_DION(x, s)	simplify_repeat_dion(#x, x, s)
#define ONCE_FUNC(x, s)		simplify_once_func(#x, x, s)
#define REPEAT_FUNC(x, s)	simplify_repeat_func(#x, x, s)

	/*
	 * First step:
	 * Clean up parsed code.
	 */
	ONCE(simplify_fix_dimensions, scope);
	ONCE_FUNC(simplify_split_vardecl_init, scope);
	ONCE(simplify_merge_declarations_of_blocks, scope);

	/*
	 * Second step:
	 * Translate control structures.
	 * Split some expressions.
	 */
	ONCE_FUNC(stmt_simplify, scope);
	ONCE_FUNC(stmt_simplify_params, scope);
	ONCE_FUNC(stmt_simplify_returns, scope);
	REPEAT_DION(expr_simplify, scope);

	/*
	 * Third step:
	 * Do inlining.
	 */
	REPEAT_FUNC(simplify_inline, scope);
	REPEAT_DION(expr_simplify, scope);

	/*
	 * Fourth step:
	 * Do optimization.
	 */
	do {
		retval = REPEAT_DION(expr_optimize, scope);
		retval |= ONCE_FUNC(stmt_optimize, scope);
	} while (retval);

	/*
	 * Fifth step:
	 * Replace all pointers by integer vars.
	 */
	ONCE(simplify_pointer_to_int, scope);

	/*
	 * Sixth step:
	 * Do some more optimizations.
	 */
	do {
		retval = REPEAT_DION(expr_optimize, scope);
		retval |= ONCE_FUNC(stmt_optimize, scope);
	} while (retval);

	/*
	 * Seventh step:
	 * Prepare for compiling.
	 */
	ONCE(simplify_inline_remove, scope);
	ONCE(simplify_rename_labels, scope);
	ONCE_DION(cleanup_func_scope, scope);
}
