/*
 * $Id: arch_i386_gen.c,v 1.320 2012/02/24 07:14:31 vrsieh Exp $
 *
 * Copyright (C) 2008-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.
 */

/*
 * For gcc inline assembler see e.g.
 * http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
 */

#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

#include "identifier.h"
#include "declaration.h"
#include "scope.h"
#include "stmt.h"
#include "simplify.h"
#include "arch_i386_gen.h"
#include "regalloc.h"
#include "cc1.h"

static struct declaration *arch_mul_int64;
static struct declaration *arch_mul_uint64;
static struct declaration *arch_div_int64;
static struct declaration *arch_div_uint64;
static struct declaration *arch_mod_int64;
static struct declaration *arch_mod_uint64;

enum reg {
/*0*/	AL, DL, CL, BL,
/*4*/	AH, DH, CH, BH,
/*8*/	AX, DX, CX, BX, DI, SI,
/*14*/	EAX, EDX, ECX, EBX, EDI, ESI,
/*20*/	EAX_EBX, EAX_ECX, EAX_EDX, EAX_EDI, EAX_ESI,
/*25*/	EBX_EAX, EBX_ECX, EBX_EDX, EBX_EDI, EBX_ESI,
/*30*/	ECX_EAX, ECX_EBX, ECX_EDX, ECX_EDI, ECX_ESI,
/*35*/	EDX_EAX, EDX_EBX, EDX_ECX, EDX_EDI, EDX_ESI,
/*40*/	EDI_EAX, EDI_EBX, EDI_ECX, EDI_EDX, EDI_ESI,
/*45*/	ESI_EAX, ESI_EBX, ESI_ECX, ESI_EDX, ESI_EDI,
/*50*/	ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7,

/*58*/	REG_COUNT
};

enum class {
	CLASS_NONE,

	CLASS_A,
	CLASS_D,
	CLASS_S,
	CLASS_a,
	CLASS_b,
	CLASS_c,
	CLASS_d,
	CLASS_f,
	CLASS_q,
	CLASS_r,
	CLASS_t,
	CLASS_u,
	CLASS_st2,
	CLASS_st3,
	CLASS_st4,
	CLASS_st5,
	CLASS_st6,
	CLASS_st7,

	CLASS_COUNT
};

enum type_mod {
	REG_MOD_NONE,

	REG_MOD_b,
	REG_MOD_h,
	REG_MOD_w,
	REG_MOD_u,
};

#define REG_8	\
		((1ULL << AL) \
		| (1ULL << BL) \
		| (1ULL << CL) \
		| (1ULL << DL))
#define REG_16	\
		((1ULL << AX) | (1ULL << BX) \
		| (1ULL << CX) | (1ULL << DX) \
		| (1ULL << DI) | (1ULL << SI))
#define REG_32	\
		((1ULL << EAX) | (1ULL << EBX) \
		| (1ULL << ECX) | (1ULL << EDX) \
		| (1ULL << EDI) | (1ULL << ESI))
#define REG_64	\
		((1ULL << EAX_EBX) | (1ULL << EAX_ECX) \
		| (1ULL << EAX_EDX) | (1ULL << EAX_EDI) \
		| (1ULL << EAX_ESI) \
		| (1ULL << EBX_EAX) | (1ULL << EBX_ECX) \
		| (1ULL << EBX_EDX) | (1ULL << EBX_EDI) \
		| (1ULL << EBX_ESI) \
		| (1ULL << ECX_EAX) | (1ULL << ECX_EBX) \
		| (1ULL << ECX_EDX) | (1ULL << ECX_EDI) \
		| (1ULL << ECX_ESI) \
		| (1ULL << EDX_EAX) | (1ULL << EDX_EBX) \
		| (1ULL << EDX_ECX) | (1ULL << EDX_EDI) \
		| (1ULL << EDX_ESI) \
		| (1ULL << EDI_EAX) | (1ULL << EDI_EBX) \
		| (1ULL << EDI_ECX) | (1ULL << EDI_EDX) \
		| (1ULL << EDI_ESI) \
		| (1ULL << ESI_EAX) | (1ULL << ESI_EBX) \
		| (1ULL << ESI_ECX) | (1ULL << ESI_EDX) \
		| (1ULL << ESI_EDI))

#define REG_A	\
		((1ULL << EAX_EDX))
#define REG_D	\
		((1ULL << DI) | (1ULL << EDI) \
		| (1ULL << EDI_EAX) | (1ULL << EDI_EBX) \
		| (1ULL << EDI_ECX) | (1ULL << EDI_EDX) \
		| (1ULL << EDI_ESI))
#define REG_S	\
		((1ULL << SI) | (1ULL << ESI) \
		| (1ULL << ESI_EAX) | (1ULL << ESI_EBX) \
		| (1ULL << ESI_ECX) | (1ULL << ESI_EDX) \
		| (1ULL << ESI_EDI))
#define REG_a	\
		((1ULL << AL) | (1ULL << AX) | (1ULL << EAX) \
		| (1ULL << EAX_EBX) | (1ULL << EAX_ECX) \
		| (1ULL << EAX_EDX) | (1ULL << EAX_EDI) \
		| (1ULL << EAX_ESI))
#define REG_b	\
		((1ULL << BL) | (1ULL << BX) | (1ULL << EBX) \
		| (1ULL << EBX_EAX) | (1ULL << EBX_ECX) \
		| (1ULL << EBX_EDX) | (1ULL << EBX_EDI) \
		| (1ULL << EBX_ESI))
#define REG_c	\
		((1ULL << CL) | (1ULL << CX) | (1ULL << ECX) \
		| (1ULL << ECX_EAX) | (1ULL << ECX_EBX) \
		| (1ULL << ECX_EDX) | (1ULL << ECX_EDI) \
		| (1ULL << ECX_ESI))
#define REG_d	\
		((1ULL << DL) | (1ULL << DX) | (1ULL << EDX) \
		| (1ULL << EDX_EAX) | (1ULL << EDX_EBX) \
		| (1ULL << EDX_ECX) | (1ULL << EDX_EDI) \
		| (1ULL << EDX_ESI))
#define REG_t	\
		(1ULL << ST0)
#define REG_u	\
		(1ULL << ST1)
#define REG_st2	\
		(1ULL << ST2)
#define REG_st3	\
		(1ULL << ST3)
#define REG_st4	\
		(1ULL << ST4)
#define REG_st5	\
		(1ULL << ST5)
#define REG_st6	\
		(1ULL << ST6)
#define REG_st7	\
		(1ULL << ST7)

#define REG_f	\
		(REG_t | REG_u | REG_st2 | REG_st3 \
		| REG_st4 | REG_st5 | REG_st6 | REG_st7)
#define REG_q	\
		(REG_a | REG_b | REG_c | REG_d)
#define REG_r	\
		(REG_a | REG_b | REG_c | REG_d | REG_D | REG_S)

#define REG_CALLER	\
		((1ULL << EAX) \
		| (1ULL << ECX) \
		| (1ULL << EDX) \
		| (1ULL << ST0) \
		| (1ULL << ST1) \
		| (1ULL << ST2) \
		| (1ULL << ST3) \
		| (1ULL << ST4) \
		| (1ULL << ST5) \
		| (1ULL << ST6) \
		| (1ULL << ST7))
#define REG_CALLEE	\
		((REG_32 | REG_f) & ~REG_CALLER)

static struct storage_register arch_reginfo[] = {
	/* 8-Bit Registers */
	[AL] = { "al", CLASS_a, TYPE_UINT8 },
	[BL] = { "bl", CLASS_b, TYPE_UINT8 },
	[CL] = { "cl", CLASS_c, TYPE_UINT8 },
	[DL] = { "dl", CLASS_d, TYPE_UINT8 },

	[AH] = { "ah", CLASS_COUNT, TYPE_UINT8 },
	[BH] = { "bh", CLASS_COUNT, TYPE_UINT8 },
	[CH] = { "ch", CLASS_COUNT, TYPE_UINT8 },
	[DH] = { "dh", CLASS_COUNT, TYPE_UINT8 },

	/* 16-Bit Registers */
	[AX] = { "ax", CLASS_a, TYPE_UINT16 },
	[BX] = { "bx", CLASS_b, TYPE_UINT16 },
	[CX] = { "cx", CLASS_c, TYPE_UINT16 },
	[DX] = { "dx", CLASS_d, TYPE_UINT16 },
	[DI] = { "di", CLASS_D, TYPE_UINT16 },
	[SI] = { "si", CLASS_S, TYPE_UINT16 },
	/* "bp" Used as frame pointer. */
	/* "sp" Used as stack pointer. */

	/* 32-Bit Registers */
	[EAX] = { "eax", CLASS_a, TYPE_UINT32 },
	[EBX] = { "ebx", CLASS_b, TYPE_UINT32 },
	[ECX] = { "ecx", CLASS_c, TYPE_UINT32 },
	[EDX] = { "edx", CLASS_d, TYPE_UINT32 },
	[EDI] = { "edi", CLASS_D, TYPE_UINT32 },
	[ESI] = { "esi", CLASS_S, TYPE_UINT32 },
	/* "ebp" Used as frame pointer. */
	/* "esp" Used as stack pointer. */

	/* 64-Bit Registers */
	[EAX_EBX] = { "eax_ebx", CLASS_NONE, TYPE_UINT64 },
	[EAX_ECX] = { "eax_ecx", CLASS_NONE, TYPE_UINT64 },
	[EAX_EDX] = { "eax_edx", CLASS_NONE, TYPE_UINT64 },
	[EAX_EDI] = { "eax_edi", CLASS_NONE, TYPE_UINT64 },
	[EAX_ESI] = { "eax_esi", CLASS_NONE, TYPE_UINT64 },

	[EBX_EAX] = { "ebx_eax", CLASS_NONE, TYPE_UINT64 },
	[EBX_ECX] = { "ebx_ecx", CLASS_NONE, TYPE_UINT64 },
	[EBX_EDX] = { "ebx_edx", CLASS_NONE, TYPE_UINT64 },
	[EBX_EDI] = { "ebx_edi", CLASS_NONE, TYPE_UINT64 },
	[EBX_ESI] = { "ebx_esi", CLASS_NONE, TYPE_UINT64 },

	[ECX_EAX] = { "ecx_eax", CLASS_NONE, TYPE_UINT64 },
	[ECX_EBX] = { "ecx_ebx", CLASS_NONE, TYPE_UINT64 },
	[ECX_EDX] = { "ecx_edx", CLASS_NONE, TYPE_UINT64 },
	[ECX_EDI] = { "ecx_edi", CLASS_NONE, TYPE_UINT64 },
	[ECX_ESI] = { "ecx_esi", CLASS_NONE, TYPE_UINT64 },

	[EDX_EAX] = { "edx_eax", CLASS_NONE, TYPE_UINT64 },
	[EDX_EBX] = { "edx_ebx", CLASS_NONE, TYPE_UINT64 },
	[EDX_ECX] = { "edx_ecx", CLASS_NONE, TYPE_UINT64 },
	[EDX_EDI] = { "edx_edi", CLASS_NONE, TYPE_UINT64 },
	[EDX_ESI] = { "edx_esi", CLASS_NONE, TYPE_UINT64 },

	[EDI_EAX] = { "edi_eax", CLASS_NONE, TYPE_UINT64 },
	[EDI_EBX] = { "edi_ebx", CLASS_NONE, TYPE_UINT64 },
	[EDI_ECX] = { "edi_ecx", CLASS_NONE, TYPE_UINT64 },
	[EDI_EDX] = { "edi_edx", CLASS_NONE, TYPE_UINT64 },
	[EDI_ESI] = { "edi_esi", CLASS_NONE, TYPE_UINT64 },

	[ESI_EAX] = { "esi_eax", CLASS_NONE, TYPE_UINT64 },
	[ESI_EBX] = { "esi_ebx", CLASS_NONE, TYPE_UINT64 },
	[ESI_ECX] = { "esi_ecx", CLASS_NONE, TYPE_UINT64 },
	[ESI_EDX] = { "esi_edx", CLASS_NONE, TYPE_UINT64 },
	[ESI_EDI] = { "esi_edi", CLASS_NONE, TYPE_UINT64 },

	/* Floating Point Registers */
	[ST0] = { "st(0)", CLASS_t, TYPE_FLOAT64 },
	[ST1] = { "st(1)", CLASS_u, TYPE_FLOAT64 },
	[ST2] = { "st(2)", CLASS_st2, TYPE_FLOAT64 },
	[ST3] = { "st(3)", CLASS_st3, TYPE_FLOAT64 },
	[ST4] = { "st(4)", CLASS_st4, TYPE_FLOAT64 },
	[ST5] = { "st(5)", CLASS_st5, TYPE_FLOAT64 },
	[ST6] = { "st(6)", CLASS_st6, TYPE_FLOAT64 },
	[ST7] = { "st(7)", CLASS_st7, TYPE_FLOAT64 },
};

static struct {
	unsigned int count;
	enum reg reg[4];
} arch_gen_reg_parts[] = {
	/* 8-Bit Registers */
	[AL] = { 0 },
	[BL] = { 0 },
	[CL] = { 0 },
	[DL] = { 0 },

	[AH] = { 0 },
	[BH] = { 0 },
	[CH] = { 0 },
	[DH] = { 0 },

	/* 16-Bit Registers */
	[AX] = { 2, { AL, AH } },
	[BX] = { 2, { BL, BH } },
	[CX] = { 2, { CL, CH } },
	[DX] = { 2, { DL, DH } },
	[DI] = { 0 },
	[SI] = { 0 },
	/* "bp" Used as frame pointer. */
	/* "sp" Used as stack pointer. */

	/* 32-Bit Registers */
	[EAX] = { 1, { AX } },
	[EBX] = { 1, { BX } },
	[ECX] = { 1, { CX } },
	[EDX] = { 1, { DX } },
	[EDI] = { 1, { DI } },
	[ESI] = { 1, { SI } },
	/* "ebp" Used as frame pointer. */
	/* "esp" Used as stack pointer. */

	/* 64-Bit Registers */
	[EAX_EBX] = { 2, { EAX, EBX } },
	[EAX_ECX] = { 2, { EAX, ECX } },
	[EAX_EDX] = { 2, { EAX, EDX } },
	[EAX_EDI] = { 2, { EAX, EDI } },
	[EAX_ESI] = { 2, { EAX, ESI } },

	[EBX_EAX] = { 2, { EBX, EAX } },
	[EBX_ECX] = { 2, { EBX, ECX } },
	[EBX_EDX] = { 2, { EBX, EDX } },
	[EBX_EDI] = { 2, { EBX, EDI } },
	[EBX_ESI] = { 2, { EBX, ESI } },

	[ECX_EAX] = { 2, { ECX, EAX } },
	[ECX_EBX] = { 2, { ECX, EBX } },
	[ECX_EDX] = { 2, { ECX, EDX } },
	[ECX_EDI] = { 2, { ECX, EDI } },
	[ECX_ESI] = { 2, { ECX, ESI } },

	[EDX_EAX] = { 2, { EDX, EAX } },
	[EDX_EBX] = { 2, { EDX, EBX } },
	[EDX_ECX] = { 2, { EDX, ECX } },
	[EDX_EDI] = { 2, { EDX, EDI } },
	[EDX_ESI] = { 2, { EDX, ESI } },

	[EDI_EAX] = { 2, { EDI, EAX } },
	[EDI_EBX] = { 2, { EDI, EBX } },
	[EDI_ECX] = { 2, { EDI, ECX } },
	[EDI_EDX] = { 2, { EDI, EDX } },
	[EDI_ESI] = { 2, { EDI, ESI } },

	[ESI_EAX] = { 2, { ESI, EAX } },
	[ESI_EBX] = { 2, { ESI, EBX } },
	[ESI_ECX] = { 2, { ESI, ECX } },
	[ESI_EDX] = { 2, { ESI, EDX } },
	[ESI_EDI] = { 2, { ESI, EDI } },

	[ST0] = { 0 },
	[ST1] = { 0 },
	[ST2] = { 0 },
	[ST3] = { 0 },
	[ST4] = { 0 },
	[ST5] = { 0 },
	[ST6] = { 0 },
	[ST7] = { 0 },
};

