Logo Search packages:      
Sourcecode: faucc version File versions  Download package

expr.c

/* $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 *ce1;
      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;
}

Generated by  Doxygen 1.6.0   Back to index