static const char *arch_reg_name_b[] = {
	[AL] = NULL,
	[BL] = NULL,
	[CL] = NULL,
	[DL] = NULL,

	[AH] = NULL,
	[BH] = NULL,
	[CH] = NULL,
	[DH] = NULL,

	[AX] = "al",
	[BX] = "bl",
	[CX] = "cl",
	[DX] = "dl",
	[DI] = NULL,
	[SI] = NULL,

	[EAX] = "al",
	[EBX] = "bl",
	[ECX] = "cl",
	[EDX] = "dl",
	[EDI] = NULL,
	[ESI] = NULL,

	[EAX_EBX] = "al",
	[EAX_ECX] = "al",
	[EAX_EDX] = "al",
	[EAX_EDI] = "al",
	[EAX_ESI] = "al",

	[EBX_EAX] = "bl",
	[EBX_ECX] = "bl",
	[EBX_EDX] = "bl",
	[EBX_EDI] = "bl",
	[EBX_ESI] = "bl",

	[ECX_EAX] = "cl",
	[ECX_EBX] = "cl",
	[ECX_EDX] = "cl",
	[ECX_EDI] = "cl",
	[ECX_ESI] = "cl",

	[EDX_EAX] = "dl",
	[EDX_EBX] = "dl",
	[EDX_ECX] = "dl",
	[EDX_EDI] = "dl",
	[EDX_ESI] = "dl",

	[EDI_EAX] = NULL,
	[EDI_EBX] = NULL,
	[EDI_ECX] = NULL,
	[EDI_EDX] = NULL,
	[EDI_ESI] = NULL,

	[ESI_EAX] = NULL,
	[ESI_EBX] = NULL,
	[ESI_ECX] = NULL,
	[ESI_EDX] = NULL,
	[ESI_EDI] = NULL,

	[ST0] = NULL,
	[ST1] = NULL,
	[ST2] = NULL,
	[ST3] = NULL,
	[ST4] = NULL,
	[ST5] = NULL,
	[ST6] = NULL,
	[ST7] = NULL,
};

static const char *arch_reg_name_h[] = {
	[AL] = NULL,
	[BL] = NULL,
	[CL] = NULL,
	[DL] = NULL,

	[AH] = NULL,
	[BH] = NULL,
	[CH] = NULL,
	[DH] = NULL,

	[AX] = "ah",
	[BX] = "bh",
	[CX] = "ch",
	[DX] = "dh",
	[DI] = NULL,
	[SI] = NULL,

	[EAX] = "ah",
	[EBX] = "bh",
	[ECX] = "ch",
	[EDX] = "dh",
	[EDI] = NULL,
	[ESI] = NULL,

	[EAX_EBX] = "ah",
	[EAX_ECX] = "ah",
	[EAX_EDX] = "ah",
	[EAX_EDI] = "ah",
	[EAX_ESI] = "ah",

	[EBX_EAX] = "bh",
	[EBX_ECX] = "bh",
	[EBX_EDX] = "bh",
	[EBX_EDI] = "bh",
	[EBX_ESI] = "bh",

	[ECX_EAX] = "ch",
	[ECX_EBX] = "ch",
	[ECX_EDX] = "ch",
	[ECX_EDI] = "ch",
	[ECX_ESI] = "ch",

	[EDX_EAX] = "dh",
	[EDX_EBX] = "dh",
	[EDX_ECX] = "dh",
	[EDX_EDI] = "dh",
	[EDX_ESI] = "dh",

	[EDI_EAX] = NULL,
	[EDI_EBX] = NULL,
	[EDI_ECX] = NULL,
	[EDI_EDX] = NULL,
	[EDI_ESI] = NULL,

	[ESI_EAX] = NULL,
	[ESI_EBX] = NULL,
	[ESI_ECX] = NULL,
	[ESI_EDX] = NULL,
	[ESI_EDI] = NULL,

	[ST0] = NULL,
	[ST1] = NULL,
	[ST2] = NULL,
	[ST3] = NULL,
	[ST4] = NULL,
	[ST5] = NULL,
	[ST6] = NULL,
	[ST7] = NULL,
};

static const char *arch_reg_name_w[] = {
	[AL] = NULL,
	[BL] = NULL,
	[CL] = NULL,
	[DL] = NULL,

	[AH] = NULL,
	[BH] = NULL,
	[CH] = NULL,
	[DH] = NULL,

	[AX] = NULL,
	[BX] = NULL,
	[CX] = NULL,
	[DX] = NULL,
	[DI] = NULL,
	[SI] = NULL,

	[EAX] = "ax",
	[EBX] = "bx",
	[ECX] = "cx",
	[EDX] = "dx",
	[EDI] = "di",
	[ESI] = "si",

	[EAX_EBX] = "ax",
	[EAX_ECX] = "ax",
	[EAX_EDX] = "ax",
	[EAX_EDI] = "ax",
	[EAX_ESI] = "ax",

	[EBX_EAX] = "bx",
	[EBX_ECX] = "bx",
	[EBX_EDX] = "bx",
	[EBX_EDI] = "bx",
	[EBX_ESI] = "bx",

	[ECX_EAX] = "cx",
	[ECX_EBX] = "cx",
	[ECX_EDX] = "cx",
	[ECX_EDI] = "cx",
	[ECX_ESI] = "cx",

	[EDX_EAX] = "dx",
	[EDX_EBX] = "dx",
	[EDX_ECX] = "dx",
	[EDX_EDI] = "dx",
	[EDX_ESI] = "dx",

	[EDI_EAX] = NULL,
	[EDI_EBX] = NULL,
	[EDI_ECX] = NULL,
	[EDI_EDX] = NULL,
	[EDI_ESI] = NULL,

	[ESI_EAX] = NULL,
	[ESI_EBX] = NULL,
	[ESI_ECX] = NULL,
	[ESI_EDX] = NULL,
	[ESI_EDI] = NULL,

	[ST0] = NULL,
	[ST1] = NULL,
	[ST2] = NULL,
	[ST3] = NULL,
	[ST4] = NULL,
	[ST5] = NULL,
	[ST6] = NULL,
	[ST7] = NULL,
};

static const char *arch_reg_name_u[] = {
	[AL] = NULL,
	[BL] = NULL,
	[CL] = NULL,
	[DL] = NULL,

	[AH] = NULL,
	[BH] = NULL,
	[CH] = NULL,
	[DH] = NULL,

	[AX] = NULL,
	[BX] = NULL,
	[CX] = NULL,
	[DX] = NULL,
	[DI] = NULL,
	[SI] = NULL,

	[EAX] = NULL,
	[EBX] = NULL,
	[ECX] = NULL,
	[EDX] = NULL,
	[EDI] = NULL,
	[ESI] = NULL,

	[EAX_EBX] = "ebx",
	[EAX_ECX] = "ecx",
	[EAX_EDX] = "edx",
	[EAX_EDI] = "edi",
	[EAX_ESI] = "esi",

	[EBX_EAX] = "eax",
	[EBX_ECX] = "ecx",
	[EBX_EDX] = "edx",
	[EBX_EDI] = "edi",
	[EBX_ESI] = "esi",

	[ECX_EAX] = "eax",
	[ECX_EBX] = "ebx",
	[ECX_EDX] = "edx",
	[ECX_EDI] = "edi",
	[ECX_ESI] = "esi",

	[EDX_EAX] = "eax",
	[EDX_EBX] = "ebx",
	[EDX_ECX] = "ecx",
	[EDX_EDI] = "edi",
	[EDX_ESI] = "esi",

	[EDI_EAX] = "eax",
	[EDI_EBX] = "ebx",
	[EDI_ECX] = "ecx",
	[EDI_EDX] = "edx",
	[EDI_ESI] = "esi",

	[ESI_EAX] = "eax",
	[ESI_EBX] = "ebx",
	[ESI_ECX] = "ecx",
	[ESI_EDX] = "edx",
	[ESI_EDI] = "edi",

	[ST0] = NULL,
	[ST1] = NULL,
	[ST2] = NULL,
	[ST3] = NULL,
	[ST4] = NULL,
	[ST5] = NULL,
	[ST6] = NULL,
	[ST7] = NULL,
};

static const char *arch_reg_name_def[] = {
	[AL] = "al",
	[BL] = "bl",
	[CL] = "cl",
	[DL] = "dl",

	[AH] = "ah",
	[BH] = "bh",
	[CH] = "ch",
	[DH] = "dh",

	[AX] = "ax",
	[BX] = "bx",
	[CX] = "cx",
	[DX] = "dx",
	[DI] = "di",
	[SI] = "si",

	[EAX] = "eax",
	[EBX] = "ebx",
	[ECX] = "ecx",
	[EDX] = "edx",
	[EDI] = "edi",
	[ESI] = "esi",

	[EAX_EBX] = "eax",
	[EAX_ECX] = "eax",
	[EAX_EDX] = "eax",
	[EAX_EDI] = "eax",
	[EAX_ESI] = "eax",

	[EBX_EAX] = "ebx",
	[EBX_ECX] = "ebx",
	[EBX_EDX] = "ebx",
	[EBX_EDI] = "ebx",
	[EBX_ESI] = "ebx",

	[ECX_EAX] = "ecx",
	[ECX_EBX] = "ecx",
	[ECX_EDX] = "ecx",
	[ECX_EDI] = "ecx",
	[ECX_ESI] = "ecx",

	[EDX_EAX] = "edx",
	[EDX_EBX] = "edx",
	[EDX_ECX] = "edx",
	[EDX_EDI] = "edx",
	[EDX_ESI] = "edx",

	[EDI_EAX] = "edi",
	[EDI_EBX] = "edi",
	[EDI_ECX] = "edi",
	[EDI_EDX] = "edi",
	[EDI_ESI] = "edi",

	[ESI_EAX] = "esi",
	[ESI_EBX] = "esi",
	[ESI_ECX] = "esi",
	[ESI_EDX] = "esi",
	[ESI_EDI] = "esi",
};

static unsigned int arch_classinfo[] = {
	['A'] = CLASS_A,
	['D'] = CLASS_D,
	['Q'] = CLASS_q,
	['R'] = CLASS_r,
	['S'] = CLASS_S,
	['a'] = CLASS_a,
	['b'] = CLASS_b,
	['c'] = CLASS_c,
	['d'] = CLASS_d,
	['f'] = CLASS_f,  /* FLOAT_REGS */
	/* ['l'] = CLASS_l, */ /* INDEX_REG (all but %esp) - FIXME */
	['q'] = CLASS_q,
	['r'] = CLASS_r,
	['t'] = CLASS_t, /* %st(0) */
	['u'] = CLASS_u, /* %st(1) */
};

static unsigned int arch_typeinfo[TYPE_MAX] = {
	[TYPE_VA_LIST] = CLASS_r,

	[TYPE_INT8] = CLASS_q,
	[TYPE_UINT8] = CLASS_q,
	[TYPE_INT16] = CLASS_r,
	[TYPE_UINT16] = CLASS_r,
	[TYPE_INT32] = CLASS_r,
	[TYPE_UINT32] = CLASS_r,
	[TYPE_INT64] = CLASS_r,
	[TYPE_UINT64] = CLASS_r,

	[TYPE_FLOAT32] = CLASS_f,
	[TYPE_FLOAT64] = CLASS_f,
	[TYPE_FLOAT80] = CLASS_f,

	[TYPE_STRUCT] = CLASS_NONE,
	[TYPE_UNION] = CLASS_NONE,

	[TYPE_POINTER] = CLASS_r,
	[TYPE_ARRAY] = CLASS_NONE,
	[TYPE_FUNCTION] = CLASS_NONE,
};

static int
seg_enabled(void)
{
	return 0; /* FIXME */
}

struct type *
arch_i386_type_intptr_t(void)
{
	if (seg_enabled()) { 
		assert(0); /* FIXME */
	} else {
		return type_int32();
	}    
}

struct type *
arch_i386_type_uintptr_t(void)
{
	if (seg_enabled()) { 
		assert(0); /* FIXME */
	} else {
		return type_uint32();
	}    
}

void
arch_i386_align_size(
	struct scope *scope,
	struct type *t,
	unsigned int *alignp,
	unsigned int *sizep
)
{
	switch (t->type) {
	case TYPE_INT8:
	case TYPE_UINT8:
		*alignp = 1;
		*sizep = 1;
		break;

	case TYPE_INT16:
	case TYPE_UINT16:
		*alignp = 2;
		*sizep = 2;
		break;

	case TYPE_INT32:
	case TYPE_UINT32:
		*alignp = 4;
		*sizep = 4;
		break;

	case TYPE_INT64:
	case TYPE_UINT64:
		*alignp = 4;
		*sizep = 8;
		break;

	case TYPE_FLOAT32:
		*alignp = 4;
		*sizep = 4;
		break;

	case TYPE_FLOAT64:
		*alignp = 4;
		*sizep = 8;
		break;

	case TYPE_FLOAT80:
		*alignp = 4;
		*sizep = 16;
		break;

	case TYPE_VA_LIST:
		*alignp = 4;
		*sizep = 4;
		break;
	case TYPE_POINTER:
		*alignp = 4;
		*sizep = 4;
		break;
	default:
		assert(0);
	}
}

void
arch_i386_gen_class_and_type_get(
	const char *name,
	unsigned int *classp,
	enum type_type *typep
)
{
	unsigned int reg;

	for (reg = 0; ; reg++) {
		assert(reg < REG_COUNT);
		if (strcmp(arch_reginfo[reg].name, name) == 0) {
			*classp = arch_reginfo[reg].class;
			*typep = arch_reginfo[reg].type;
			break;
		}
	}
}

unsigned int
arch_i386_gen_class_or(unsigned int a, unsigned int b)
{
	unsigned int class;

	if (a == CLASS_NONE) {
		class = b;
	} else if (b == CLASS_NONE) {
		class = a;
	} else {
		/* FIXME */
		class = 0; /* Make cppcheck happy. */
		assert(0);
	}

	return class;
}

unsigned int
arch_i386_gen_class_and(unsigned int a, unsigned int b)
{
	unsigned int class;

	if (a == CLASS_f
	 && (b == CLASS_t
	  || b == CLASS_u
	  || b == CLASS_st2
	  || b == CLASS_st3
	  || b == CLASS_st4
	  || b == CLASS_st5
	  || b == CLASS_st6
	  || b == CLASS_st7
	  || b == CLASS_f)) {
		class = b;

	} else if (b == CLASS_f
		&& (a == CLASS_t
		 || a == CLASS_u
		 || a == CLASS_st2
		 || a == CLASS_st3
		 || a == CLASS_st4
		 || a == CLASS_st5
		 || a == CLASS_st6
		 || a == CLASS_st7
		 || a == CLASS_f)) {
		class = a;

	} else if (a == CLASS_r
		&& (b == CLASS_A
		 || b == CLASS_D
		 || b == CLASS_S
		 || b == CLASS_a
		 || b == CLASS_b
		 || b == CLASS_c
		 || b == CLASS_d
		 || b == CLASS_q
		 || b == CLASS_r)) {
		class = b;

	} else if (b == CLASS_r
		&& (a == CLASS_A
		 || a == CLASS_D
		 || a == CLASS_S
		 || a == CLASS_a
		 || a == CLASS_b
		 || a == CLASS_c
		 || a == CLASS_d
		 || a == CLASS_q
		 || a == CLASS_r)) {
		class = a;

	} else if (a == CLASS_q
		&& (b == CLASS_A
		 || b == CLASS_a
		 || b == CLASS_b
		 || b == CLASS_c
		 || b == CLASS_d
		 || b == CLASS_q
		 || b == CLASS_r)) {
		class = b;

	} else if (b == CLASS_q
		&& (a == CLASS_A
		 || a == CLASS_a
		 || a == CLASS_b
		 || a == CLASS_c
		 || a == CLASS_d
		 || a == CLASS_q
		 || a == CLASS_r)) {
		class = a;

	} else if (a == b) {
		class = a;

	} else {
		class = CLASS_NONE;
	}

	return class;
}

void
arch_i386_color_init(unsigned int *count)
{
	unsigned int class;

	for (class = 0; class < DECL_CLASS_COUNT; class++) {
		count[class] = 0;
	}
}

void
arch_i386_color_add(unsigned int *count, unsigned int class, enum type_type type)
{
	switch (class) {
	case CLASS_NONE: /* Memory only. */
		break;
	case CLASS_A:
		assert(type == TYPE_INT64
		    || type == TYPE_UINT64);
		count[CLASS_a]++;
		count[CLASS_d]++;
		break;
	case CLASS_D:
	case CLASS_S:
	case CLASS_a:
	case CLASS_b:
	case CLASS_d:
		assert(type == TYPE_VA_LIST
		    || type == TYPE_INT8
		    || type == TYPE_UINT8
		    || type == TYPE_INT16
		    || type == TYPE_UINT16
		    || type == TYPE_INT32
		    || type == TYPE_UINT32
		    || type == TYPE_POINTER);
		count[class]++;
		break;
	case CLASS_c: /* FIXME */
		count[CLASS_c]++;
		if (type == TYPE_INT64
		 || type == TYPE_UINT64) {
			count[CLASS_r]++;
		}
		break;
	case CLASS_f:
	case CLASS_t:
	case CLASS_u:
	case CLASS_st2:
	case CLASS_st3:
	case CLASS_st4:
	case CLASS_st5:
	case CLASS_st6:
	case CLASS_st7:
		assert(type == TYPE_FLOAT32
		    || type == TYPE_FLOAT64
		    || type == TYPE_FLOAT80);
		count[class]++;
		break;
	case CLASS_q:
	case CLASS_r:
		assert(type == TYPE_VA_LIST
		    || type == TYPE_INT8
		    || type == TYPE_UINT8
		    || type == TYPE_INT16
		    || type == TYPE_UINT16
		    || type == TYPE_INT32
		    || type == TYPE_UINT32
		    || type == TYPE_INT64
		    || type == TYPE_UINT64
		    || type == TYPE_POINTER);
		count[class]++;
		if (type == TYPE_INT64
		 || type == TYPE_UINT64) {
			count[CLASS_r]++;
		}
		break;
	default:
		fprintf(stderr, "class: %u, type: %u\n", class, type);
		assert(0);
	}
}

void
arch_i386_color_sub(unsigned int *count, unsigned int class, enum type_type type)
{
	switch (class) {
	case CLASS_NONE: /* Memory only. */
		break;
	case CLASS_A:
		assert(1 <= count[CLASS_a]);
		assert(1 <= count[CLASS_d]);
		count[CLASS_a]--;
		count[CLASS_d]--;
		break;
	case CLASS_D:
	case CLASS_S:
	case CLASS_a:
	case CLASS_b:
	case CLASS_d:
		assert(1 <= count[class]);
		count[class]--;
		break;
	case CLASS_c: /* FIXME */
		assert(1 <= count[CLASS_c]);
		count[CLASS_c]--;
		if (type == TYPE_INT64
		 || type == TYPE_UINT64) {
			assert(1 <= count[CLASS_r]);
			count[CLASS_r]--;
		}
		break;
	case CLASS_f:
	case CLASS_t:
	case CLASS_u:
	case CLASS_st2:
	case CLASS_st3:
	case CLASS_st4:
	case CLASS_st5:
	case CLASS_st6:
	case CLASS_st7:
		assert(1 <= count[class]);
		count[class]--;
		break;
	case CLASS_q:
	case CLASS_r:
		assert(1 <= count[class]);
		count[class]--;
		if (type == TYPE_INT64
		 || type == TYPE_UINT64) {
			assert(1 <= count[CLASS_r]);
			count[CLASS_r]--;
		}
		break;
	default:
		fprintf(stderr, "class: %u, type: %u\n", class, type);
		assert(0);
	}
}

int
arch_i386_color_check(unsigned int *count, unsigned int class, enum type_type type)
{
	unsigned int num;

	switch (class) {
	case CLASS_NONE: /* Memory only. */
		return 0;
	case CLASS_A:
		num = (1 < count[CLASS_a]) ? 1 : count[CLASS_a];
		num += (1 < count[CLASS_d]) ? 1 : count[CLASS_d];
		num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
		if (4 < num) num = 4;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (6 < num) num = 6;
		return num < 1;
	case CLASS_D:
	case CLASS_S:
		num = (1 < count[class]) ? 1 : count[class];
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (1 < num) num = 1;
		return num < 1;
	case CLASS_a:
	case CLASS_b:
	case CLASS_c:
	case CLASS_d:
		num = (1 < count[class]) ? 1 : count[class];
		num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
		if (4 < num) num = 4;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (6 < num) num = 6;
		return num < 1;
	case CLASS_f:
		num = (1 < count[CLASS_t]) ? 1 : count[CLASS_t];
		num += (1 < count[CLASS_u]) ? 1 : count[CLASS_u];
		num += (1 < count[CLASS_st2]) ? 1 : count[CLASS_st2];
		num += (1 < count[CLASS_st3]) ? 1 : count[CLASS_st3];
		num += (1 < count[CLASS_st4]) ? 1 : count[CLASS_st4];
		num += (1 < count[CLASS_st5]) ? 1 : count[CLASS_st5];
		num += (1 < count[CLASS_st6]) ? 1 : count[CLASS_st6];
		num += (1 < count[CLASS_st7]) ? 1 : count[CLASS_st7];
		num += (8 < count[CLASS_f]) ? 8 : count[CLASS_f];
		if (8 < num) num = 8;
		return num < 8;
		break;
	case CLASS_q:
		num = (1 < count[CLASS_a]) ? 1 : count[CLASS_a];
		num += (1 < count[CLASS_b]) ? 1 : count[CLASS_b];
		num += (1 < count[CLASS_c]) ? 1 : count[CLASS_c];
		num += (1 < count[CLASS_d]) ? 1 : count[CLASS_d];
		num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
		if (4 < num) num = 4;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (6 < num) num = 6;
		return num < 4;
	case CLASS_r:
		num = (1 < count[CLASS_a]) ? 1 : count[CLASS_a];
		num += (1 < count[CLASS_b]) ? 1 : count[CLASS_b];
		num += (1 < count[CLASS_c]) ? 1 : count[CLASS_c];
		num += (1 < count[CLASS_d]) ? 1 : count[CLASS_d];
		num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
		if (4 < num) num = 4;
		num += (1 < count[CLASS_D]) ? 1 : count[CLASS_D];
		num += (1 < count[CLASS_S]) ? 1 : count[CLASS_S];
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (6 < num) num = 6;
		if (type != TYPE_INT64
		 && type != TYPE_UINT64) {
			return num < 6;
		} else {
			return num < 5;
		}
	case CLASS_t:
	case CLASS_u:
	case CLASS_st2:
	case CLASS_st3:
	case CLASS_st4:
	case CLASS_st5:
	case CLASS_st6:
	case CLASS_st7:
		num = (1 < count[class]) ? 1 : count[class];
		num += (8 < count[CLASS_f]) ? 8 : count[CLASS_f];
		if (1 < num) num = 1;
		return num < 1;
	default:
		fprintf(stderr, "class: %u, type: %u\n", class, type);
		assert(0);
	}
}

void
arch_i386_gen_reg_init(uint32_t *conflicts)
{
	memset(conflicts, 0, (REG_COUNT + 7) / 8);
}

static void
arch_gen_reg_add_2(uint32_t *conflicts, unsigned int reg)
{
	unsigned int count;
	unsigned int reg2;

	*(uint64_t *) conflicts |= 1ULL << reg;

	for (reg2 = 0; reg2 < REG_COUNT; reg2++) {
		for (count = 0; count < arch_gen_reg_parts[reg2].count; count++) {
			if (arch_gen_reg_parts[reg2].reg[count] == reg) {
				arch_gen_reg_add_2(conflicts, reg2);
			}
		}
	}
}

static void
arch_gen_reg_add_1(uint32_t *conflicts, unsigned int reg)
{
	if (arch_gen_reg_parts[reg].count == 0) {
		arch_gen_reg_add_2(conflicts, reg);

	} else {
		unsigned int count;
		unsigned int reg2;

		for (count = 0; count < arch_gen_reg_parts[reg].count; count++) {
			reg2 = arch_gen_reg_parts[reg].reg[count];
			arch_gen_reg_add_1(conflicts, reg2);
		}
	}
}

void
arch_i386_gen_reg_add(uint32_t *conflicts, unsigned int reg)
{
	arch_gen_reg_add_1(conflicts, reg);
}

int
arch_i386_gen_reg_get(
	uint32_t *conflicts,
	unsigned int class,
	enum type_type type
)
{
	uint64_t regs;
	unsigned int reg;

	switch (class) {
	case CLASS_NONE: regs = 0; break;
	case CLASS_A: regs = REG_A; break;
	case CLASS_D: regs = REG_D; break;
	case CLASS_S: regs = REG_S; break;
	case CLASS_a: regs = REG_a; break;
	case CLASS_b: regs = REG_b; break;
	case CLASS_c: regs = REG_c; break;
	case CLASS_d: regs = REG_d; break;
	case CLASS_f: regs = REG_f; break;
	case CLASS_q: regs = REG_q; break;
	case CLASS_r: regs = REG_r; break;
	case CLASS_t: regs = REG_t; break;
	case CLASS_u: regs = REG_u; break;
	case CLASS_st2: regs = REG_st2; break;
	case CLASS_st3: regs = REG_st3; break;
	case CLASS_st4: regs = REG_st4; break;
	case CLASS_st5: regs = REG_st5; break;
	case CLASS_st6: regs = REG_st6; break;
	case CLASS_st7: regs = REG_st7; break;
	default: assert(0); break;
	}
	switch (type) {
	case TYPE_NONE: regs &= 0; break;
	case TYPE_INT8:
	case TYPE_UINT8: regs &= REG_8; break;
	case TYPE_INT16:
	case TYPE_UINT16: regs &= REG_16; break;
	case TYPE_INT32:
	case TYPE_UINT32: regs &= REG_32; break;
	case TYPE_INT64:
	case TYPE_UINT64: regs &= REG_64; break;
	case TYPE_FLOAT32:
	case TYPE_FLOAT64:
	case TYPE_FLOAT80: regs &= REG_f; break;
	case TYPE_UNION: regs &= 0; break;
	case TYPE_STRUCT: regs &= 0; break;
	case TYPE_ARRAY: regs &= 0; break;
	case TYPE_FUNCTION: regs &= 0; break;
	case TYPE_VA_LIST: regs &= REG_32; break;
	case TYPE_POINTER: regs &= REG_32; break;
	default: assert(0); break;
	}

	regs &= ~ *(uint64_t *) conflicts;

	for (reg = 0; ; reg++) {
		if (reg == REG_COUNT) {
			return -1;
		}
		if ((regs >> reg) & 1) {
			return reg;
		}
	}
}

static const char *
arch_gen_name(unsigned int local, const char *prefix, const char *name)
{
	static unsigned int rand = 0;
	static char buf[10][1024];
	static unsigned int cur = 0;
	char *retval;

	if (! rand) {
		struct timeval tv;
		int ret;

		ret = gettimeofday(&tv, NULL);
		assert(0 <= ret);

		rand = tv.tv_sec ^ tv.tv_usec;
	}

	retval = buf[cur];
	cur = (cur + 1) % 10;

	if (prefix) {
		strcpy(retval, prefix);
		strcat(retval, "_");
	} else {
		strcpy(retval, "");
	}
	if (local
	 && seg_enabled()) {
		sprintf(retval + strlen(retval), "L%08x_", rand);
	}
	strcat(retval, name);

	return retval;
}

static void
arch_gen_name_def(int local, const char *name)
{
}

static void
arch_gen_local_def(const char *name)
{
	arch_gen_name_def(1, name);
}

static const char *
arch_gen_local(const char *name)
{
	return arch_gen_name(1, NULL, name);
}

static const char *
arch_gen_local_seg(const char *name)
{
	assert(seg_enabled());

	return arch_gen_name(1, "SEG", name);
}

static const char *
arch_gen_local_off(const char *name)
{
	if (seg_enabled()) {
		return arch_gen_name(1, "OFF", name);
	} else {
		return arch_gen_name(1, NULL, name);
	}
}

static void
arch_gen_global_def(const char *name)
{
	arch_gen_name_def(0, name);
}

static const char *
arch_gen_global(const char *name)
{
	return arch_gen_name(0, NULL, name);
}

static const char *
arch_gen_global_seg(const char *name)
{
	assert(seg_enabled());

	return arch_gen_name(0, "SEG", name);
}

static const char *
arch_gen_global_off(const char *name)
{
	if (seg_enabled()) {
		return arch_gen_name(0, "OFF", name);
	} else {
		return arch_gen_name(0, NULL, name);
	}
}

static void
arch_gen_data_init(
	FILE *fp,
	struct scope *scope,
	struct type *t,
	struct expr *e
)
{
	struct declaration *dion;

	switch (t->type) {
	case TYPE_NONE:
	case TYPE_ELIPSIS:
	case TYPE_VOID:
	case TYPE_UNION:
	case TYPE_ENUM:
	case TYPE_FUNCTION:
	case TYPE_MAX:
		assert(0);

	case TYPE_INT8:
	case TYPE_UINT8:
		if (e) {
			assert(e->type == EXPR_INTEGER);
			fprintf(fp, "\t.byte 0x%02x\n", (uint8_t) e->integer);
		} else {
			fprintf(fp, "\t.byte 0x00\n");
		}
		break;
	case TYPE_INT16:
	case TYPE_UINT16:
		if (e) {
			switch (e->type) {
			case EXPR_INTEGER:
				fprintf(fp, "\t.word 0x%04x\n",
					(uint16_t) e->integer);
				break;
			case EXPR_TYPE_CONVERSION:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
		} else {
			fprintf(fp, "\t.word 0x0000\n");
		}
		break;
	case TYPE_INT32:
	case TYPE_UINT32:
		if (e) {
			switch (e->type) {
			case EXPR_INTEGER:
				fprintf(fp, "\t.long 0x%08x\n",
					(uint32_t) e->integer);
				break;
			case EXPR_TYPE_CONVERSION:
				if (e->expr0->type == EXPR_AMPHERSAND) {
					/*
					 * Special case: (int) &var
					 */
					dion = e->expr0->expr0->declaration;
				} else if (e->expr0->type == EXPR_IDENTIFIER) {
					/*
					 * Special case: (int) array
					 * Special case: (int) func
					 */
					dion = e->expr0->declaration;
				} else {
					dion = NULL;
				}
				assert(dion);

				fprintf(fp, "\t.long %s\n",
						dion->identifier);
				break;
			default:
				assert(0);
			}
		} else {
			fprintf(fp, "\t.long 0x00000000\n");
		}
		break;
	case TYPE_INT64:
	case TYPE_UINT64:
		if (e) {
			switch (e->type) {
			case EXPR_INTEGER:
				fprintf(fp, "\t.long 0x%08x, 0x%08x\n",
					(uint32_t) (e->integer >>  0),
					(uint32_t) (e->integer >> 32));
				break;
			default:
				assert(0);
			}
		} else {
			fprintf(fp, "\t.long 0x00000000, 0x00000000\n");
		}
		break;

	case TYPE_FLOAT32:
		if (e) {
			assert(e->type == EXPR_REAL);
			fprintf(fp, "\t.float %Le\n", e->real);
		} else {
			fprintf(fp, "\t.float 0.0\n");
		}
		break;

	case TYPE_FLOAT64:
		if (e) {
			assert(e->type == EXPR_REAL);
			fprintf(fp, "\t.dfloat %Le\n", e->real);
		} else {
			fprintf(fp, "\t.dfloat 0.0\n");
		}
		break;

	case TYPE_FLOAT80:
		/* 10 Bytes Data */
		if (e) {
			assert(e->type == EXPR_REAL);
			fprintf(fp, "\t.tfloat %Le\n", e->real);
		} else {
			fprintf(fp, "\t.tfloat 0.0\n");
		}
		/* 6 Bytes Alignment */
		fprintf(fp, "\t.byte 0, 0, 0, 0, 0, 0\n");
		break;

	case TYPE_STRUCT: {
		struct expr *ce;

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

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

		for (dion = t->scope->declaration_first, ce = e->first;
		    dion;
		    dion = dion->next, ce = (ce ? ce->next : NULL)) {
			arch_gen_data_init(fp, scope, dion->type_name, ce);
		}
		break;
	    }

	case TYPE_VA_LIST:
	case TYPE_POINTER:
		assert(0);
		break;

	case TYPE_ARRAY: {
		unsigned long long dim;
		struct expr *ce;
		unsigned long long i;
		unsigned char c;

		assert(t->dimension);
		assert(t->dimension->type == EXPR_INTEGER);
		dim = t->dimension->integer;
		t = type_array_access(t);

		switch (e->type) {
		case EXPR_BRACES:
			for (i = 0, ce = e->first;
			    i < dim;
			    i++, ce = (ce ? ce->next : NULL)) {
				arch_gen_data_init(fp, scope, t, ce);
			}
			break;

		case EXPR_STRING:
			assert(t->type == TYPE_INT8
			    || t->type == TYPE_UINT8);

			fprintf(fp, "\t.string \"");

			for (i = 0; i < dim; i++) {
				if (i < e->string_len) {
					c = e->string[i];
				} else {
					c = '\0';
				}
				switch (c) {
				case '\0':
					fprintf(fp, "\\0");
					break;
				case '\a':
					fprintf(fp, "\\a");
					break;
				case '\b':
					fprintf(fp, "\\b");
					break;
				case '\n':
					fprintf(fp, "\\n");
					break;
				case '\r':
					fprintf(fp, "\\r");
					break;
				case '\t':
					fprintf(fp, "\\t");
					break;
				case '"':
					fprintf(fp, "\\\"");
					break;
				case '\\':
					fprintf(fp, "\\\\");
					break;
				default:
					if (' ' <= c && c < 127) {
						fprintf(fp, "%c", c);
					} else {
						fprintf(fp, "\\%03o", c);
					}
				}
			}
			fprintf(fp, "\"\n");
			break;
			
		default:
			assert(0);
		}
		break;
	    }

	default:
		assert(0);
	}
}

static void
arch_gen_data(FILE *fp, struct scope *scope, struct declaration *dion)
{
	unsigned int align;
	unsigned int size;

	type_align_size(scope, dion->type_name, &align, &size);
	if (dion->attr_aligned) {
		align = dion->attr_aligned;
	}

	if (dion->type_name->mod_const) {
		fprintf(fp, "\t.section .rodata\n");
	} else {
		fprintf(fp, "\t.data\n");
	}
	if (dion->initializer) {
		if (dion->storage == STORAGE_STATIC) {
			/*
			 * NOTE:
			 * Label *must* be global. Otherwise our
			 * linker tool will not generate __SEG_x/__OFF_x
			 * symbols for it.
			 */
			fprintf(fp, "\t.globl %s\n",
					arch_gen_local(dion->identifier));

			fprintf(fp, "\t.align %u\n", align);
			fprintf(fp, "\t.type %s, @object\n",
					arch_gen_local(dion->identifier));
			fprintf(fp, "\t.size %s, %u\n",
					arch_gen_local(dion->identifier),
					size);
			fprintf(fp, "%s:\n",
					arch_gen_local(dion->identifier));
		} else {
			fprintf(fp, "\t.globl %s\n",
					arch_gen_global(dion->identifier));
			fprintf(fp, "\t.align %u\n", align);
			fprintf(fp, "\t.type %s, @object\n",
					arch_gen_global(dion->identifier));
			fprintf(fp, "\t.size %s, %u\n",
					arch_gen_global(dion->identifier),
					size);
			fprintf(fp, "%s:\n",
					arch_gen_global(dion->identifier));
		}
		arch_gen_data_init(fp, scope, dion->type_name,
				dion->initializer);
	} else {
		if (dion->storage == STORAGE_STATIC) {
			fprintf(fp, "\t.local %s\n",
					arch_gen_local(dion->identifier));
			fprintf(fp, "\t.comm %s, %u, %u\n",
					arch_gen_local(dion->identifier),
					size, align);
		} else {
			fprintf(fp, "\t.comm %s, %u, %u\n",
					arch_gen_global(dion->identifier),
					size, align);
		}
	}
}

static struct declaration *
arch_tmp(struct scope *s, struct type *t)
{
	struct declaration *var;

	var = simplify_declaration_add(s, t, identifier_tmp());
	var->storage = STORAGE_AUTO;

	return var;
}

static void
arch_op_func(
	struct expr *e,
	struct declaration *dion
)
{
	struct expr *ce0;
	struct expr *ce1;

	ce0 = e->expr0;
	ce1 = e->expr1;

	e->type = EXPR_FUNC;

	e->expr0 = expr_new();
	e->expr0->type = EXPR_IDENTIFIER;
	e->expr0->declaration = dion;

	e->expr1 = expr_new();
	e->expr1->type = EXPR_LIST;
	ce0->prev = NULL;
	ce0->next = ce1;
	ce1->prev = ce0;
	ce1->next = NULL;
	e->expr1->first = ce0;
	e->expr1->last = ce1;
}

static void
arch_gen_expr_1(
	struct stmt *block,
	struct stmt *s,
	struct expr *e,
	struct expr *lhs
)
{
	struct type *t;
	struct type *t1;
	struct expr *ce;
	struct declaration *var;
	struct declaration *var0;
	struct declaration *var1;
	struct declaration *var2;
	struct declaration *dion;
	uint64_t regs_change;
	unsigned int reg;

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

	assert(lhs
	    || e->type == EXPR_FUNC);

	if (lhs
	 && lhs->type == EXPR_IDENTIFIER
	 && (lhs->declaration->storage == STORAGE_STATIC
	  || lhs->declaration->storage == STORAGE_NONE
	  || lhs->declaration->storage == STORAGE_EXTERN)) {
		/*
		 * Store Operation
		 */
		assert(e->type == EXPR_INTEGER
		    || e->type == EXPR_REAL
		    || e->type == EXPR_IDENTIFIER);

		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			/*
			 * movb %1, %0
			 */
			constraint_input(s, "m", expr_dup(lhs));
			constraint_input(s, "iq", expr_dup(e));
			break;
		case TYPE_INT16:
		case TYPE_UINT16:
			/*
			 * movw %1, %0
			 */
			/*FALLTHROUGH*/
		case TYPE_INT32:
		case TYPE_UINT32:
			/*
			 * movl %1, %0
			 */
			/*FALLTHROUGH*/
		case TYPE_INT64:
		case TYPE_UINT64:
			/*
			 * movl %1, %0
			 * movl %u1, %u0
			 */
			constraint_input(s, "m", expr_dup(lhs));
			constraint_input(s, "ir", expr_dup(e));
			break;
		case TYPE_FLOAT32:
		case TYPE_FLOAT64:
		case TYPE_FLOAT80:
			assert(0); /* FIXME */
		case TYPE_VA_LIST:
			assert(0); /* FIXME */
		default:
			assert(0); /* Mustn't happen. */
		}

	} else if (e->type == EXPR_IDENTIFIER
		&& (e->declaration->storage == STORAGE_STATIC
		 || e->declaration->storage == STORAGE_NONE
		 || e->declaration->storage == STORAGE_EXTERN)) {
		/*
		 * Load Operation
		 */
		assert(lhs
		    && lhs->type == EXPR_IDENTIFIER
		    && lhs->declaration->storage != STORAGE_STATIC
		    && lhs->declaration->storage != STORAGE_NONE
		    && lhs->declaration->storage != STORAGE_EXTERN);

		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			/*
			 * movb %1, %0
			 */
			/*FALLTHROUGN*/
		case TYPE_INT16:
		case TYPE_UINT16:
			/*
			 * movw %1, %0
			 */
			/*FALLTHROUGH*/
		case TYPE_INT32:
		case TYPE_UINT32:
			/*
			 * movl %1, %0
			 */
			/*FALLTHROUGH*/
		case TYPE_INT64:
		case TYPE_UINT64:
			/*
			 * movl %1, %0
			 * movl %u1, %u0
			 */
			constraint_output(s, "=r", expr_dup(lhs));
			constraint_input(s, "m", expr_dup(e));
			break;
		case TYPE_FLOAT32:
		case TYPE_FLOAT64:
		case TYPE_FLOAT80:
			assert(0); /* FIXME */
		case TYPE_VA_LIST:
			assert(0); /* FIXME */
		default:
			assert(0); /* Mustn't happen. */
		}

	} else {
		/*
		 * Non-Load/Non-Store Operation
		 */
		switch (e->type) {
		case EXPR_NONE:

		case EXPR_BRACES:

		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_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_NOT:
		case EXPR_SHORT_AND:
		case EXPR_SHORT_OR:
		case EXPR_CONDITION:
		case EXPR_LIST:

		case EXPR_STRING:

		case EXPR_AMPHERSAND:
			assert(0);
			break;

		case EXPR_INTEGER:
		case EXPR_REAL:
		case EXPR_IDENTIFIER:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * movb %1, %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * movw %1, %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * movl %1, %0
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_input(s, "imr", expr_dup(e));
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * movl %1, %0
				 * movl %u1, %u0
				 */
				constraint_output(s, "=&r", expr_dup(lhs));
				constraint_input(s, "imr", expr_dup(e));
				break;
			case TYPE_FLOAT32:
				/*
				 * fsts %0
				 */
			case TYPE_FLOAT64:
				/*
				 * fstl %0
				 */
			case TYPE_FLOAT80:
				/*
				 * fstt %0
				 */
				constraint_output(s, "=m", expr_dup(lhs));
				constraint_input(s, "it", expr_dup(e));
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_BUILTIN_VA_ARG:
			t1 = e->type_name;

			switch (t1->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * movb (%1), %0
				 * addl $4, %1
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * movw (%1), %0
				 * addl $4, %1
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * movl (%1), %0
				 * addl $4, %1
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * movl (%1), %0
				 * movl 4(%1), %u0
				 * addl $8, %1
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_output(s, "=r", expr_dup(e->expr0));
				constraint_input(s, "1", expr_dup(e->expr0));
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0);
			default:
				assert(0);
			}
			break;

		case EXPR_TYPE_CONVERSION:
			if (e->expr0->type == EXPR_AMPHERSAND) {
				/*
				 * Special case: (int) &var
				 */
				dion = e->expr0->expr0->declaration;
				e = e->expr0->expr0;
				goto special;

			} else if (e->expr0->type == EXPR_IDENTIFIER
				&& (e->expr0->declaration->type_name->type == TYPE_ARRAY
				 || e->expr0->declaration->type_name->type == TYPE_FUNCTION)) {
				/*
				 * Special case: (int) array
				 * Special case: (int) function
				 */
				dion = e->expr0->declaration;
				e = e->expr0;
			special:
				if (dion->storage == STORAGE_NONE
				 || dion->storage == STORAGE_EXTERN
				 || dion->storage == STORAGE_STATIC) {
					/*
					 * movl $%1, %0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "m", expr_dup(e));
				} else {
					/*
					 * leal %1, %0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "m", expr_dup(e));
				}
				break;
			}

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

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				switch (t1->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
					/*
					 * No code.
					 */
					constraint_output(s, "=mr", expr_dup(lhs));
					constraint_input(s, "0", expr_dup(e->expr0));
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
					/*
					 * movb %b1, %0
					 */
					/*FALLTHROUGH*/
				case TYPE_INT32:
				case TYPE_UINT32:
					/*
					 * movb %b1, %0
					 */
					/*FALLTHROUGH*/
				case TYPE_INT64:
				case TYPE_UINT64:
					/*
					 * movb %b1, %0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mq", expr_dup(e->expr0));
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					assert(0); /* FIXME */
					break;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				switch (t1->type) {
				case TYPE_INT8:
					/*
					 * movsbw %1, %0
					 */
					/*FALLTHROUGH*/
				case TYPE_UINT8:
					/*
					 * movzbw %1, %0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mq", expr_dup(e->expr0));
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
					/*
					 * No code.
					 */
					constraint_output(s, "=mr", expr_dup(lhs));
					constraint_input(s, "0", expr_dup(e->expr0));
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					/*
					 * movw %w1, %0
					 */
					/*FALLTHROUGH*/
				case TYPE_INT64:
				case TYPE_UINT64:
					/*
					 * movw %w1, %0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					/*
					 * fnstcw %0		switch cntrl word
					 * movw %0, %2
					 * orw $0xc00, %2
					 * movw %2, %1
					 * fldcw %1
					 *
					 * fistps %3
					 *
					 * fldcw %0		switch back
					 */
					goto type_conv_float_to_int;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				switch (t1->type) {
				case TYPE_INT8:
					/*
					 * movsbl %1, %0
					 */
					/*FALLTHROUGH*/
				case TYPE_UINT8:
					/*
					 * movzbl %1, %0
					 */
					/*FALLTHROUGH*/
				case TYPE_INT16:
					/*
					 * movswl %1, %0
					 */
					/*FALLTHROUGH*/
				case TYPE_UINT16:
					/*
					 * movzwl %1, %0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					/*
					 * No code.
					 */
					constraint_output(s, "=mr", expr_dup(lhs));
					constraint_input(s, "0", expr_dup(e->expr0));
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					/*
					 * movl %1, %0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					/*
					 * fnstcw %0		switch cntrl word
					 * movw %0, %2
					 * orw $0xc00, %2
					 * movw %2, %1
					 * fldcw %1
					 *
					 * fistpl %3
					 *
					 * fldcw %0		switch back
					 */
					goto type_conv_float_to_int;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				switch (t1->type) {
				case TYPE_INT8:
					/*
					 * movsbl %1, %eax
					 * cltd
					 */
					constraint_output(s, "=A", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_UINT8:
					/*
					 * movzbl %1, %0
					 * xorl %u0, %u0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_INT16:
					/*
					 * movswl %1, %eax
					 * cltd
					 */
					constraint_output(s, "=A", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_UINT16:
					/*
					 * movzwl %1, %0
					 * xorl %u0, %u0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_INT32:
					/*
					 * movl %1, %eax
					 * cltd
					 */
					constraint_output(s, "=A", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_UINT32:
					/*
					 * movl %1, %0
					 * xorl %u0, %u0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					/*
					 * No code.
					 */
					constraint_output(s, "=mr", expr_dup(lhs));
					constraint_input(s, "0", expr_dup(e->expr0));
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					/*
					 * fnstcw %0		switch cntrl word
					 * movw %0, %2
					 * orw $0xc00, %2
					 * movw %2, %1
					 * fldcw %1
					 *
					 * fistpll %3
					 *
					 * fldcw %0		switch back
					 */
				type_conv_float_to_int:;
					var0 = arch_tmp(block->scope, type_uint16());
					var1 = arch_tmp(block->scope, type_uint16());
					var2 = arch_tmp(block->scope, type_uint16());

					constraint_output(s, "=m", expr_identifier(var0));
					constraint_output(s, "=m", expr_identifier(var1));
					constraint_output(s, "=r", expr_identifier(var2));
					constraint_output(s, "=m", expr_dup(lhs));
					constraint_input(s, "t", expr_dup(e->expr0));
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				switch (t1->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
				case TYPE_INT16:
				case TYPE_UINT16:
				case TYPE_INT32:
				case TYPE_UINT32:
				case TYPE_POINTER:
				case TYPE_INT64:
				case TYPE_UINT64:
					assert(0); /* FIXME */
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					/*
					 * No code. Constraint only.
					 */
					constraint_output(s, "=t", expr_dup(lhs));
					constraint_input(s, "0", expr_dup(e->expr0));
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_VA_LIST:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_STAR:
			assert(lhs);

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * movb (%1), %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * movw (%1), %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * movl (%1), %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * movl (%1), %0
				 * movl 4(%1), %u0
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_input(s, "r", expr_dup(e->expr0->expr0));
				break;
			case TYPE_FLOAT32:
				/*
				 * movl (%2), %0
				 * movl %0, %1
				 *
				 * flds (%2)
				 *
				 * flds (%2)
				 * fstp %1
				 */
				goto star_float;
			case TYPE_FLOAT64:
				/*
				 * movl (%2), %0
				 * movl %0, %1
				 * movl 4(%2), %0
				 * movl %0, 4+%1
				 *
				 * fldl (%2)
				 *
				 * fldl (%2)
				 * fstp %1
				 */
				goto star_float;
			case TYPE_FLOAT80:
				/*
				 * movl (%2), %0
				 * movl %0, %1
				 * movl 4(%2), %0
				 * movl %0, 4+%1
				 * movl 8+(%2), %0
				 * movl %0, 8+%1
				 * movl 12+(%2), %0
				 * movl %0, 12+%1
				 *
				 * fldt (%2)
				 *
				 * fldt (%2)
				 * fstp %1
				 */
			star_float:;
				var = arch_tmp(block->scope, type_uint32());

				constraint_output(s, "=&r", expr_identifier(var));
				constraint_output(s, "=fm", expr_dup(lhs));
				constraint_input(s, "r", expr_dup(e->expr0->expr0));
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_NEG:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
			case TYPE_INT16:
			case TYPE_UINT16:
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * neg{b,w,l} %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * negl %0
				 * adcl $0, %u0
				 * negl %u0
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}

		case EXPR_INV:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * xorb $-1, %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * xorw $-1, %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * xorl $-1, %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * xorl $-1, %0
				 * xorl $-1, %u0
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_EQUAL:
		case EXPR_NOT_EQUAL:
		case EXPR_LESS:
		case EXPR_LESS_EQUAL:
		case EXPR_GREATER:
		case EXPR_GREATER_EQUAL:
			if (e->expr0->type == EXPR_INTEGER
			 || e->expr0->type == EXPR_REAL) {
				/* Exchange lhs/rhs. */
				struct expr *tmp;

				tmp = e->expr0;
				e->expr0 = e->expr1;
				e->expr1 = tmp;

				switch (e->type) {
				case EXPR_EQUAL:
					break;
				case EXPR_NOT_EQUAL:
					break;
				case EXPR_LESS:
					e->type = EXPR_GREATER;
					break;
				case EXPR_LESS_EQUAL:
					e->type = EXPR_GREATER_EQUAL;
					break;
				case EXPR_GREATER:
					e->type = EXPR_LESS;
					break;
				case EXPR_GREATER_EQUAL:
					e->type = EXPR_LESS_EQUAL;
					break;
				default:
					assert(0);
				}
			}

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * cmp{b,w,l} %3, %2
				 * setcc %1
				 * movb %1, %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * cmp{b,w,l} %3, %2
				 * setcc %1
				 * movzbw %1, %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * cmp{b,w,l} %3, %2
				 * setcc %1
				 * movzb{b,w,l} %1, %0
				 */
				var = arch_tmp(block->scope, type_char());

				constraint_output(s, "=r", expr_dup(lhs));
				constraint_output(s, "=rm", expr_identifier(var));
				constraint_input(s, "ri", expr_dup(e->expr0));
				constraint_input(s, "rm", expr_dup(e->expr1));
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * cmpw %w2, %w1
				 * jcc 2f
				 * ...
				 * 1:
				 * movw $1, %0
				 * jmp 3f
				 * 2:
				 * xorw %0, %0
				 * 3:
				 */
				constraint_output(s, "=mr", expr_dup(lhs));
				constraint_input(s, "r", expr_dup(e->expr0));
				constraint_input(s, "ri", expr_dup(e->expr1));
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_LEFT:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
			case TYPE_INT16:
			case TYPE_UINT16:
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * shl{b,w,l} %b2, %0
				 */
				goto shift_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * shldl %0, %u0
				 * sall %b2, %0
				 * testb $32, %b2
				 * je 1f
				 * movl %0, %u0
				 * xorl %0, %0
				 * 1:
				 */
				goto shift_int;
			default:
				assert(0);
			}
			break;

		case EXPR_RIGHT:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_INT16:
			case TYPE_INT32:
				/*
				 * sar{b,w,l} %b2, %0
				 */
				goto shift_int;
			case TYPE_UINT8:
			case TYPE_UINT16:
			case TYPE_UINT32:
				/*
				 * shr{b,w,l} %b2, %0
				 */
				goto shift_int;
			case TYPE_INT64:
				/*
				 * shrdl %u0, %0
				 * sarl %b2, %u0
				 * testb $32, %b2
				 * je 1f
				 * movl %u0, %0
				 * sarl $31, %u0
				 * 1:
				 */
				goto shift_int;
			case TYPE_UINT64:
				/*
				 * shrdl %u0, %0
				 * shrl %b2, %u0
				 * testb $32, %b2
				 * je 1f
				 * movl %u0, %0
				 * xorl %u0, %u0
				 * 1:
				 */
			shift_int:
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "ci", expr_dup(e->expr1));
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_ADD:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * addb %2, %0
				 */
				goto sub_int;
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * addw %2, %0
				 */
				goto sub_int;
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * addl %2, %0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * addl %2, %0
				 * adcl %u2, %u0
				 */
				goto sub_int;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				/*
				 * faddp
				 */
				goto sub_float;
			default:
				assert(0);
			}
			break;

		case EXPR_SUB:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * subb %2, %0
				 */
				goto sub_int;
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * subw %2, %0
				 */
				goto sub_int;
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * subl %2, %0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * subl %2, %0
				 * sbbl %u2, %u0
				 */
			sub_int:
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "imr", expr_dup(e->expr1));
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				/*
				 * fsubp
				 */
			sub_float:;
				constraint_output(s, "=t", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "u", expr_dup(e->expr1));
				/* constraint_change(s, "st(1)"); FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_MUL:
			switch (t->type) {
			case TYPE_INT8:
				/*
				 * imulb %2
				 */
				/*FALLTHROUGH*/
			case TYPE_UINT8:
				/*
				 * mulb %2
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
				/*
				 * imulw %2
				 */
				/*FALLTHROUGH*/
			case TYPE_UINT16:
				/*
				 * mulw %2
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
				/*
				 * imull %2
				 */
				/*FALLTHROUGH*/
			case TYPE_UINT32:
				/*
				 * mull %2
				 */
				constraint_output(s, "=a", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "mr", expr_dup(e->expr1));
				constraint_change(s, "edx");
				break;

			case TYPE_INT64:
				arch_op_func(e, arch_mul_int64);
				goto func;
			case TYPE_UINT64:
				arch_op_func(e, arch_mul_uint64);
				goto func;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_DIV:
			switch (t->type) {
			case TYPE_INT8:
				/*
				 * cbw
				 * idivb %2
				 */
				/*FALLTHROUGN*/
			case TYPE_UINT8:
				/*
				 * xorb %%ah, %%ah
				 * divb %2
				 */
				constraint_output(s, "=a", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "mr", expr_dup(e->expr1));
				constraint_change(s, "ah");
				break;
			case TYPE_INT16:
				/*
				 * cwd
				 * idivw %2
				 */
				/*FALLTHROUGH*/
			case TYPE_UINT16:
				/*
				 * xorw %%dx, %%dx
				 * divw %2
				 */
				constraint_output(s, "=a", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "mr", expr_dup(e->expr1));
				constraint_change(s, "dx");
				break;
			case TYPE_INT32:
				/*
				 * cltd
				 * idivl %2
				 */
				/*FALLTHROUGH*/
			case TYPE_UINT32:
				/*
				 * xorl %%edx, %%edx
				 * divl %2
				 */
				constraint_output(s, "=a", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "mr", expr_dup(e->expr1));
				constraint_change(s, "edx");
				break;
			case TYPE_INT64:
				arch_op_func(e, arch_div_int64);
				goto func;
			case TYPE_UINT64:
				arch_op_func(e, arch_div_uint64);
				goto func;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;

			default:
				assert(0);
			}
			break;

		case EXPR_MOD:
			switch (t->type) {
			case TYPE_INT8:
				/*
				 * cbw
				 * idiv %2
				 * movb %%ah, %%al
				 */
				/*FALLTHROUGH*/
			case TYPE_UINT8:
				/*
				 * xorb %%ah, %%ah
				 * div %2
				 * movb %%ah, %%al
				 */
				constraint_output(s, "=a", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "mr", expr_dup(e->expr1));
				constraint_change(s, "ah");
				break;
			case TYPE_INT16:
				/*
				 * cwd
				 * idivw %2
				 */
				/*FALLTHROUGH*/
			case TYPE_UINT16:
				/*
				 * xorw %%dx, %%dx
				 * divw %3
				 */
				var = arch_tmp(block->scope, type_uint16());

				constraint_output(s, "=a", expr_identifier(var));
				constraint_output(s, "=&d", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "mr", expr_dup(e->expr1));
				break;
			case TYPE_INT32:
				/*
				 * cltd
				 * idivl %3
				 */
				/*FALLTHROUGH*/
			case TYPE_UINT32:
				/*
				 * xorl %%edx, %%edx
				 * divl %3
				 */
				var = arch_tmp(block->scope, type_uint32());

				constraint_output(s, "=a", expr_identifier(var));
				constraint_output(s, "=&d", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "rm", expr_dup(e->expr1));
				break;
			case TYPE_INT64:
				arch_op_func(e, arch_mod_int64);
				goto func;
			case TYPE_UINT64:
				arch_op_func(e, arch_mod_uint64);
				goto func;

			case TYPE_FLOAT32:
				assert(0); /* FIXME */
				break;
			case TYPE_FLOAT64:
				assert(0); /* FIXME */
				break;
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_AND:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * andb %2, %0
				 */
				goto sub_int;
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * andw %2, %0
				 */
				goto sub_int;
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * andl %2, %0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * andl %2, %0
				 * andl %u2, %u0
				 */
				goto sub_int;
			default:
				assert(0);
			}
			break;

		case EXPR_OR:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * orb %2, %0
				 */
				goto sub_int;
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * orw %2, %0
				 */
				goto sub_int;
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * orl %2, %0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * orl %2, %0
				 * orl %u2, %u0
				 */
				goto sub_int;
			default:
				assert(0);
			}
			break;

		case EXPR_XOR:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * xorb %2, %0
				 */
				goto sub_int;
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * xorw %2, %0
				 */
				goto sub_int;
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * xorl %2, %0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * xorl %2, %0
				 * xorl %u2, %u0
				 */
				goto sub_int;
			default:
				assert(0);
			}
			break;

		case EXPR_FUNC:
		func:	;
			/*
			 * pushl %N-1
			 * pushl %N-2
			 * ...
			 * pushl %1
			 * call %0
			 * addl $c, %esp
			 */
			regs_change = REG_CALLER;
			if (lhs) {
				switch (t->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
				case TYPE_INT16:
				case TYPE_UINT16:
				case TYPE_INT32:
				case TYPE_UINT32:
					constraint_output(s, "=a", expr_dup(lhs));
					regs_change &= ~(1ULL << EAX);
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					constraint_output(s, "=A", expr_dup(lhs));
					regs_change &= ~((1ULL << EAX) | (1ULL << EDX));
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					constraint_output(s, "=t", expr_dup(lhs));
					regs_change &= ~(1ULL << ST0);
					break;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
			}

			switch (e->expr0->type) {
			case EXPR_IDENTIFIER:
				break;
			case EXPR_STAR:
				constraint_input(s, "r", expr_dup(e->expr0->expr0->expr0));
				break;
			default:
				assert(0);
			}

			for (ce = e->expr1->first; ce; ce = ce->next) {
				constraint_input(s, "rmi", expr_dup(ce));
			}

			constraint_change(s, "memory");
			for (reg = 0; regs_change; reg++) {
				if ((regs_change >> reg) & 1) {
					constraint_change(s, arch_reginfo[reg].name);
					regs_change &= ~(1ULL << reg);
				}
			}
			break;

		default:
			assert(0);
		}
	}
}

static void
arch_gen_expr_2(
	char *buf0,
	struct stmt *block,
	struct stmt *s,
	struct expr *e,
	struct expr *lhs
)
{
	struct type *t;
	struct type *t1;
	struct expr *ce;
	unsigned int size;
	struct constraint *c;
	int reglhs;
	int reg;
	int reg0;
	int reg1;
	unsigned int n;

	assert(lhs
	    || e->type == EXPR_FUNC);

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

	if (lhs
	 && lhs->type == EXPR_IDENTIFIER
	 && lhs->declaration->storage == STORAGE_REGISTER) {
		reglhs = lhs->declaration->storage_register;
	} else {
		reglhs = -1;
	}
	if (e->type == EXPR_IDENTIFIER
	 && e->declaration->storage == STORAGE_REGISTER) {
		reg = e->declaration->storage_register;
	} else {
		reg = -1;
	}
	if (e->expr0
	 && e->expr0->type == EXPR_IDENTIFIER
	 && e->expr0->declaration->storage == STORAGE_REGISTER) {
		reg0 = e->expr0->declaration->storage_register;
	} else {
		reg0 = -1;
	}
	if (e->expr1
	 && e->expr1->type == EXPR_IDENTIFIER
	 && e->expr1->declaration->storage == STORAGE_REGISTER) {
		reg1 = e->expr1->declaration->storage_register;
	} else {
		reg1 = -1;
	}

	if (lhs
	 && lhs->type == EXPR_IDENTIFIER
	 && (lhs->declaration->storage == STORAGE_STATIC
	  || lhs->declaration->storage == STORAGE_NONE
	  || lhs->declaration->storage == STORAGE_EXTERN)) {
		/*
		 * Store Operation
		 */
		assert(e->type == EXPR_INTEGER
		    || e->type == EXPR_REAL
		    || e->type == EXPR_IDENTIFIER);

		strcpy(buf0, "");
		
		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			strcat(buf0, "\tmovb %1, %0\n");
			break;
		case TYPE_INT16:
		case TYPE_UINT16:
			strcat(buf0, "\tmovw %1, %0\n");
			break;
		case TYPE_INT32:
		case TYPE_UINT32:
			strcat(buf0, "\tmovl %1, %0\n");
			break;
		case TYPE_INT64:
		case TYPE_UINT64:
			strcat(buf0, "\tmovl %1, %0\n");
			strcat(buf0, "\tmovl %u1, 4+%0\n");
			break;
		case TYPE_FLOAT32:
		case TYPE_FLOAT64:
		case TYPE_FLOAT80:
			assert(0); /* FIXME */
		case TYPE_VA_LIST:
			assert(0); /* FIXME */
		default:
			assert(0);
		}

	} else if (e->type == EXPR_IDENTIFIER
		&& (e->declaration->storage == STORAGE_STATIC
		 || e->declaration->storage == STORAGE_NONE
		 || e->declaration->storage == STORAGE_EXTERN)) {
		/*
		 * Load Operation
		 */
		assert(lhs
		    && lhs->type == EXPR_IDENTIFIER
		    && lhs->declaration->storage != STORAGE_STATIC
		    && lhs->declaration->storage != STORAGE_NONE
		    && lhs->declaration->storage != STORAGE_EXTERN);

		strcpy(buf0, "");
		
		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			strcat(buf0, "\tmovb %1, %0\n");
			break;
		case TYPE_INT16:
		case TYPE_UINT16:
			strcat(buf0, "\tmovw %1, %0\n");
			break;
		case TYPE_INT32:
		case TYPE_UINT32:
			strcat(buf0, "\tmovl %1, %0\n");
			break;
		case TYPE_INT64:
		case TYPE_UINT64:
			strcat(buf0, "\tmovl %1, %0\n");
			strcat(buf0, "\tmovl 4+%1, %u0\n");
			break;
		case TYPE_FLOAT32:
		case TYPE_FLOAT64:
		case TYPE_FLOAT80:
			assert(0); /* FIXME */
		case TYPE_VA_LIST:
			assert(0); /* FIXME */
		default:
			assert(0); /* Mustn't happen. */
		}

	} else {
		/*
		 * Non-Load/Non-Store Operation
		 */
		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_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_NOT:
		case EXPR_SHORT_AND:
		case EXPR_SHORT_OR:
		case EXPR_CONDITION:
		case EXPR_LIST:
			assert(0);

		case EXPR_STRING:
			assert(0);

		case EXPR_AMPHERSAND:
			assert(0);

		case EXPR_INTEGER:
		case EXPR_REAL:
		simple_copy:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\tmovb %1, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tmovw %1, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tmovl %1, %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tmovl %1, %0\n");
				strcat(buf0, "\tmovl %u1, %u0\n");
				break;
			case TYPE_FLOAT32:
				strcpy(buf0, "\tfstps %0\n");
				break;
			case TYPE_FLOAT64:
				strcpy(buf0, "\tfstpl %0\n");
				break;
			case TYPE_FLOAT80:
				strcpy(buf0, "\tfstpt %0\n");
				break;
			case TYPE_VA_LIST:
				strcpy(buf0, "\tmovl %1, %0\n");
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_IDENTIFIER:
			if (reglhs == -1
			 || reg == -1) {
				goto simple_copy;
			}
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
			case TYPE_INT16:
			case TYPE_UINT16:
			case TYPE_INT32:
			case TYPE_UINT32:
			case TYPE_VA_LIST:
				/*
				 * Only one register to copy => simple.
				 */
				if (reglhs == reg) {
					/*
					 * Omit instructions "mov %x, %x".
					 */
					strcpy(buf0, "");
					break;
				} else {
					goto simple_copy;
				}

			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * We have *two* registers in
				 * *two* real registers
				 * (e.g. EAX_EBX and EBX_ECX).
				 */
				assert(arch_gen_reg_parts[reglhs].count == 2);
				assert(arch_gen_reg_parts[reg].count == 2);

				if (arch_gen_reg_parts[reglhs].reg[0]
					== arch_gen_reg_parts[reg].reg[0]
				 && arch_gen_reg_parts[reglhs].reg[1]
					== arch_gen_reg_parts[reg].reg[1]) {
					/* A_B <- A_B. */
					strcpy(buf0, "");

				} else if (arch_gen_reg_parts[reglhs].reg[0]
						== arch_gen_reg_parts[reg].reg[1]
					&& arch_gen_reg_parts[reglhs].reg[1]
						== arch_gen_reg_parts[reg].reg[0]) {
					/* A_B <- B_A. */
					strcpy(buf0, "\txchgl %1, %0\n");

				} else if (arch_gen_reg_parts[reglhs].reg[0]
						== arch_gen_reg_parts[reg].reg[0]) {
					/* A_B <- A_C. */
					strcpy(buf0, "\tmovl %u1, %u0\n");

				} else if (arch_gen_reg_parts[reglhs].reg[1]
						== arch_gen_reg_parts[reg].reg[1]) {
					/* A_B <- C_B. */
					strcpy(buf0, "\tmovl %1, %0\n");

				} else if (arch_gen_reg_parts[reglhs].reg[0]
						== arch_gen_reg_parts[reg].reg[1]) {
					/* A_B <- C_A. */
					strcpy(buf0, "\tmovl %u1, %u0\n");
					strcat(buf0, "\tmovl %1, %0\n");

				} else if (arch_gen_reg_parts[reglhs].reg[1]
						== arch_gen_reg_parts[reg].reg[0]) {
					/* A_B <- B_C. */
					strcpy(buf0, "\tmovl %1, %0\n");
					strcat(buf0, "\tmovl %u1, %u0\n");

				} else {
					/* A_B <- C_D. */
					strcpy(buf0, "\tmovl %1, %0\n");
					strcat(buf0, "\tmovl %u1, %u0\n");
				}
				break;

			default:
				assert(0); /* Cannot happen. */
			}
			break;

		case EXPR_BUILTIN_VA_ARG:
			t1 = e->type_name;

			switch (t1->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\tmovb (%1), %0\n");
				strcat(buf0, "\taddl $1, %1\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tmovw (%1), %0\n");
				strcat(buf0, "\taddl $2, %1\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tmovl (%1), %0\n");
				strcat(buf0, "\taddl $4, %1\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tmovl (%1), %0\n");
				strcat(buf0, "\tmovl 4(%1), %u0\n");
				strcat(buf0, "\taddl $8, %1\n");
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0);
			default:
				assert(0);
			}
			break;

		case EXPR_TYPE_CONVERSION:
			if (e->expr0->type == EXPR_AMPHERSAND) {
				/*
				 * Special case: (int) &var
				 */
				if (e->expr0->expr0->declaration->storage
						== STORAGE_NONE
				 || e->expr0->expr0->declaration->storage
						== STORAGE_EXTERN
				 || e->expr0->expr0->declaration->storage
						== STORAGE_STATIC) {
					strcpy(buf0, "\tmovl %1, %0\n");
				} else {
					strcpy(buf0, "\tleal %1, %0\n");
				}
				break;

			} else if (e->expr0->type == EXPR_IDENTIFIER
				&& (e->expr0->declaration->type_name->type == TYPE_ARRAY
				 || e->expr0->declaration->type_name->type == TYPE_FUNCTION)) {
				/*
				 * Special case: (int) array
				 * Special case: (int) function
				 */
				if (e->expr0->declaration->storage == STORAGE_NONE
				 || e->expr0->declaration->storage == STORAGE_EXTERN
				 || e->expr0->declaration->storage == STORAGE_STATIC) {
					strcpy(buf0, "\tmovl %1, %0\n");
				} else {
					strcpy(buf0, "\tleal %1, %0\n");
				}
				break;
			}

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

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				switch (t1->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
					strcpy(buf0, "");
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
				case TYPE_INT32:
				case TYPE_UINT32:
				case TYPE_INT64:
				case TYPE_UINT64:
					strcpy(buf0, "\tmovb %b1, %0\n");
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					assert(0); /* FIXME */
					break;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				switch (t1->type) {
				case TYPE_INT8:
					strcpy(buf0, "\tmovsbw %1, %0\n");
					break;
				case TYPE_UINT8:
					strcpy(buf0, "\tmovzbw %1, %0\n");
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
					strcpy(buf0, "");
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
				case TYPE_INT64:
				case TYPE_UINT64:
					strcpy(buf0, "\tmovw %w1, %0\n");
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					/* Unsigned - FIXME */
					strcpy(buf0, "\tfnstcw %0\n"
							"\tmovw %0, %2\n"
							"\torw $0xc00, %2\n"
							"\tmovw %2, %1\n"
							"\tfldcw %1\n"
							"\tfistps %3\n"
							"\tfldcw %0\n");
					break;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				switch (t1->type) {
				case TYPE_INT8:
					strcpy(buf0, "\tmovsbl %1, %0\n");
					break;
				case TYPE_UINT8:
					strcpy(buf0, "\tmovzbl %1, %0\n");
					break;
				case TYPE_INT16:
					strcpy(buf0, "\tmovswl %1, %0\n");
					break;
				case TYPE_UINT16:
					strcpy(buf0, "\tmovzwl %1, %0\n");
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					strcpy(buf0, "");
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					strcpy(buf0, "\tmovl %1, %0\n");
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					/* Unsigned - FIXME */
					strcpy(buf0, "\tfnstcw %0\n");
					strcat(buf0, "\tmovw %0, %2\n");
					strcat(buf0, "\torw $0xc00, %2\n");
					strcat(buf0, "\tmovw %2, %1\n");
					strcat(buf0, "\tfldcw %1\n");
					strcat(buf0, "\tfistpl %3\n");
					strcat(buf0, "\tfldcw %0\n");
					break;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				switch (t1->type) {
				case TYPE_INT8:
					strcpy(buf0, "\tmovsbl %1, %0\n");
					strcat(buf0, "\tcltd\n");
					break;
				case TYPE_UINT8:
					strcpy(buf0, "\tmovzbl %1, %0\n");
					strcat(buf0, "\txorl %u0, %u0\n");
					break;
				case TYPE_INT16:
					strcpy(buf0, "\tmovswl %1, %0\n");
					strcat(buf0, "\tcltd\n");
					break;
				case TYPE_UINT16:
					strcpy(buf0, "\tmovzwl %1, %0\n");
					strcat(buf0, "\txorl %u0, %u0\n");
					break;
				case TYPE_INT32:
					strcpy(buf0, "\tmovl %1, %0\n");
					strcat(buf0, "\tcltd\n");
					break;
				case TYPE_UINT32:
					strcpy(buf0, "\tmovl %1, %0\n");
					strcat(buf0, "\txorl %u0, %u0\n");
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					strcpy(buf0, "");
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					/* Unsigned - FIXME */
					strcpy(buf0, "\tfnstcw %0\n"
							"\tmovw %0, %2\n"
							"\torw $0xc00, %2\n"
							"\tmovw %2, %1\n"
							"\tfldcw %1\n"
							"\tfistpll %3\n"
							"\tfldcw %0\n");
					break;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				switch (t1->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:
					assert(0); /* FIXME */
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					strcpy(buf0, "");
					break;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_STAR:
			assert(lhs);

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\tmovb (%1), %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tmovw (%1), %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tmovl (%1), %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tmovl (%1), %%eax\n"
						"\tmovl 4(%1), %%edx\n");
				break;
			case TYPE_FLOAT32:
				switch (s->output->first->next->expr->declaration->storage_register) {
				case ST0:
				case ST1:
					strcpy(buf0, "\tflds (%2)\n");
					break;
				case ST2:
				case ST3:
				case ST4:
				case ST5:
				case ST6:
				case ST7:
					strcpy(buf0, "\tflds (%2)\n"
							"\tfstp %1\n");
					break;
				default:
					/* Memory */
					strcpy(buf0, "\tmovl (%2), %0\n"
							"\tmovl %0, %1\n");
					break;
				}
				break;
			case TYPE_FLOAT64:
				switch (s->output->first->next->expr->declaration->storage_register) {
				case ST0:
				case ST1:
					strcpy(buf0, "\tfldl (%2)\n");
					break;
				case ST2:
				case ST3:
				case ST4:
				case ST5:
				case ST6:
				case ST7:
					strcpy(buf0, "\tfldl (%2)\n"
							"\tfstp %1\n");
					break;
				default:
					/* Memory */
					strcpy(buf0, "\tmovl (%2), %0\n"
							"\tmovl %0, %1\n"
							"\tmovl 4(%2), %0\n"
							"\tmovl %0, 4+%1\n");
					break;
				}
				break;
			case TYPE_FLOAT80:
				switch (s->output->first->next->expr->declaration->storage_register) {
				case ST0:
				case ST1:
					strcpy(buf0, "\tfldt (%2)\n");
					break;
				case ST2:
				case ST3:
				case ST4:
				case ST5:
				case ST6:
				case ST7:
					strcpy(buf0, "\tfldt (%2)\n"
							"\tfstp %1\n");
					break;
				default:
					/* Memory */
					strcpy(buf0, "\tmovl (%2), %0\n"
							"\tmovl %0, %1\n"
							"\tmovl 4(%2), %0\n"
							"\tmovl %0, 4+%1\n"
							"\tmovl 8(%2), %0\n"
							"\tmovl %0, 8+%1\n"
							"\tmovl 12(%2), %0\n"
							"\tmovl %0, 12+%1\n");
					break;
				}
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_NEG:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\tnegb %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tnegw %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tnegl %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tnegl %0\n");
				strcat(buf0, "\tadcl $0, %u0\n");
				strcat(buf0, "\tnegl %u0\n");
				break;
			case TYPE_FLOAT32:
				assert(0); /* FIXME */
				break;
			case TYPE_FLOAT64:
				assert(0); /* FIXME */
				break;
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_INV:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\txorb $-1, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\txorw $-1, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\txorl $-1, %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\txorl $-1, %0\n");
				strcat(buf0, "\txorl $-1, %u0\n");
				break;
			default:
				assert(0);
			}
			break;

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

			switch (t1->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
			case TYPE_INT16:
			case TYPE_UINT16:
			case TYPE_INT32:
			case TYPE_UINT32:
				switch (t1->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
					strcpy(buf0, "\tcmpb %3, %2\n");
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
					strcpy(buf0, "\tcmpw %3, %2\n");
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					strcpy(buf0, "\tcmpl %3, %2\n");
					break;
				default:
					assert(0);
				}
				switch (t1->type) {
				case TYPE_INT8:
				case TYPE_INT16:
				case TYPE_INT32:
					switch (e->type) {
					case EXPR_EQUAL:
						strcat(buf0, "\tsete %1\n");
						break;
					case EXPR_NOT_EQUAL:
						strcat(buf0, "\tsetne %1\n");
						break;
					case EXPR_LESS:
						strcat(buf0, "\tsetl %1\n");
						break;
					case EXPR_LESS_EQUAL:
						strcat(buf0, "\tsetle %1\n");
						break;
					case EXPR_GREATER:
						strcat(buf0, "\tsetg %1\n");
						break;
					case EXPR_GREATER_EQUAL:
						strcat(buf0, "\tsetge %1\n");
						break;
					default:
						assert(0);
					}
					break;
				case TYPE_UINT8:
				case TYPE_UINT16:
				case TYPE_UINT32:
					switch (e->type) {
					case EXPR_EQUAL:
						strcat(buf0, "\tsete %1\n");
						break;
					case EXPR_NOT_EQUAL:
						strcat(buf0, "\tsetne %1\n");
						break;
					case EXPR_LESS:
						strcat(buf0, "\tsetb %1\n");
						break;
					case EXPR_LESS_EQUAL:
						strcat(buf0, "\tsetbe %1\n");
						break;
					case EXPR_GREATER:
						strcat(buf0, "\tseta %1\n");
						break;
					case EXPR_GREATER_EQUAL:
						strcat(buf0, "\tsetae %1\n");
						break;
					default:
						assert(0);
					}
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				assert(0);
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0);
			default:
				assert(0);
			}

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcat(buf0, "\tmovzbw %1, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcat(buf0, "\tmovzbl %1, %0\n");
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_LEFT:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\tshlb %b2, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tshlw %b2, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tshll %b2, %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tshldl %0, %u0\n");
				strcat(buf0, "\tsall %b2, %0\n");
				strcat(buf0, "\ttestb $32, %b2\n");
				strcat(buf0, "\tje 1f\n");
				strcat(buf0, "\tmovl %0, %u0\n");
				strcat(buf0, "\txorl %0, %0\n");
				strcat(buf0, "1:\n");
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_RIGHT:
			switch (t->type) {
			case TYPE_INT8:
				strcpy(buf0, "\tsarb %b2, %0\n");
				break;
			case TYPE_UINT8:
				strcpy(buf0, "\tshrb %b2, %0\n");
				break;
			case TYPE_INT16:
				strcpy(buf0, "\tsarw %b2, %0\n");
				break;
			case TYPE_UINT16:
				strcpy(buf0, "\tshrw %b2, %0\n");
				break;
			case TYPE_INT32:
				strcpy(buf0, "\tsarl %b2, %0\n");
				break;
			case TYPE_UINT32:
				strcpy(buf0, "\tshrl %b2, %0\n");
				break;
			case TYPE_INT64:
				strcpy(buf0, "\tshrdl %u0, %0\n");
				strcat(buf0, "\tsarl %b2, %u0\n");
				strcat(buf0, "\ttestb $32, %b2\n");
				strcat(buf0, "\tje 1f\n");
				strcat(buf0, "\tmovl %u0, %0\n");
				strcat(buf0, "\tsarl $31, %u0\n");
				strcat(buf0, "1:\n");
				break;
			case TYPE_UINT64:
				strcpy(buf0, "\tshrdl %u0, %0\n");
				strcat(buf0, "\tsarl %b2, %u0\n");
				strcat(buf0, "\ttestb $32, %b2\n");
				strcat(buf0, "\tje 1f\n");
				strcat(buf0, "\tmovl %u0, %0\n");
				strcat(buf0, "\txorl %u0, %u0\n");
				strcat(buf0, "1:\n");
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_ADD:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\taddb %2, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\taddw %2, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\taddl %2, %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\taddl %2, %0\n");
				strcat(buf0, "\tadcl %u2, %u0\n");
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				strcpy(buf0, "\tfaddp\n");
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_SUB:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\tsubb %2, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tsubw %2, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tsubl %2, %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tsubl %2, %0\n");
				strcat(buf0, "\tsbbl %u2, %u0\n");
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				strcpy(buf0, "\tfsubp\n");
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_MUL:
			switch (t->type) {
			case TYPE_INT8:
				strcpy(buf0, "\timulb %2\n");
				break;
			case TYPE_UINT8:
				strcpy(buf0, "\tmulb %2\n");
				break;
			case TYPE_INT16:
				strcpy(buf0, "\timulw %2\n");
				break;
			case TYPE_UINT16:
				strcpy(buf0, "\tmulw %2\n");
				break;
			case TYPE_INT32:
				strcpy(buf0, "\timull %2\n");
				break;
			case TYPE_UINT32:
				strcpy(buf0, "\tmull %2\n");
				break;
			case TYPE_INT64:
				assert(0); /* Operator replaced by function call. */
			case TYPE_UINT64:
				assert(0); /* Operator replaced by function call. */
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0);
			default:
				assert(0);
			}
			break;

		case EXPR_DIV:
			switch (t->type) {
			case TYPE_INT8:
				strcpy(buf0, "\tcbw\n");
				strcat(buf0, "\tidivb %2\n");
				break;
			case TYPE_UINT8:
				strcpy(buf0, "\txorb %%ah, %%ah\n");
				strcat(buf0, "\tdivb %2\n");
				break;
			case TYPE_INT16:
				strcpy(buf0, "\tcwd\n");
				strcat(buf0, "\tidivw %2\n");
				break;
			case TYPE_UINT16:
				strcpy(buf0, "\txorw %%dx, %%dx\n");
				strcat(buf0, "\tdivw %2\n");
				break;
			case TYPE_INT32:
				strcpy(buf0, "\tcltd\n");
				strcat(buf0, "\tidivl %2\n");
				break;
			case TYPE_UINT32:
				strcpy(buf0, "\txorl %%edx, %%edx\n");
				strcat(buf0, "\tdivl %2\n");
				break;
			case TYPE_INT64:
				assert(0); /* Operator replaced by function call. */
			case TYPE_UINT64:
				assert(0); /* Operator replaced by function call. */
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_MOD:
			switch (t->type) {
			case TYPE_INT8:
				strcpy(buf0, "\tcbw\n");
				strcat(buf0, "\tidivb %2\n");
				strcat(buf0, "\tmovb %%ah, %%al\n");
				break;
			case TYPE_UINT8:
				strcpy(buf0, "\txorb %%ah, %%ah\n");
				strcat(buf0, "\tdivb %2\n");
				strcat(buf0, "\tmovb %%ah, %%al\n");
				break;
			case TYPE_INT16:
				strcpy(buf0, "\tcwd\n");
				strcat(buf0, "\tidivw %3\n");
				break;
			case TYPE_UINT16:
				strcpy(buf0, "\txorw %%dx, %%dx\n");
				strcat(buf0, "\tdivw %3\n");
				break;
			case TYPE_INT32:
				strcpy(buf0, "\tcltd\n");
				strcat(buf0, "\tidivl %3\n");
				break;
			case TYPE_UINT32:
				strcpy(buf0, "\txorl %%edx, %%edx\n");
				strcat(buf0, "\tdivl %3\n");
				break;
			case TYPE_INT64:
				assert(0); /* Operator replaced by function call. */
			case TYPE_UINT64:
				assert(0); /* Operator replaced by function call. */
			case TYPE_FLOAT32:
				assert(0); /* FIXME */
				break;
			case TYPE_FLOAT64:
				assert(0); /* FIXME */
				break;
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_AND:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\tandb %2, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tandw %2, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tandl %2, %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tandl %2, %0\n");
				strcat(buf0, "\tandl %u2, %u0\n");
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0);
			default:
				assert(0);
			}
			break;

		case EXPR_OR:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\torb %2, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\torw %2, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\torl %2, %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\torl %2, %0\n");
				strcat(buf0, "\torl %u2, %u0\n");
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0);
			default:
				assert(0);
			}
			break;

		case EXPR_XOR:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\txorb %2, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\txorw %2, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\txorl %2, %0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\txorl %2, %0\n");
				strcat(buf0, "\txorl %u2, %u0\n");
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0);
			default:
				assert(0);
			}
			break;

		case EXPR_FUNC:
			/*
			 * pushl %0
			 * pushl %1
			 * ...
			 * pushl %N-1
			 * call label
			 * addl $c, %esp
			 */
			strcpy(buf0, "");
			size = 0;
			n = 0;
			for (c = s->output->first; c; c = c->next) {
				n++;
			}
			for (c = s->input->first; c; c = c->next) {
				n++;
			}
			for (ce = e->expr1->last; ce; ce = ce->prev) {
				n--;
				if (ce->type == EXPR_TYPE_CONVERSION) {
					struct declaration *dion;

					if (ce->expr0->type == EXPR_AMPHERSAND) {
						/*
						 * Special case: (int) &var
						 */
						dion = ce->expr0->expr0->declaration;
					} else if (ce->expr0->type == EXPR_IDENTIFIER) {
						/*
						 * Special case: (int) array
						 * Special case: (int) function
						 */
						dion = ce->expr0->declaration;
					} else {
						dion = NULL;
					}
					assert(dion);
					if (dion->storage == STORAGE_NONE
					 || dion->storage == STORAGE_EXTERN
					 || dion->storage == STORAGE_STATIC) {
						sprintf(buf0 + strlen(buf0), "\tpushl %%%u\n", n);
					} else {
						sprintf(buf0 + strlen(buf0), "\tleal %%%u, %%%%eax\n", n); /* FIXME */
						sprintf(buf0 + strlen(buf0), "\tpushl %%%%eax\n"); /* FIXME */
					}
					size += 4;
					continue;
				}
				t1 = expr_typeof(block->scope, ce);
				t1 = type_pure(t1);

				switch (t1->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
					sprintf(buf0 + strlen(buf0), "\tpushb %%%u\n", n);
					size += 2;
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
					sprintf(buf0 + strlen(buf0), "\tpushw %%%u\n", n);
					size += 2;
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					sprintf(buf0 + strlen(buf0), "\tpushl %%%u\n", n);
					size += 4;
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					sprintf(buf0 + strlen(buf0), "\tpushl %%u%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushl %%%u\n", n);
					size += 8;
					break;
				case TYPE_FLOAT32:
					sprintf(buf0 + strlen(buf0), "\tpushl %%%u\n", n);
					break;
				case TYPE_FLOAT64:
					sprintf(buf0 + strlen(buf0), "\tpushl %%u%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushl %%%u\n", n);
					size += 8;
				case TYPE_FLOAT80:
					sprintf(buf0 + strlen(buf0), "\tpushl 12+%%%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushl 8+%%%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushl 4+%%%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushl %%%u\n", n);
					size += 16;
					break;
				case TYPE_VA_LIST:
					sprintf(buf0 + strlen(buf0), "\tpushl %%%u\n", n);
					size += 4;
					break;
				default:
					assert(0);
				}
			}
			switch (e->expr0->type) {
			case EXPR_IDENTIFIER:
				sprintf(buf0 + strlen(buf0), "\tcall %s\n",
						e->expr0->declaration->identifier);
				break;
			case EXPR_STAR:
				sprintf(buf0 + strlen(buf0), "\tcall *%%%u\n", n++);
				break;
			default:
				assert(0);
			}
			if (0 < size) {
				sprintf(buf0 + strlen(buf0), "\taddl $%u, %%%%esp\n",
						size);
			}
			break;

		default:
			assert(0);
		}
	}
}

static void
arch_gen_stmt_simplify_1(struct stmt *block, struct stmt *s)
{
	struct type *t;
	struct declaration *var;

	switch (s->type) {
	case STMT_NONE:
		assert(0);
	case STMT_NULL:
	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:
		assert(0);

	case STMT_LABEL:
		/*
		 * label:
		 */
		/* No constraints needed. */
		break;

	case STMT_EXPR:
		switch (s->expr0->type) {
		case EXPR_ASSIGN:
			switch (s->expr0->expr0->type) {
			case EXPR_IDENTIFIER:
				arch_gen_expr_1(block, s, s->expr0->expr1,
						s->expr0->expr0);
				break;

			case EXPR_STAR:
				t = expr_typeof(block->scope, s->expr0->expr0);
				t = type_pure(t);

				switch (t->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
					/*
					 * movb %1, (%0)
					 */
					/*FALLTHROUGH*/
				case TYPE_INT16:
				case TYPE_UINT16:
					/*
					 * movw %1, (%0)
					 */
					/*FALLTHROUGH*/
				case TYPE_INT32:
				case TYPE_UINT32:
					/*
					 * movl %1, (%0)
					 */
					/*FALLTHROUGH*/
				case TYPE_INT64:
				case TYPE_UINT64:
					/*
					 * movl %1, (%0)
					 * movl %u1, 4(%0)
					 */
					constraint_input(s, "r",
						expr_dup(s->expr0->expr0->expr0->expr0));
					constraint_input(s, "r",
						expr_dup(s->expr0->expr1));
					break;
				case TYPE_FLOAT32:
					/*
					 * movl %2, %0
					 * movl %0, (%1)
					 *
					 * fstps (%1)
					 *
					 * flds %2
					 * fstps (%1)
					 */
					/*FALLTHROUGH*/
				case TYPE_FLOAT64:
					/*
					 * movl %2, %0
					 * movl %0, (%1)
					 * movl 4+%2, %0
					 * movl %0, 4(%1)
					 *
					 * fstpl (%1)
					 *
					 * fldl %2
					 * fstpl (%1)
					 */
					/*FALLTHROUGH*/
				case TYPE_FLOAT80:
					/*
					 * movl %2, %0
					 * movl %0, (%1)
					 * movl 4+%2, %0
					 * movl %0, 4(%1)
					 * movl 8+%2, %0
					 * movl %0, 8(%1)
					 * movl 12+%2, %0
					 * movl %0, 12(%1)
					 *
					 * fstpt (%1)
					 *
					 * fldt %2
					 * fstpt (%1)
					 */
					var = arch_tmp(block->scope,
							type_uint32());

					constraint_output(s, "=r",
						expr_identifier(var));
					constraint_input(s, "r",
						expr_dup(s->expr0->expr0->expr0->expr0));
					constraint_input(s, "fm",
						expr_dup(s->expr0->expr1));
					break;
				default:
					assert(0);
				}
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_FUNC:
			arch_gen_expr_1(block, s, s->expr0, NULL);
			break;

		default:
			assert(0);
		}
		break;

	case STMT_IF:
		t = expr_typeof(block->scope, s->expr0->expr0);
		t = type_pure(t);

		if (s->expr0->expr0->type == EXPR_INTEGER
		 || s->expr0->expr0->type == EXPR_REAL) {
			/* Exchange lhs/rhs. */
			struct expr *tmp;

			tmp = s->expr0->expr0;
			s->expr0->expr0 = s->expr0->expr1;
			s->expr0->expr1 = tmp;

			switch (s->expr0->type) {
			case EXPR_EQUAL:
				break;
			case EXPR_NOT_EQUAL:
				break;
			case EXPR_LESS:
				s->expr0->type = EXPR_GREATER;
				break;
			case EXPR_LESS_EQUAL:
				s->expr0->type = EXPR_GREATER_EQUAL;
				break;
			case EXPR_GREATER:
				s->expr0->type = EXPR_LESS;
				break;
			case EXPR_GREATER_EQUAL:
				s->expr0->type = EXPR_LESS_EQUAL;
				break;
			default:
				assert(0);
			}
		}

		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			/*
			 * cmpb %1, %0
			 * jcc label
			 */
			/*FALLTHROUGH*/
		case TYPE_INT16:
		case TYPE_UINT16:
			/*
			 * cmpw %1, %0
			 * jcc label
			 */
			/*FALLTHROUGH*/
		case TYPE_INT32:
		case TYPE_UINT32:
			/*
			 * cmpl %1, %0
			 * jcc label
			 */
			/*FALLTHROUGH*/
		case TYPE_INT64:
		case TYPE_UINT64:
			/*
			 * cmpl %u1, %u0
			 * jcc label
			 * cmpl %1, %0
			 * jcc label
			 */
			constraint_input(s, "mr", expr_dup(s->expr0->expr0));
			constraint_input(s, "ir", expr_dup(s->expr0->expr1));
			break;
		case TYPE_FLOAT32:
			assert(0); /* FIXME */
			break;
		case TYPE_FLOAT64:
			assert(0); /* FIXME */
			break;
		case TYPE_FLOAT80:
			assert(0); /* FIXME */
			break;
		case TYPE_VA_LIST:
			assert(0); /* FIXME */
			break;
		default:
			assert(0);
		}
		break;

	case STMT_SWITCH:
		/*
		 * cmp{b,w,l} $c0, %0
		 * je l0
		 * cmp{b,w,l} $c1, %0
		 * je l1
		 * cmp{b,w,l} $c2, %0
		 * je l2
		 * ...
		 * cmp{b,w,l} $cN, %0
		 * je lN
		 * jmp ldef
		 */
		constraint_input(s, "r", expr_dup(s->expr0));
		break;

	case STMT_GOTO:
		/*
		 * jmp label
		 */
		/* No constraints needed. */
		break;

	case STMT_RETURN:
		/*
		 * No code. Constraint only.
		 */
		if (s->expr0) {
			t = expr_typeof(block->scope, s->expr0);
			t = type_pure(t);

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
			case TYPE_INT16:
			case TYPE_UINT16:
			case TYPE_INT32:
			case TYPE_UINT32:
				constraint_input(s, "a", expr_dup(s->expr0));
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				constraint_input(s, "A", expr_dup(s->expr0));
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0);
			default:
				assert(0);
			}
		}
		break;

	case STMT_ASM:
		/*
		 * Code in asm statement.
		 */
		/* Constraints in asm statement. */
		break;

	case STMT_VA_START:
		/*
		 * leal X(%ebp), %0
		 */
		constraint_output(s, "=r", expr_dup(s->expr0));
		break;

	case STMT_VA_END:
		/*
		 * No code. No constraints.
		 */
		break;

	default:
		assert(0);
	}
}

static void
arch_gen_stmt_simplify_2(
	struct stmt *block,
	struct stmt *s,
	unsigned int paramsize
)
{
	struct type *t;
	struct constraint *c;
	struct stmt *cs;
	struct stmt *s0;
	char buf0[1024*1024];
	char buf1[1024*1024];
	const char *from;
	char *to;
	struct declaration *dion;

	switch (s->type) {
	case STMT_NONE:
		assert(0);
	case STMT_NULL:
	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:
		assert(0);

	case STMT_LABEL:
		sprintf(buf0, "%s:\n", s->label->identifier);
		break;

	case STMT_EXPR:
		switch (s->expr0->type) {
		case EXPR_ASSIGN:
			switch (s->expr0->expr0->type) {
			case EXPR_IDENTIFIER:
				arch_gen_expr_2(buf0,
						block, s, s->expr0->expr1,
						s->expr0->expr0);
				break;
			case EXPR_STAR:
				t = expr_typeof(block->scope, s->expr0->expr1);
				t = type_pure(t);

				switch (t->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
					strcpy(buf0, "\tmovb %1, (%0)\n");
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
					strcpy(buf0, "\tmovw %1, (%0)\n");
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					strcpy(buf0, "\tmovl %1, (%0)\n");
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					strcpy(buf0, "\tmovl %1, (%0)\n");
					strcat(buf0, "\tmovl %u1, 4(%0)\n");
					break;
				case TYPE_FLOAT32:
					switch (s->input->first->next->expr->declaration->storage_register) {
					case ST0:
					case ST1:
						strcpy(buf0, "\tfstps (%1)\n");
						break;
					case ST2:
					case ST3:
					case ST4:
					case ST5:
					case ST6:
					case ST7:
						strcpy(buf0, "\tfldt %2\n"
								"\tfstps (%1)\n");
						break;
					default:
						/* Memory */
						strcpy(buf0, "\tmovl %2, %0\n"
								"\tmovl %0, (%1)\n");
						break;
					}
					break;
				case TYPE_FLOAT64:
					switch (s->input->first->next->expr->declaration->storage_register) {
					case ST0:
					case ST1:
						strcpy(buf0, "\tfstpl (%1)\n");
						break;
					case ST2:
					case ST3:
					case ST4:
					case ST5:
					case ST6:
					case ST7:
						strcpy(buf0, "\tfldt %2\n"
								"\tfstpl (%1)\n");
						break;
					default:
						/* Memory */
						strcpy(buf0, "\tmovl %2, %0\n"
								"\tmovl %0, (%1)\n"
								"\tmovl 4+%2, %0\n"
								"\tmovl %0, 4(%1)\n");
						break;
					}
					break;
				case TYPE_FLOAT80:
					switch (s->input->first->next->expr->declaration->storage_register) {
					case ST0:
					case ST1:
						strcpy(buf0, "\tfstpt (%1)\n");
						break;
					case ST2:
					case ST3:
					case ST4:
					case ST5:
					case ST6:
					case ST7:
						strcpy(buf0, "\tfldt %2\n"
								"\tfstpt (%1)\n");
						break;
					default:
						/* Memory */
						strcpy(buf0, "\tmovl %2, %0\n"
								"\tmovl %0, (%1)\n"
								"\tmovl 4+%2, %0\n"
								"\tmovl %0, 4(%1)\n"
								"\tmovl 8+%2, %0\n"
								"\tmovl %0, 8(%1)\n"
								"\tmovl 12+%2, %0\n"
								"\tmovl %0, 12(%1)\n");
						break;
					}
					break;
				default:
					assert(0);
				}
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_FUNC:
			arch_gen_expr_2(buf0, block, s, s->expr0, NULL);
			break;

		default:
			assert(0);
		}
		break;

	case STMT_IF:
		t = expr_typeof(block->scope, s->expr0->expr0);
		t = type_pure(t);

		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
		case TYPE_INT16:
		case TYPE_UINT16:
		case TYPE_INT32:
		case TYPE_UINT32:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcpy(buf0, "\tcmpb %1, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tcmpw %1, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tcmpl %1, %0\n");
				break;
			default:
				assert(0);
			}

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_INT16:
			case TYPE_INT32:
				switch (s->expr0->type) {
				case EXPR_EQUAL:
					sprintf(buf0 + strlen(buf0), "\tje %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_NOT_EQUAL:
					sprintf(buf0 + strlen(buf0), "\tjne %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_LESS:
					sprintf(buf0 + strlen(buf0), "\tjl %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_LESS_EQUAL:
					sprintf(buf0 + strlen(buf0), "\tjle %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_GREATER:
					sprintf(buf0 + strlen(buf0), "\tjg %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_GREATER_EQUAL:
					sprintf(buf0 + strlen(buf0), "\tjge %s\n",
							s->stmt0->label->identifier);
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_UINT8:
			case TYPE_UINT16:
			case TYPE_UINT32:
				switch (s->expr0->type) {
				case EXPR_EQUAL:
					sprintf(buf0 + strlen(buf0), "\tje %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_NOT_EQUAL:
					sprintf(buf0 + strlen(buf0), "\tjne %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_LESS:
					sprintf(buf0 + strlen(buf0), "\tjb %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_LESS_EQUAL:
					sprintf(buf0 + strlen(buf0), "\tjbe %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_GREATER:
					sprintf(buf0 + strlen(buf0), "\tja %s\n",
							s->stmt0->label->identifier);
					break;
				case EXPR_GREATER_EQUAL:
					sprintf(buf0 + strlen(buf0), "\tjae %s\n",
							s->stmt0->label->identifier);
					break;
				default:
					assert(0);
				}
				break;
			default:
				assert(0);
			}
			break;
		case TYPE_INT64:
			switch (s->expr0->type) {
			case EXPR_EQUAL:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tje %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_NOT_EQUAL:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				break;
			case EXPR_LESS:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_LESS_EQUAL:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjle %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_GREATER:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_GREATER_EQUAL:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjge %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			default:
				assert(0);
			}
			break;
		case TYPE_UINT64:
			switch (s->expr0->type) {
			case EXPR_EQUAL:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tje %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_NOT_EQUAL:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				break;
			case EXPR_LESS:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_LESS_EQUAL:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_GREATER:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_GREATER_EQUAL:
				strcpy(buf0, "\tcmpl %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpl %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjae %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			default:
				assert(0);
			}
			break;
		case TYPE_FLOAT32:
			assert(0); /* FIXME */
			break;
		case TYPE_FLOAT64:
			assert(0); /* FIXME */
			break;
		case TYPE_FLOAT80:
			assert(0); /* FIXME */
			break;
		default:
			assert(0);
		}
		break;

	case STMT_SWITCH:
		assert(s->expr0->type == EXPR_IDENTIFIER);

		t = expr_typeof(block->scope, s->expr0);
		t = type_pure(t);

		strcpy(buf0, "");
		for (cs = s->stmt0->stmt_first;
		    cs->type != STMT_DEFAULT;
		    cs = cs->next) {
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				sprintf(buf0 + strlen(buf0),
						"\tcmpb $%lld, %%0\n",
						cs->expr0->integer);
				sprintf(buf0 + strlen(buf0),
						"\tje %s\n",
						cs->stmt0->label->identifier);
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				sprintf(buf0 + strlen(buf0),
						"\tcmpw $%lld, %%0\n",
						cs->expr0->integer);
				sprintf(buf0 + strlen(buf0),
						"\tje %s\n",
						cs->stmt0->label->identifier);
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				sprintf(buf0 + strlen(buf0),
						"\tcmpl $%lld, %%0\n",
						cs->expr0->integer);
				sprintf(buf0 + strlen(buf0),
						"\tje %s\n",
						cs->stmt0->label->identifier);
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				sprintf(buf0 + strlen(buf0),
						"\tcmpl $%u, %%0\n",
						(uint32_t) (cs->expr0->integer >> 0));
				strcat(buf0, "\tjne 1f\n");
				sprintf(buf0 + strlen(buf0),
						"\tcmpl $%u, %%u0\n",
						(uint32_t) (cs->expr0->integer >> 32));
				sprintf(buf0 + strlen(buf0),
						"\tje %s\n",
						cs->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			default:
				assert(0);
			}
		}
		sprintf(buf0 + strlen(buf0), "\tjmp %s\n",
				cs->stmt0->label->identifier);
		break;

	case STMT_GOTO:
		sprintf(buf0, "\tjmp %s\n", s->label->identifier);
		break;

	case STMT_RETURN:
		/*
		 * No code. Asm constraints only.
		 */
		strcpy(buf0, "");
		break;

	case STMT_ASM:
		strcpy(buf0, s->code);
		if (0 < strlen(buf0)
		 && buf0[strlen(buf0) - 1] != '\n') {
			/* Newline is missing... */
			strcat(buf0, "\n");
		}
		break;

	case STMT_VA_START:
		sprintf(buf0, "\tleal %u(%%%%ebp), %%0\n", paramsize);
		break;

	case STMT_VA_END:
		strcpy(buf0, "");
		break;

	default:
		assert(0);
	}

	/*
	 * Replace %0, %1, ... in asm statements.
	 */
	from = buf0;
	to = buf1;
	while (*from) {
		enum type_mod mod;
		unsigned int n;
		unsigned int offset;
		const char *reg;
		unsigned long long val;

		if (*from != '%') {
			*to++ = *from++;
			continue;
		}
		from++;
		if (*from == '%') {
			from++;
			*to++ = '%';
			continue;
		}

		/*
		 * Get byte/word/long modifier.
		 */
		switch (*from) {
		case 'b':
			mod = REG_MOD_b;
			offset = 0;
			from++;
			break;
		case 'h':
			mod = REG_MOD_h;
			offset = 1;
			from++;
			break;
		case 'w':
			mod = REG_MOD_w;
			offset = 0;
			from++;
			break;
		case 'u':
			mod = REG_MOD_u;
			offset = 4;
			from++;
			break;
		default:
			mod = REG_MOD_NONE;
			offset = 0;
			break;
		}

		/*
		 * Get constraint number.
		 */
		if (*from < '0'
		 || '9' < *from) {
			fprintf(stderr, "No constraint number.\n");
			goto problem;
		}
		assert('0' <= *from && *from <= '9');

		n = 0;
		while ('0' <= *from && *from <= '9') {
			n *= 10;
			n += *from++ - '0';
		}
		if (100 <= n) {
			fprintf(stderr, "Constraint number >= 100.\n");
			goto problem;
		}
		assert(n < 100);

		/*
		 * Lookup constraint.
		 */
		for (c = s->output->first; c && n; c = c->next) {
			n--;
		}
		if (! c) {
			for (c = s->input->first; c && n; c = c->next) {
				n--;
			}
		}
		if (! c) {
			fprintf(stderr, "Too few constraints.\n");
			goto problem;
		}
		assert(c);

		/*
		 * Replace "%num" by real operand.
		 */
		switch (c->expr->type) {
		case EXPR_INTEGER:
			switch (c->expr->type_name->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				val = (uint8_t) c->expr->integer;
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				val = (uint16_t) c->expr->integer;
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				val = (uint32_t) c->expr->integer;
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				val = c->expr->integer;
				break;
			default:
				assert(0);
			}
			switch (mod) {
			case REG_MOD_NONE:
				val &= 0xffffffff;
				break;
			case REG_MOD_b:
				val &= 0xff;
				break;
			case REG_MOD_h:
				val >>= 8;
				val &= 0xff;
				break;
			case REG_MOD_w:
				val &= 0xffff;
				break;
			case REG_MOD_u:
				val >>= 32;
				val &= 0xffffffff;
				break;
			default:
				assert(0);
			}
			sprintf(to, "$0x%llx", val);
			to += strlen(to);
			break;

		case EXPR_REAL:
			assert(0);

		case EXPR_IDENTIFIER:
			dion = c->expr->declaration;
			assert(dion);

			switch (dion->storage) {
			case STORAGE_AUTO:
				sprintf(to, "%d(%%ebp)",
						dion->offset + offset);
				to += strlen(to);
				break;

			case STORAGE_REGISTER:
				switch (mod) {
				case REG_MOD_NONE:
					reg = arch_reg_name_def[dion->storage_register];
					break;
				case REG_MOD_b:
					reg = arch_reg_name_b[dion->storage_register];
					break;
				case REG_MOD_h:
					reg = arch_reg_name_h[dion->storage_register];
					break;
				case REG_MOD_w:
					reg = arch_reg_name_w[dion->storage_register];
					break;
				case REG_MOD_u:
					reg = arch_reg_name_u[dion->storage_register];
					break;
				default:
					assert(0); /* Mustn't happen. */
				}
				if (! reg) {
					fprintf(stderr, "mod=%u, reg=%u\n",
							mod, dion->storage_register);
					goto problem;
				}
				assert(reg);

				sprintf(to, "%%%s", reg);
				to += strlen(to);
				break;

			case STORAGE_PARAM:
				sprintf(to, "%u(%%bp)",
						dion->offset + offset);
				to += strlen(to);
				break;

			case STORAGE_STATIC:
				if (offset == 0) {
					sprintf(to, "%s", arch_gen_local_off(dion->identifier));
					to += strlen(to);
				} else { assert(offset == 2);
					sprintf(to, "%s", arch_gen_local_seg(dion->identifier));
					to += strlen(to);
				}
				break;

			case STORAGE_NONE:
			case STORAGE_EXTERN:
				if (offset == 0) {
					sprintf(to, "%s", arch_gen_global_off(dion->identifier));
					to += strlen(to);
				} else { assert(offset == 2);
					sprintf(to, "%s", arch_gen_global_seg(dion->identifier));
					to += strlen(to);
				}
				break;

			case STORAGE_TYPEDEF:
			case STORAGE_ASM:
				assert(0);
			}
			break;

		case EXPR_AMPHERSAND:
			assert(0);

		case EXPR_TYPE_CONVERSION:
			if (c->expr->expr0->type == EXPR_AMPHERSAND) {
				/*
				 * Special case: (int) &var
				 */
				dion = c->expr->expr0->expr0->declaration;
			} else if (c->expr->expr0->type == EXPR_IDENTIFIER) {
				/*
				 * Special case: (int) array
				 * Special case: (int) func
				 */
				dion = c->expr->expr0->declaration;
			} else {
				assert(0);
			}
			assert(dion);

			if (dion->storage == STORAGE_STATIC) {
				if (offset == 0) {
					sprintf(to, "$%s",
							arch_gen_local_off(dion->identifier));
				} else { assert(offset == 4);
					sprintf(to, "$%s",
							arch_gen_local_seg(dion->identifier));
				}
			} else if (dion->storage == STORAGE_NONE
				|| dion->storage == STORAGE_EXTERN) {
				if (offset == 0) {
					sprintf(to, "$%s",
							arch_gen_global_off(dion->identifier));
				} else { assert(offset == 4);
					sprintf(to, "$%s",
							arch_gen_global_seg(dion->identifier));
				}
			} else if (dion->storage == STORAGE_AUTO) {
				sprintf(to, "%d(%%ebp)",
						dion->offset + offset);
			} else if (dion->storage == STORAGE_PARAM) {
				sprintf(to, "%d(%%ebp)",
						dion->offset + offset);
			}
			to += strlen(to);
			break;

		default:
		problem:
			fprintf(stderr, "Problem with:\n");
			fprintf(stderr, "%s", buf0);
			assert(0);
		}
	}
	*to = '\0';

	s0 = stmt_new();
	s0->type = STMT_ASM;
	s0->code = identifier_new(buf1);
	s0->code_len = strlen(s0->code);

	s0->output = constraint_list_new();
	s0->input = constraint_list_new();
	s0->change = constraint_list_new();

	stmt_replace_1_by_1(block, s, s0);
}

static void
arch_gen_func(FILE *fp, struct declaration *dion)
{
	struct declaration *save[REG_COUNT];
	struct declaration *d;
	struct stmt *cs;
	int offset;
	unsigned int align;
	unsigned int size;
	unsigned int autosize;
	unsigned int paramsize;

	/*
	 * Check "dion->stmt->stmt_last->type == STMT_RETURN" should
	 * not be neccessary - FIXME.
	 */
	if (! dion->attr_noreturn
	 && dion->stmt->stmt_last->type == STMT_RETURN) {
		unsigned int reg;
		uint64_t regs_to_save;

		assert(dion->stmt->stmt_last->type == STMT_RETURN);

		/* Save Registers */
		regs_to_save = REG_CALLEE;

		assert((regs_to_save & ~REG_32) == 0);
		cs = stmt_new();
		cs->type = STMT_ASM;
		cs->code = identifier_new("");
		cs->code_len = 0;
		for (reg = 0; reg < REG_COUNT; reg++) {
			const char *constraint;

			if (! ((regs_to_save >> reg) & 1)) {
				save[reg] = NULL;
				continue;
			}
			save[reg] = simplify_declaration_add(dion->stmt->scope,
					type_uint32(), identifier_tmp());
			save[reg]->storage = STORAGE_AUTO;
			switch (reg) {
			case EAX: constraint = "=a"; break;
			case EBX: constraint = "=b"; break;
			case ECX: constraint = "=c"; break;
			case EDX: constraint = "=d"; break;
			case EDI: constraint = "=D"; break;
			case ESI: constraint = "=S"; break;
			default: assert(0); /* Mustn't happen. */
			}
			constraint_output(cs, constraint,
					expr_identifier(save[reg]));
		}
		stmt_prepend_first(dion->stmt, cs);

		/* Restore Registers */
		cs = dion->stmt->stmt_last;
		for (reg = 0; reg < REG_COUNT; reg++) {
			const char *constraint;

			if (! ((regs_to_save >> reg) & 1)) {
				assert(save[reg] == NULL);
				continue;
			}
			switch (reg) {
			case EAX: constraint = "a"; break;
			case EBX: constraint = "b"; break;
			case ECX: constraint = "c"; break;
			case EDX: constraint = "d"; break;
			case EDI: constraint = "D"; break;
			case ESI: constraint = "S"; break;
			default: assert(0); /* Mustn't happen. */
			}
			constraint_input(cs, constraint,
					expr_identifier(save[reg]));
		}
	}

	/*
	 * Transform statements into assembler code.
	 * First step.
	 */
	for (cs = dion->stmt->stmt_first; cs; ) {
		struct stmt *next;

		next = cs->next;

		arch_gen_stmt_simplify_1(dion->stmt, cs);

		cs = next;
	}

	/*
	 * Add missing constraint info.
	 */
	for (cs = dion->stmt->stmt_first; cs; cs = cs->next) {
		if (! cs->output) {
			cs->output = constraint_list_new();
		}
		if (! cs->input) {
			cs->input = constraint_list_new();
		}
		if (! cs->change) {
			cs->change = constraint_list_new();
		}
	}
#if 0
	print_declaration(stderr, 0, dion);
#endif

	/*
	 * Register assignment.
	 */
	regalloc(arch_reginfo, arch_classinfo,
			arch_typeinfo, dion->stmt);
#if 0
	print_declaration(stderr, 0, dion);
#endif

	offset = 0;
	for (d = dion->stmt->scope->declaration_first;
	    d;
	    d = d->next) {
		if (d->storage == STORAGE_AUTO) {
			type_align_size(dion->stmt->scope, d->type_name,
					&align, &size);
			offset -= size;
			offset &= ~(align - 1);
			d->offset = offset;
		}
	}

	autosize = offset;
	autosize &= ~(4 - 1); /* 4 Byte Alignment */
	autosize = -autosize;

	offset = 4; /* Base Pointer */
	offset += 4; /* Return Address */
	for (d = dion->type_name->parameter->declaration_first;
	    d && d->type_name->type != TYPE_ELIPSIS;
	    d = d->next) {
		type_align_size(dion->stmt->scope, d->type_name,
				&align, &size);

		offset += align - 1;
		offset &= ~(align - 1);
		d->offset = offset;
		offset += size;
	}

	paramsize = offset;
	paramsize += 4 - 1; /* 4 Byte Alignment */
	paramsize &= ~(4 - 1);

	/*
	 * Transform remaining statements into assembler code.
	 * Second step.
	 */
	for (cs = dion->stmt->stmt_first; cs; ) {
		struct stmt *next;

		next = cs->next;

		arch_gen_stmt_simplify_2(dion->stmt, cs, paramsize);

		cs = next;
	}

	/*
	 * Generate Header
	 */
	fprintf(fp, "\t.text\n");
	fprintf(fp, "\t.code32\n");
	fprintf(fp, "\t.arch i386\n");
	if (opt_f_align_functions != 1) {
		unsigned int p;

		for (p = 0; (1 << p) < opt_f_align_functions; p++) {
		}
		fprintf(fp, "\t.p2align %u\n", p);
	}
	if (dion->storage == STORAGE_STATIC) {
		/*
		 * NOTE:
		 * Label *must* be global. Otherwise our
		 * linker tool will not generate __SEG_x/__OFF_x
		 * symbols for it.
		 */
		fprintf(fp, "\t.globl %s\n", arch_gen_local(dion->identifier));

		fprintf(fp, "%s:\n", arch_gen_local(dion->identifier));
	} else {
		fprintf(fp, "\t.globl %s\n", arch_gen_global(dion->identifier));
		fprintf(fp, "%s:\n", arch_gen_global(dion->identifier));
	}

	fprintf(fp, "\tpushl %%ebp\n");
	fprintf(fp, "\tmovl %%esp, %%ebp\n");
	if (0 < autosize) {
		fprintf(fp, "\tsubl $%u, %%esp\n", autosize);
	}

	/*
	 * Generate Code
	 */
	for (cs = dion->stmt->stmt_first; cs; cs = cs->next) {
		if (cs->type == STMT_ASM) {
			fprintf(fp, "%s", cs->code);
		} else {
			fprintf(fp, "...\n");
		}
	}

	/*
	 * Generate Trailer
	 */
	fprintf(fp, "\tleave\n");
	fprintf(fp, "\tret\n");
}

static void
arch_gen_dion(FILE *fp, struct scope *scope, struct declaration *dion)
{
	if (dion->storage == STORAGE_TYPEDEF) {
		/* Type definition only. */
		return;
	}
	if (dion->storage != STORAGE_ASM
	 && dion->identifier == NULL) {
		/* struct/union/enum definition only. */
		return;
	}
	if (dion->storage == STORAGE_EXTERN) {
		/* Extern declaration only. */
		return;
	}
	if ((dion->storage == STORAGE_NONE
	  || dion->storage == STORAGE_EXTERN
	  || dion->storage == STORAGE_STATIC)
	 && type_is_function(dion->type_name)
	 && ! dion->stmt) {
		/* Function prototype only. */
		return;
	}

	if (dion->storage == STORAGE_ASM) {
		fprintf(fp, "%s\n", dion->code);
	} else {
		if (dion->storage == STORAGE_STATIC) {
			arch_gen_local_def(dion->identifier);
		} else {
			arch_gen_global_def(dion->identifier);
		}
		if (! type_is_function(dion->type_name)) {
			arch_gen_data(fp, scope, dion);
		} else {
			arch_gen_func(fp, dion);
		}
	}
	fprintf(fp, "\n");
}

void
arch_i386_gen(const char *out, struct scope *scope)
{
	FILE *fp;
	struct declaration *dion;
	int ret;

	fp = fopen(out, "w");
	assert(fp);

	assert(REG_COUNT <= DECL_REG_COUNT);
	assert(CLASS_NONE == DECL_CLASS_NONE);
	assert(CLASS_COUNT <= DECL_CLASS_COUNT);

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

	arch_mul_int64 = declaration_identifier("__mul_int64");
	arch_mul_int64->type_name = type_new();
	arch_mul_int64->type_name->type = TYPE_FUNCTION;
	arch_mul_int64->type_name->declarator = type_new();
	arch_mul_int64->type_name->declarator->type = TYPE_INT64;

	arch_mul_uint64 = declaration_identifier("__i386_mul_uint64");
	arch_mul_uint64->type_name = type_new();
	arch_mul_uint64->type_name->type = TYPE_FUNCTION;
	arch_mul_uint64->type_name->declarator = type_new();
	arch_mul_uint64->type_name->declarator->type = TYPE_UINT64;

	arch_div_int64 = declaration_identifier("__i386_div_int64");
	arch_div_int64->type_name = type_new();
	arch_div_int64->type_name->type = TYPE_FUNCTION;
	arch_div_int64->type_name->declarator = type_new();
	arch_div_int64->type_name->declarator->type = TYPE_INT64;

	arch_div_uint64 = declaration_identifier("__i386_div_uint64");
	arch_div_uint64->type_name = type_new();
	arch_div_uint64->type_name->type = TYPE_FUNCTION;
	arch_div_uint64->type_name->declarator = type_new();
	arch_div_uint64->type_name->declarator->type = TYPE_UINT64;

	arch_mod_int64 = declaration_identifier("__i386_mod_int64");
	arch_mod_int64->type_name = type_new();
	arch_mod_int64->type_name->type = TYPE_FUNCTION;
	arch_mod_int64->type_name->declarator = type_new();
	arch_mod_int64->type_name->declarator->type = TYPE_INT64;

	arch_mod_uint64 = declaration_identifier("__i386_mod_uint64");
	arch_mod_uint64->type_name = type_new();
	arch_mod_uint64->type_name->type = TYPE_FUNCTION;
	arch_mod_uint64->type_name->declarator = type_new();
	arch_mod_uint64->type_name->declarator->type = TYPE_UINT64;

	for (dion = scope->declaration_first; dion; dion = dion->next) {
#if 0
		print_declaration(stderr, 0, dion);
#endif
		arch_gen_dion(fp, scope, dion);
#if 0
		print_declaration(stderr, 0, dion);
#endif
	}

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