/*
 * $Id: arch_i286_gen.c,v 1.348 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_i286_gen.h"
#include "regalloc.h"
#include "cc1.h"

static struct declaration *arch_mul_int32;
static struct declaration *arch_mul_uint32;
static struct declaration *arch_div_int32;
static struct declaration *arch_div_uint32;
static struct declaration *arch_mod_int32;
static struct declaration *arch_mod_uint32;
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,
/*12*/	DI, SI,
/*14*/	AX_BX, AX_CX, AX_DX, AX_DI, AX_SI,
/*19*/	BX_AX, BX_CX, BX_DX, BX_DI, BX_SI,
/*24*/	CX_AX, CX_BX, CX_DX, CX_DI, CX_SI,
/*29*/	DX_AX, DX_BX, DX_CX, DX_DI, DX_SI,
/*34*/	DI_AX, DI_BX, DI_CX, DI_DX, DI_SI,
/*39*/	SI_AX, SI_BX, SI_CX, SI_DX, SI_DI,
/*44*/	AX_DX_DI_SI,

/*45*/	REG_COUNT
};

enum class {
	CLASS_NONE,

	CLASS_A,
	CLASS_D,
	CLASS_S,
	CLASS_a,
	CLASS_b,
	CLASS_c,
	CLASS_d,
	CLASS_q,
	CLASS_r,
	CLASS_l,

	CLASS_COUNT
};

enum type_mod {
	REG_MOD_NONE,

	REG_MOD_b,
	REG_MOD_h,
	REG_MOD_w,
	REG_MOD_u,
	REG_MOD_v,
};

#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 << AX_BX) | (1ULL << AX_CX) \
		| (1ULL << AX_DX) | (1ULL << AX_DI) | (1ULL << AX_SI) \
		| (1ULL << BX_AX) | (1ULL << BX_CX) | (1ULL << BX_DX) \
		| (1ULL << BX_DI) | (1ULL << BX_SI) \
		| (1ULL << CX_AX) | (1ULL << CX_BX) | (1ULL << CX_DX) \
		| (1ULL << CX_DI) | (1ULL << CX_SI) \
		| (1ULL << DX_AX) | (1ULL << DX_BX) | (1ULL << DX_CX) \
		| (1ULL << DX_DI) | (1ULL << DX_SI) \
		| (1ULL << DI_AX) | (1ULL << DI_BX) | (1ULL << DI_CX) \
		| (1ULL << DI_DX) | (1ULL << DI_SI) \
		| (1ULL << SI_AX) | (1ULL << SI_BX) | (1ULL << SI_CX) \
		| (1ULL << SI_DX) | (1ULL << SI_DI))
#define REG_64	\
		(1ULL << AX_DX_DI_SI)

#define REG_A	\
		(1ULL << AL) \
		| (1ULL << AX) \
		| (1ULL << AX_DX) \
		| (1ULL << AX_DX_DI_SI)
#define REG_D	\
		((1ULL << DI) \
		| (1ULL << DI_AX) | (1ULL << DI_BX) \
		| (1ULL << DI_CX) | (1ULL << DI_DX) \
		| (1ULL << DI_SI))
#define REG_S	\
		((1ULL << SI) \
		| (1ULL << SI_AX) | (1ULL << SI_BX) \
		| (1ULL << SI_CX) | (1ULL << SI_DX) \
		| (1ULL << SI_DI))
#define REG_a	\
		((1ULL << AL) | (1ULL << AX) \
		| (1ULL << AX_BX) | (1ULL << AX_CX) \
		| (1ULL << AX_DX) | (1ULL << AX_DI) \
		| (1ULL << AX_SI))
#define REG_b	\
		((1ULL << BL) | (1ULL << BX) \
		| (1ULL << BX_AX) | (1ULL << BX_CX) \
		| (1ULL << BX_DX) | (1ULL << BX_DI) \
		| (1ULL << BX_SI))
#define REG_c	\
		((1ULL << CL) | (1ULL << CX) \
		| (1ULL << CX_AX) | (1ULL << CX_BX) \
		| (1ULL << CX_DX) | (1ULL << CX_DI) \
		| (1ULL << CX_SI))
#define REG_d	\
		((1ULL << DL) | (1ULL << DX) \
		| (1ULL << DX_AX) | (1ULL << DX_BX) \
		| (1ULL << DX_CX) | (1ULL << DX_DI) \
		| (1ULL << DX_SI))

#define REG_q	\
		(REG_a | REG_b | REG_c | REG_d)
#define REG_r	\
		(REG_8 | REG_16 | REG_32 | REG_64)
#define REG_l	\
		((1ULL << BX) | (1ULL << DI) | (1ULL << SI) | (1ULL << BX_DI) \
		| (1ULL << BX_SI) | (1ULL << BX_AX) | (1ULL << BX_CX) \
		| (1ULL << BX_DX))

#define REG_CALLER	\
		((1ULL << AX) \
		| (1ULL << CX) \
		| (1ULL << DX))
#define REG_CALLEE	\
		(REG_16 & ~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 */
	[AX_BX ]= { "ax_bx", CLASS_COUNT, TYPE_UINT32 },
	[AX_CX ]= { "ax_cx", CLASS_COUNT, TYPE_UINT32 },
	[AX_DX ]= { "ax_dx", CLASS_COUNT, TYPE_UINT32 },
	[AX_DI ]= { "ax_di", CLASS_COUNT, TYPE_UINT32 },
	[AX_SI ]= { "ax_si", CLASS_COUNT, TYPE_UINT32 },

	[BX_AX ]= { "bx_ax", CLASS_COUNT, TYPE_UINT32 },
	[BX_CX ]= { "bx_cx", CLASS_COUNT, TYPE_UINT32 },
	[BX_DX ]= { "bx_dx", CLASS_COUNT, TYPE_UINT32 },
	[BX_DI ]= { "bx_di", CLASS_COUNT, TYPE_UINT32 },
	[BX_SI ]= { "bx_si", CLASS_COUNT, TYPE_UINT32 },

	[CX_AX ]= { "cx_ax", CLASS_COUNT, TYPE_UINT32 },
	[CX_BX ]= { "cx_bx", CLASS_COUNT, TYPE_UINT32 },
	[CX_DX ]= { "cx_dx", CLASS_COUNT, TYPE_UINT32 },
	[CX_DI ]= { "cx_di", CLASS_COUNT, TYPE_UINT32 },
	[CX_SI ]= { "cx_si", CLASS_COUNT, TYPE_UINT32 },

	[DX_AX ]= { "dx_ax", CLASS_COUNT, TYPE_UINT32 },
	[DX_BX ]= { "dx_bx", CLASS_COUNT, TYPE_UINT32 },
	[DX_CX ]= { "dx_cx", CLASS_COUNT, TYPE_UINT32 },
	[DX_DI ]= { "dx_di", CLASS_COUNT, TYPE_UINT32 },
	[DX_SI ]= { "dx_si", CLASS_COUNT, TYPE_UINT32 },

	[DI_AX ]= { "di_ax", CLASS_COUNT, TYPE_UINT32 },
	[DI_BX ]= { "di_bx", CLASS_COUNT, TYPE_UINT32 },
	[DI_CX ]= { "di_cx", CLASS_COUNT, TYPE_UINT32 },
	[DI_DX ]= { "di_dx", CLASS_COUNT, TYPE_UINT32 },
	[DI_SI ]= { "di_si", CLASS_COUNT, TYPE_UINT32 },

	[SI_AX ]= { "si_ax", CLASS_COUNT, TYPE_UINT32 },
	[SI_BX ]= { "si_bx", CLASS_COUNT, TYPE_UINT32 },
	[SI_CX ]= { "si_cx", CLASS_COUNT, TYPE_UINT32 },
	[SI_DX ]= { "si_dx", CLASS_COUNT, TYPE_UINT32 },
	[SI_DI ]= { "si_di", CLASS_COUNT, TYPE_UINT32 },

	/* 64-Bit Register */
	[AX_DX_DI_SI ]= { "ax_dx_di_si", CLASS_COUNT, TYPE_UINT64 },
};

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 */
	[AX_BX] = { 2, { AX, BX } },
	[AX_CX] = { 2, { AX, CX } },
	[AX_DX] = { 2, { AX, DX } },
	[AX_DI] = { 2, { AX, DI } },
	[AX_SI] = { 2, { AX, SI } },

	[BX_AX] = { 2, { BX, AX } },
	[BX_CX] = { 2, { BX, CX } },
	[BX_DX] = { 2, { BX, DX } },
	[BX_DI] = { 2, { BX, DI } },
	[BX_SI] = { 2, { BX, SI } },

	[CX_AX] = { 2, { CX, AX } },
	[CX_BX] = { 2, { CX, BX } },
	[CX_DX] = { 2, { CX, DX } },
	[CX_DI] = { 2, { CX, DI } },
	[CX_SI] = { 2, { CX, SI } },

	[DX_AX] = { 2, { DX, AX } },
	[DX_BX] = { 2, { DX, BX } },
	[DX_CX] = { 2, { DX, CX } },
	[DX_DI] = { 2, { DX, DI } },
	[DX_SI] = { 2, { DX, SI } },

	[DI_AX] = { 2, { DI, AX } },
	[DI_BX] = { 2, { DI, BX } },
	[DI_CX] = { 2, { DI, CX } },
	[DI_DX] = { 2, { DI, DX } },
	[DI_SI] = { 2, { DI, SI } },

	[SI_AX] = { 2, { SI, AX } },
	[SI_BX] = { 2, { SI, BX } },
	[SI_CX] = { 2, { SI, CX } },
	[SI_DX] = { 2, { SI, DX } },
	[SI_DI] = { 2, { SI, DI } },

	/* 64-Bit Register */
	[AX_DX_DI_SI] = { 4, { AX, DX, DI, SI } },
};

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,

	[AX_BX] = "al",
	[AX_CX] = "al",
	[AX_DX] = "al",
	[AX_DI] = "al",
	[AX_SI] = "al",

	[BX_AX] = "bl",
	[BX_CX] = "bl",
	[BX_DX] = "bl",
	[BX_DI] = "bl",
	[BX_SI] = "bl",

	[CX_AX] = "cl",
	[CX_BX] = "cl",
	[CX_DX] = "cl",
	[CX_DI] = "cl",
	[CX_SI] = "cl",

	[DX_AX] = "dl",
	[DX_BX] = "dl",
	[DX_CX] = "dl",
	[DX_DI] = "dl",
	[DX_SI] = "dl",

	[DI_AX] = NULL,
	[DI_BX] = NULL,
	[DI_CX] = NULL,
	[DI_DX] = NULL,
	[DI_SI] = NULL,

	[SI_AX] = NULL,
	[SI_BX] = NULL,
	[SI_CX] = NULL,
	[SI_DX] = NULL,
	[SI_DI] = NULL,

	[AX_DX_DI_SI] = "al",
};

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,

	[AX_BX] = "ah",
	[AX_CX] = "ah",
	[AX_DX] = "ah",
	[AX_DI] = "ah",
	[AX_SI] = "ah",

	[BX_AX] = "bh",
	[BX_CX] = "bh",
	[BX_DX] = "bh",
	[BX_DI] = "bh",
	[BX_SI] = "bh",

	[CX_AX] = "ch",
	[CX_BX] = "ch",
	[CX_DX] = "ch",
	[CX_DI] = "ch",
	[CX_SI] = "ch",

	[DX_AX] = "dh",
	[DX_BX] = "dh",
	[DX_CX] = "dh",
	[DX_DI] = "dh",
	[DX_SI] = "dh",

	[DI_AX] = NULL,
	[DI_BX] = NULL,
	[DI_CX] = NULL,
	[DI_DX] = NULL,
	[DI_SI] = NULL,

	[SI_AX] = NULL,
	[SI_BX] = NULL,
	[SI_CX] = NULL,
	[SI_DX] = NULL,
	[SI_DI] = NULL,

	[AX_DX_DI_SI] = "ah",
};

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,

	[AX_BX] = "bx",
	[AX_CX] = "cx",
	[AX_DX] = "dx",
	[AX_DI] = "di",
	[AX_SI] = "si",

	[BX_AX] = "ax",
	[BX_CX] = "cx",
	[BX_DX] = "dx",
	[BX_DI] = "di",
	[BX_SI] = "si",

	[CX_AX] = "ax",
	[CX_BX] = "bx",
	[CX_DX] = "dx",
	[CX_DI] = "di",
	[CX_SI] = "si",

	[DX_AX] = "ax",
	[DX_BX] = "bx",
	[DX_CX] = "cx",
	[DX_DI] = "di",
	[DX_SI] = "si",

	[DI_AX] = "ax",
	[DI_BX] = "bx",
	[DI_CX] = "cx",
	[DI_DX] = "dx",
	[DI_SI] = "si",

	[SI_AX] = "ax",
	[SI_BX] = "bx",
	[SI_CX] = "cx",
	[SI_DX] = "dx",
	[SI_DI] = "di",

	[AX_DX_DI_SI] = "dx",
};

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,

	[AX_BX] = NULL,
	[AX_CX] = NULL,
	[AX_DX] = NULL,
	[AX_DI] = NULL,
	[AX_SI] = NULL,

	[BX_AX] = NULL,
	[BX_CX] = NULL,
	[BX_DX] = NULL,
	[BX_DI] = NULL,
	[BX_SI] = NULL,

	[CX_AX] = NULL,
	[CX_BX] = NULL,
	[CX_DX] = NULL,
	[CX_DI] = NULL,
	[CX_SI] = NULL,

	[DX_AX] = NULL,
	[DX_BX] = NULL,
	[DX_CX] = NULL,
	[DX_DI] = NULL,
	[DX_SI] = NULL,

	[DI_AX] = NULL,
	[DI_BX] = NULL,
	[DI_CX] = NULL,
	[DI_DX] = NULL,
	[DI_SI] = NULL,

	[SI_AX] = NULL,
	[SI_BX] = NULL,
	[SI_CX] = NULL,
	[SI_DX] = NULL,
	[SI_DI] = NULL,

	[AX_DX_DI_SI] = "di",
};

static const char *arch_reg_name_v[] = {
	[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,

	[AX_BX] = NULL,
	[AX_CX] = NULL,
	[AX_DX] = NULL,
	[AX_DI] = NULL,
	[AX_SI] = NULL,

	[BX_AX] = NULL,
	[BX_CX] = NULL,
	[BX_DX] = NULL,
	[BX_DI] = NULL,
	[BX_SI] = NULL,

	[CX_AX] = NULL,
	[CX_BX] = NULL,
	[CX_DX] = NULL,
	[CX_DI] = NULL,
	[CX_SI] = NULL,

	[DX_AX] = NULL,
	[DX_BX] = NULL,
	[DX_CX] = NULL,
	[DX_DI] = NULL,
	[DX_SI] = NULL,

	[DI_AX] = NULL,
	[DI_BX] = NULL,
	[DI_CX] = NULL,
	[DI_DX] = NULL,
	[DI_SI] = NULL,

	[SI_AX] = NULL,
	[SI_BX] = NULL,
	[SI_CX] = NULL,
	[SI_DX] = NULL,
	[SI_DI] = NULL,

	[AX_DX_DI_SI] = "si",
};

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",

	[AX_BX] = "ax",
	[AX_CX] = "ax",
	[AX_DX] = "ax",
	[AX_DI] = "ax",
	[AX_SI] = "ax",

	[BX_AX] = "bx",
	[BX_CX] = "bx",
	[BX_DX] = "bx",
	[BX_DI] = "bx",
	[BX_SI] = "bx",

	[CX_AX] = "cx",
	[CX_BX] = "cx",
	[CX_DX] = "cx",
	[CX_DI] = "cx",
	[CX_SI] = "cx",

	[DX_AX] = "dx",
	[DX_BX] = "dx",
	[DX_CX] = "dx",
	[DX_DI] = "dx",
	[DX_SI] = "dx",

	[DI_AX] = "di",
	[DI_BX] = "di",
	[DI_CX] = "di",
	[DI_DX] = "di",
	[DI_SI] = "di",

	[SI_AX] = "si",
	[SI_BX] = "si",
	[SI_CX] = "si",
	[SI_DX] = "si",
	[SI_DI] = "si",

	[AX_DX_DI_SI] = "ax",
};

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,
	['l'] = CLASS_l,	/* indirect addressing */
	['q'] = CLASS_q,
	['r'] = CLASS_r,
};

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_NONE,
	[TYPE_FLOAT64] = CLASS_NONE,
	[TYPE_FLOAT80] = CLASS_NONE,

	[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 opt_f_segment_enable;
}

struct type *
arch_i286_type_intptr_t(void)
{
	if (seg_enabled()) {
		return type_int32();
	} else {
		return type_int16();
	}
}

struct type *
arch_i286_type_uintptr_t(void)
{
	if (seg_enabled()) {
		return type_uint32();
	} else {
		return type_uint16();
	}
}

void
arch_i286_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 = 2;
		*sizep = 4;
		break;

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

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

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

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

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

void
arch_i286_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_i286_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_i286_gen_class_and(unsigned int a, unsigned int b)
{
	unsigned int class;

        if (a == CLASS_r) {
                class = b;
        } else if (b == CLASS_r) {
                class = a;
        } else if (a == CLASS_q) {
                class = b;
        } else if (b == CLASS_q) {
                class = a;
        } else if (a == b) {
                class = a;
        } else {
                class = CLASS_NONE;
        }

	return class;
}

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

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

void
arch_i286_color_add(unsigned int *count, unsigned int class, enum type_type type)
{
	switch (class) {
	case CLASS_NONE:
		break;
	case CLASS_A:
		switch (type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			count[CLASS_a]++;
			break;
		case TYPE_INT16:
		case TYPE_UINT16:
			count[CLASS_a]++;
			break;
		case TYPE_INT32:
		case TYPE_UINT32:
			count[CLASS_a]++;
			count[CLASS_d]++;
			break;
		case TYPE_INT64:
		case TYPE_UINT64:
			count[CLASS_a]++;
			count[CLASS_d]++;
			count[CLASS_D]++;
			count[CLASS_S]++;
			break;
		case TYPE_VA_LIST:
			count[CLASS_a]++;
			break;
		default:
			assert(0); /* Mustn't happen. */
		}
		break;
	case CLASS_D:
	case CLASS_S:
		assert(/*type == TYPE_VA_LIST
		    || */type == TYPE_INT16
		    || type == TYPE_UINT16
		    || type == TYPE_POINTER);
		count[class]++;
		break;
	case CLASS_a:
		if (type == TYPE_INT8
		 || type == TYPE_UINT8
		 || type == TYPE_INT16
		 || type == TYPE_UINT16) {
			count[class]++;
		} else if (type == TYPE_INT32
			|| type == TYPE_UINT32) {
			count[class]++;
			count[CLASS_r]++;
		} else if (type == TYPE_INT64
			|| type == TYPE_UINT64) {
			count[CLASS_D]++;
			count[CLASS_S]++;
			count[CLASS_a]++;
			count[CLASS_d]++;
		} else {
			assert(0);
		}
		break;
	case CLASS_b:
	case CLASS_c:
	case CLASS_d:
		if (type == TYPE_INT8
		 || type == TYPE_UINT8
		 || type == TYPE_INT16
		 || type == TYPE_UINT16) {
			count[class]++; 
		} else if (type == TYPE_INT32
			|| type == TYPE_UINT32) {
			count[class]++; 
			count[CLASS_r]++;
		} else {
			assert(0);
		}
		break;
	case CLASS_q:
		if (type == TYPE_INT8
		 || type == TYPE_UINT8
/*		 || type == TYPE_VA_LIST*/
		 || type == TYPE_INT16
		 || type == TYPE_UINT16
		 || type == TYPE_POINTER) {
			count[class]++;
		} else if (type == TYPE_INT32
			|| type == TYPE_UINT32) {
			count[class]++;
			count[CLASS_r]++;
		} else if (type == TYPE_INT64
			|| type == TYPE_UINT64) {
			count[CLASS_D]++;
			count[CLASS_S]++;
			count[CLASS_a]++;
			count[CLASS_d]++;
		} else {
			assert(0);
		}
		break;
	case CLASS_r:
		if (/*type == TYPE_VA_LIST
		 || */type == TYPE_INT16
		 || type == TYPE_UINT16
		 || type == TYPE_POINTER) {
			count[class]++;
		} else if (type == TYPE_VA_LIST
		    || type == TYPE_INT32
			|| type == TYPE_UINT32) {
			count[class]++;
			count[CLASS_r]++;
		} else if (type == TYPE_INT64
			|| type == TYPE_UINT64) {
			count[CLASS_D]++;
			count[CLASS_S]++;
			count[CLASS_a]++;
			count[CLASS_d]++;
		} else {
			assert(0);
		}
		break;
	case CLASS_l:
		if (/*type == TYPE_VA_LIST
		    || */type == TYPE_INT16
		    || type == TYPE_UINT16
		    || type == TYPE_POINTER) {
			count[class]++;
		} else if (type == TYPE_VA_LIST
		    || type == TYPE_INT32
			|| type == TYPE_UINT32) {
			count[class]++;
			count[CLASS_r]++;
		} else {
			assert(0);
		}
		break;
	default:
		fprintf(stderr, "class: %u, type: %u\n", class, type);
		assert(0);
	}
}

void
arch_i286_color_sub(unsigned int *count, unsigned int class, enum type_type type)
{
	switch (class) {
	case CLASS_NONE:
		break;
	case CLASS_A:
		assert(type == TYPE_INT32
		    || type == TYPE_UINT32);
		assert(0 < count[CLASS_a]);
		count[CLASS_a]--;
		assert(0 < count[CLASS_d]);
		count[CLASS_d]--; 
		break;
	case CLASS_D:
	case CLASS_S:
		assert(/*type == TYPE_VA_LIST
		    || */type == TYPE_INT16
		    || type == TYPE_UINT16
		    || type == TYPE_POINTER);
		assert(0 < count[class]);
		count[class]--;
		break;
	case CLASS_a:
		if (type == TYPE_INT8
		 || type == TYPE_UINT8
		 || type == TYPE_INT16
		 || type == TYPE_UINT16) {
			assert(0 < count[CLASS_a]);
			count[CLASS_a]--;
		} else if (type == TYPE_INT32
			|| type == TYPE_UINT32) {
			assert(0 < count[CLASS_a]);
			count[CLASS_a]--;
			assert(0 < count[CLASS_r]);
			count[CLASS_r]--;
		} else if (type == TYPE_INT64
			|| type == TYPE_UINT64) {
			assert(0 < count[CLASS_D]);
			count[CLASS_D]--;
			assert(0 < count[CLASS_S]);
			count[CLASS_S]--;
			assert(0 < count[CLASS_a]);
			count[CLASS_a]--;
			assert(0 < count[CLASS_d]);
			count[CLASS_d]--;
		} else {
			assert(0);
		}
		break;
	case CLASS_b:
	case CLASS_c:
	case CLASS_d:
		if (type == TYPE_INT8
		 || type == TYPE_UINT8
		 || type == TYPE_INT16
		 || type == TYPE_UINT16) {
			assert(0 < count[class]);
			count[class]--; 
		} else if (type == TYPE_INT32	/* FIXME */
			|| type == TYPE_UINT32) {
			assert(0 < count[class]);
			count[class]--; 
			assert(0 < count[CLASS_r]);
			count[CLASS_r]--;
		} else {
			assert(0);
		}
		break;
	case CLASS_q:
		if (type == TYPE_INT8
		 || type == TYPE_UINT8
/*		 || type == TYPE_VA_LIST*/
		 || type == TYPE_INT16
		 || type == TYPE_UINT16
		 || type == TYPE_POINTER) {
			assert(0 < count[class]);
			count[class]--; 
		} else if (type == TYPE_INT32
			|| type == TYPE_UINT32) {
			assert(0 < count[class]);
			count[class]--; 
			assert(0 < count[CLASS_r]);
			count[CLASS_r]--;
		} else if (type == TYPE_INT64
			|| type == TYPE_UINT64) {
			assert(0 < count[CLASS_D]);
			count[CLASS_D]--; 
			assert(0 < count[CLASS_S]);
			count[CLASS_S]--; 
			assert(0 < count[CLASS_a]);
			count[CLASS_a]--; 
			assert(0 < count[CLASS_d]);
			count[CLASS_d]--;
		} else {
			assert(0);
		}
		break;
	case CLASS_r:
		if (/*type == TYPE_VA_LIST
		 ||*/ type == TYPE_INT16
		 || type == TYPE_UINT16
		 || type == TYPE_POINTER) {
			assert(0 < count[class]);
			count[class]--;
		} else if (type == TYPE_VA_LIST
		    || type == TYPE_INT32
			|| type == TYPE_UINT32) {
			assert(0 < count[class]);
			count[class]--;
			assert(0 < count[CLASS_r]);
			count[CLASS_r]--;
		} else if (type == TYPE_INT64
			|| type == TYPE_UINT64) {
			assert(0 < count[CLASS_D]);
			count[CLASS_D]--;
			assert(0 < count[CLASS_S]);
			count[CLASS_S]--;
			assert(0 < count[CLASS_a]);
			count[CLASS_a]--;
			assert(0 < count[CLASS_d]);
			count[CLASS_d]--;
		} else {
			assert(0);
		}
		break;
	case CLASS_l:
		assert(0 < count[class]);
		if (/*type == TYPE_VA_LIST
		    || */type == TYPE_INT16
		    || type == TYPE_UINT16
		    || type == TYPE_POINTER) {
			count[class]--;
		} else if (type == TYPE_VA_LIST
		    || type == TYPE_INT32
			|| type == TYPE_UINT32) {
			count[class]--;
			count[CLASS_r]--;
		} else {
			assert(0);
		}
		break;
	default:
		fprintf(stderr, "class: %u, type: %u\n", class, type);
		assert(0);
	}
}

int
arch_i286_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 (1 < num) num = 1;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (1 < num) num = 1;
		return num < 1;
	case CLASS_D:
		num = (1 < count[CLASS_D]) ? 1 : count[CLASS_D];
		num += (3 < count[CLASS_l]) ? 3 : count[CLASS_l];
		if (1 < num) num = 1;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (1 < num) num = 1;
		return num < 1;
	case CLASS_S:
		num = (1 < count[CLASS_S]) ? 1 : count[CLASS_S];
		num += (3 < count[CLASS_l]) ? 3 : count[CLASS_l];
		if (1 < num) num = 1;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (1 < num) num = 1;
		return num < 1;
	case CLASS_a:
		num = (1 < count[CLASS_a]) ? 1 : count[CLASS_a];
		num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
		if (1 < num) num = 1;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (1 < num) num = 1;
		return num < 1;
	case CLASS_b:
		num = (1 < count[CLASS_b]) ? 1 : count[CLASS_b];
		num += (3 < count[CLASS_l]) ? 3 : count[CLASS_l];
		if (1 < num) num = 1;
		num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
		if (1 < num) num = 1;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (1 < num) num = 1;
		return num < 1;
	case CLASS_c:
		num = (1 < count[CLASS_c]) ? 1 : count[CLASS_c];
		num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
		if (1 < num) num = 1;
		num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
		if (1 < num) num = 1;
		return num < 1;
	case CLASS_d:
		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_q:
		num = (1 < count[CLASS_b]) ? 1 : count[CLASS_b];
		num += (3 < count[CLASS_l]) ? 3 : count[CLASS_l];
		if (1 < num) num = 1;
		num += (1 < count[CLASS_a]) ? 1 : count[CLASS_a];
		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 (4 < num) num = 4;
		return num < 4;
	case CLASS_r:
		if (type == TYPE_INT8
		 || type == TYPE_UINT8) {
			assert(0); /* Don't request 'r' for bytes! */
		} else if (/*type == TYPE_VA_LIST
			||*/ type == TYPE_INT16
			|| type == TYPE_UINT16
			|| type == TYPE_POINTER) {
			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 += (3 < count[CLASS_l]) ? 3 : count[CLASS_l]; /* Correct? */
			if (6 < num) num = 6;
			num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
			if (6 < num) num = 6;
			return num < 6;
		} else if (type == TYPE_VA_LIST
		    || type == TYPE_INT32
			|| type == TYPE_UINT32) {
			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 += (3 < count[CLASS_l]) ? 3 : count[CLASS_l]; /* Correct? */
			if (6 < num) num = 6;
			num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
			if (6 < num) num = 6;
			return num < 5;
		} else if (type == TYPE_INT64
			|| type == TYPE_UINT64) {
			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 += (1 < count[CLASS_D]) ? 1 : count[CLASS_D];
			num += (1 < count[CLASS_S]) ? 1 : count[CLASS_S];
			num += (3 < count[CLASS_l]) ? 3 : count[CLASS_l];
			if (6 < num) num = 6;
			num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
			if (6 < num) num = 6;
			return num < 1;
		} else {
			assert(0);
			return 0;
		}
	case CLASS_l:
		if (/*type == TYPE_VA_LIST
		    ||*/ type == TYPE_INT16
		    || type == TYPE_UINT16/*
		    || type == TYPE_POINTER*/) {
			num = (1 < count[CLASS_b]) ? 1 : count[CLASS_b];
			num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
			if (1 < num) num = 1;
			num += (1 < count[CLASS_D]) ? 1 : count[CLASS_D];
			num += (1 < count[CLASS_S]) ? 1 : count[CLASS_S];
			num += (3 < count[CLASS_l]) ? 3 : count[CLASS_l]; /* Correct? */
			if (3 < num) num = 3;
			num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
			if (3 < num) num = 3;
			return num < 3;
		} else if (type == TYPE_VA_LIST
		    || type == TYPE_INT32 || type == TYPE_POINTER
		    || type == TYPE_UINT32) {
			num = (1 < count[CLASS_b]) ? 1 : count[CLASS_b];
			num += (4 < count[CLASS_q]) ? 4 : count[CLASS_q];
			if (1 < num) num = 1;
			num += (1 < count[CLASS_D]) ? 1 : count[CLASS_D];
			num += (1 < count[CLASS_S]) ? 1 : count[CLASS_S];
			num += (3 < count[CLASS_l]) ? 3 : count[CLASS_l];
			if (3 < num) num = 3;
			num += (6 < count[CLASS_r]) ? 6 : count[CLASS_r];
			if (3 < num) num = 3;
			return num < 1;
		} else {
			assert(0);
			return 0;
		}

	default:
		fprintf(stderr, "class: %u, type: %u\n", class, type);
		assert(0);
	}
}

void
arch_i286_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_i286_gen_reg_add(uint32_t *conflicts, unsigned int reg)
{
	arch_gen_reg_add_1(conflicts, reg);
}

int
arch_i286_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_q: regs = REG_q; break;
	case CLASS_r: regs = REG_r; break;
	case CLASS_l: regs = REG_l; 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 &= 0; 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_16; 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:
				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);

				if (seg_enabled()) {
					assert(0); /* Mustn't happen. */
				} else {
					if (dion->storage == STORAGE_STATIC) {
						fprintf(fp, "\t.word %s\n",
								arch_gen_local_off(dion->identifier));
					} else {
						fprintf(fp, "\t.word %s\n",
								arch_gen_global_off(dion->identifier));
					}
				}
				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.word 0x%04x, 0x%04x\n",
					(uint16_t) e->integer,
					(uint16_t) (e->integer >> 16));
				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);

				if (seg_enabled()) {
					if (dion->storage == STORAGE_STATIC) {
						fprintf(fp, "\t.word %s, %s\n",
								arch_gen_local_off(dion->identifier),
								arch_gen_local_seg(dion->identifier));
					} else {
						fprintf(fp, "\t.word %s, %s\n",
								arch_gen_global_off(dion->identifier),
								arch_gen_global_seg(dion->identifier));
					}
				} else {
					assert(0); /* Mustn't happen. */
				}
				break;
			default:
				assert(0);
			}
		} else {
			fprintf(fp, "\t.word 0x0000, 0x0000\n");
		}
		break;
	case TYPE_INT64:
	case TYPE_UINT64:
		if (e) {
			switch (e->type) {
			case EXPR_INTEGER:
				fprintf(fp, "\t.word 0x%04x, 0x%04x, 0x%04x, 0x%04x\n", 
					(uint16_t) e->integer,
					(uint16_t) (e->integer >> 16),
					(uint16_t) (e->integer >> 32),
					(uint16_t) (e->integer >> 48));
				break;
			default:
				assert(0);
			}
		} else {
			fprintf(fp, "\t.word 0x0000, 0x0000, 0x0000, 0x0000\n");
		}
		break;

	case TYPE_FLOAT32:
		assert(0); /* FIXME */
		break;
	case TYPE_FLOAT64:
		assert(0); /* FIXME */
		break;
	case TYPE_FLOAT80:
		assert(0); /* FIXME */
		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) {
			if (seg_enabled()) {
				/*
				 * 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 *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);

		var = arch_tmp(block->scope, type_uint16());

		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			if (seg_enabled()) {
				/*
				 * movw %w1, %0
				 * movw %0, %%es
				 * movb %2, %%es:%1
				 */
				constraint_output(s, "=&r", expr_identifier(var));
				constraint_output(s, "=m", expr_dup(lhs));
				constraint_input(s, "iq", expr_dup(e));
			} else {
				/*
				 * movb %1, %0
				 */
				constraint_output(s, "=m", expr_dup(lhs));
				constraint_input(s, "iq", expr_dup(e));
			}
			break;
		case TYPE_INT16:
		case TYPE_UINT16:
			if (seg_enabled()) {
				/*
				 * movw %w1, %0
				 * movw %0, %%es
				 * movw %2, %%es:%1
				 */
			} else {
				 /*
				  * movw %1, %0
				  */
			}
			/*FALLTHROUGH*/
		case TYPE_INT32:
		case TYPE_UINT32:
			if (seg_enabled()) {
				/*
				 * movw %w1, %0
				 * movw %0, %%es
				 * movw %2, %es:%1
				 * movw %w2, %es:2+%1
				 */
			} else {
				/*
				 * movw %1, %0
				 * movw %w1, 2+%0
				 */
			}
			/*FALLTHROUGH*/
		case TYPE_INT64:
		case TYPE_UINT64:
			if (seg_enabled()) {
				/*
				 * movw %w1, %0
				 * movw %0, %%es
				 * movw %2, %%es:%1
				 * movw %w2, %%es:2+%1
				 * movw %u2, %%es:4+%1
				 * movw %v2, %%es:6+%1
				 */
				constraint_output(s, "=&r", expr_identifier(var));
				constraint_output(s, "=m", expr_dup(lhs));
				constraint_input(s, "ir", expr_dup(e));
			} else {
				/*
				 * movw %1, %0
				 * movw %w1, 2+%0
				 * movw %u1, 4+%0
				 * movw %v1, 6+%0
				 */
				constraint_output(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);

		if (seg_enabled()) {
			var = arch_tmp(block->scope, type_uint16());
		} else {
			var = NULL;
		}

		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			if (seg_enabled()) {
				/*
				 * movw %w2, %1
				 * movw %1, %%es
				 * movb %%es:%2, %0
				 */
			} else {
				/*
				 * movb %1, %0
				 */
			}
			/*FALLTHROUGH*/
		case TYPE_INT16:
		case TYPE_UINT16:
			if (seg_enabled()) {
				/*
				 * movw %w2, %1
				 * movw %1, %%es
				 * movw %%es:%2, %0
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_output(s, "=&r", expr_identifier(var));
				constraint_input(s, "m", expr_dup(e));
			} else {
				/*
				 * movw %1, %0
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_input(s, "m", expr_dup(e));
			}
			break;
		case TYPE_INT32:
		case TYPE_UINT32:
			if (seg_enabled()) {
				/*
				 * movw %w2, %1
				 * movw %1, %%es
				 * movw %es:%2, %0
				 * movw %es:2+%2, %w0
				 */
			} else {
				/*
				 * movw %1, %0
				 * movw 2+%1, %w0
				 */
			}
			/*FALLTHROUGH*/
		case TYPE_INT64:
		case TYPE_UINT64:
			if (seg_enabled()) {
				/*
				 * movw %w2, %1
				 * movw %1, %%es
				 * movw %es:%2, %0
				 * movw %es:2+%2, %w0
				 * movw %es:4+%2, %u0
				 * movw %es:6+%2, %v0
				 */
				constraint_output(s, "=&r", expr_dup(lhs));
				constraint_output(s, "=&r", expr_identifier(var));
				constraint_input(s, "m", expr_dup(e));
			} else {
				/*
				 * movw %1, %0
				 * movw 2+%1, %w0
				 * movw 4+%1, %u0
				 * movw 6+%1, %v0
				 */
				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
				 */
				constraint_output(s, "=mr", expr_dup(lhs));
				constraint_input(s, "ir", expr_dup(e));
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * movw %1, %0
				 * movw %w1, %w0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * movw %1, %0
				 * movw %w1, %w0
				 * movw %u1, %v0
				 * movw %v1, %v0
				 */
				constraint_output(s, "=&r", expr_dup(lhs));
				constraint_input(s, "imr", expr_dup(e));
				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_BUILTIN_VA_ARG:
			t1 = e->type_name;

			switch (t1->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * movb [%ss:](%1), %0
				 * addw $2, %1
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * movw [%ss:](%1), %0
				 * addw $2, %1
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * movw [%ss:](%1), %0
				 * movw [%ss:]2(%1), %w0
				 * addw $4, %1
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * movw [%ss:](%1), %0
				 * movw [%ss:]2(%1), %w0
				 * movw [%ss:]4(%1), %u0
				 * movw [%ss:]6(%1), %v0
				 * addw $8, %1
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_output(s, "=l", 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) {
					/*
					 * movw $%1, %0
					 * [movw $%w1, %w0]
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "m", expr_dup(e));
				} else {
					/*
					 * leaw %1, %0
					 * [movw %%ss, %w0]
					 */
					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:
					/*
					 * cbw
					 */
					constraint_output(s, "=a", expr_dup(lhs));
					constraint_input(s, "a", expr_dup(e->expr0));
					break;
				case TYPE_UINT8:
					/*
					 * movb %1, %0
					 * xorb %h0, %h0
					 */
					constraint_output(s, "=&q", expr_dup(lhs));
					constraint_input(s, "mr", 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 %1, %0
					 */
					/*FALLTHROUGH*/
				case TYPE_INT64:
				case TYPE_UINT64:
					/*
					 * movw %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:
					assert(0); /* FIXME */
					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:
					/*
					 * cbw
					 * cwd
					 */
					constraint_output(s, "=A", expr_dup(lhs));
					constraint_input(s, "a", expr_dup(e->expr0));
					break;
				case TYPE_UINT8:
					/*
					 * movb %1, %b0
					 * xorb %h0, %h0
					 * xorw %w0, %w0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_INT16:
					/*
					 * cwd
					 */
					constraint_output(s, "=A", expr_dup(lhs));
					constraint_input(s, "a", expr_dup(e->expr0));
					break;
				case TYPE_UINT16:
					/*
					 * movw %1, %0
					 * xorw %w0, %w0
					 */
					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:
					/*
					 * movw %1, %0
					 * movw %w1, %w0
					 */
					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:
					assert(0); /* FIXME */
					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:
					/*
					 * cbw
					 * cwd
					 * movw %%dx, %u0
					 * movw %%dx, %v0
					 */
					constraint_output(s, "=A", expr_dup(lhs));
					constraint_input(s, "a", expr_dup(e->expr0));
					break;
				case TYPE_UINT8:
					/*
					 * movb %1, %0
					 * xorb %h0, %h0
					 * xorw %w0, %w0
					 * xorw %u0, %u0
					 * xorw %v0, %v0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_INT16:
					/*
					 * cwd
					 * movw %%dx, %u0
					 * movw %%dx, %v0
					 */
					constraint_output(s, "=A", expr_dup(lhs));
					constraint_input(s, "a", expr_dup(e->expr0));
					break;
				case TYPE_UINT16:
					/*
					 * movw %1, %0
					 * xorw %w0, %w0
					 * xorw %u0, %u0
					 * xorw %v0, %v0
					 */
					constraint_output(s, "=r", expr_dup(lhs));
					constraint_input(s, "mr", expr_dup(e->expr0));
					break;
				case TYPE_INT32:
					/*
					 * movw %1, %v0
					 * movw %w1, %0
					 * cwd
					 * xchgw %w0, %v0
					 * xchgw %w0, %0
					 * movw %v0, %u0
					 */
					constraint_output(s, "=A", expr_dup(lhs));
					constraint_input(s, "A", expr_dup(e->expr0));
					break;
				case TYPE_UINT32:
					/*
					 * movw %1, %0
					 * movw %w1, %w0
					 * xorw %u0, %u0
					 * xorw %v0, %v0
					 */
					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:
					assert(0); /* FIXME */
					break;
				case TYPE_VA_LIST:
					assert(0); /* FIXME */
					break;
				default:
					assert(0);
				}
				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 EXPR_STAR:
			assert(lhs);

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * [movw %w1, %%es]
				 * movb [%%es:](%1), %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * [movw %w1, %%es]
				 * movw [%%es:](%1), %0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * [movw %w1, %%es]
				 * movw [%%es:](%1), %0
				 * movw [%%es:]2(%1), %w0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * [movw %w1, %%es]
				 * movw [%%es:](%1), %0
				 * movw [%%es:]2(%1), %w0
				 * movw [%%es:]4(%1), %u0
				 * movw [%%es:]6(%1), %v0
				 */
				constraint_output(s, "=r", expr_dup(lhs));
				constraint_input(s, "l", expr_dup(e->expr0->expr0));
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_NEG:
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				/*
				 * negb %0
				 */
				/*FALLTRHOUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * negw %0
				 */
				/*FALLTRHOUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * negw %0
				 * adcw $0, %w0
				 * negw %w0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * negw %0
				 * adcw $0, %w0
				 * adcw $0, %u0
				 * adcw $0, %v0
				 * negw %w0
				 * adcw $0, %u0
				 * adcw $0, %v0
				 * negw %u0
				 * adcw $0, %v0
				 * negw %v0
				 */
				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:
				/*
				 * xorw $-1, %0
				 * xorw $-1, %w0
				 */
				/*FALLTHROUGH*/
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * xorw $-1, %0
				 * xorw $-1, %w0
				 * xorw $-1, %u0
				 * xorw $-1, %v0
				 */
				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} %2, %1
				 * jcc 2f
				 * 1:
				 * movw $1, %0
				 * jmp 3f
				 * 2:
				 * xorw %0, %0
				 * 3:
				 */
				/*FALLTHROUGH*/
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * cmp{b,w} %2, %1
				 * jcc 2f
				 * 1:
				 * movw $1, %0
				 * jmp 3f
				 * 2:
				 * xorw %0, %0
				 * 3:
				 */
				/*FALLTHROUGH*/
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * ...
				 */
				/*FALLTHROUGH*/
			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:
				/*
				 * shlb %b2, %0
				 */
				goto shift_int8_16;
			case TYPE_INT16:
			case TYPE_UINT16:
				/*
				 * shlw %b2, %0
				 */
				goto shift_int8_16;
			case TYPE_INT32:
			case TYPE_UINT32:
				/*
				 * jmp 2f
				 * 1:
				 * shlw $1, %0
				 * rclw $1, %w0
				 * decb %b1
				 * 2:
				 * cmpb $0, %b1
				 * jne 1b
				 */
				goto shift_int32_64;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * jmp 2f
				 * 1:
				 * shlw $1, %0
				 * rclw $1, %w0
				 * rclw $1, %u0
				 * rclw $1, %v0
				 * decb %b1
				 * 2:
				 * cmpb $0, %b1
				 * jne 1b
				 */
				goto shift_int32_64;
			default:
				assert(0);
			}
			break;

		case EXPR_RIGHT:
			switch (t->type) {
			case TYPE_INT8:
				/*
				 * sarb %b2, %0
				 */
				goto shift_int8_16;
			case TYPE_UINT8:
				/*
				 * shrb %b2, %0
				 */
				goto shift_int8_16;
			case TYPE_INT16:
				/*
				 * sarw %b2, %0
				 */
				goto shift_int8_16;
			case TYPE_UINT16:
				/*
				 * shrw %b2, %0
				 */
			shift_int8_16:
				constraint_output(s, "=rm", expr_dup(lhs));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "ci", expr_dup(e->expr1));
				break;
			case TYPE_INT32:
				/*
				 * jmp 2f
				 * 1:
				 * sarw $1, %w0
				 * rcrw $1, %0
				 * decb %b1
				 * 2:
				 * cmpb $0, %b1
				 * jne 1b
				 */
				goto shift_int32_64;
			case TYPE_UINT32:
				/*
				 * jmp 2f
				 * 1:
				 * shrw $1, %w0
				 * rcrw $1, %0
				 * decb %b1
				 * 2:
				 * cmpb $0, %b1
				 * jne 1b
				 */
				goto shift_int32_64;
			case TYPE_INT64:
				/*
				 * jmp 2f
				 * 1:
				 * sarw $1, %v0
				 * rcrw $1, %u0
				 * rcrw $1, %w0
				 * rcrw $1, %0
				 * decb %b1
				 * 2:
				 * cmpb $0, %b1
				 * jne 1b
				 */
				goto shift_int32_64;
			case TYPE_UINT64:
				/*
				 * jmp 2f
				 * 1:
				 * sarw $1, %v0
				 * rcrw $1, %u0
				 * rcrw $1, %w0
				 * rcrw $1, %0
				 * decb %b1
				 * 2:
				 * cmpb $0, %b1
				 * jne 1b
				 */
			shift_int32_64:
				var = arch_tmp(block->scope,
						type_pure(expr_typeof(block->scope, e->expr1)));

				constraint_output(s, "=rm", expr_dup(lhs));
				constraint_output(s, "=q", expr_identifier(var));
				constraint_input(s, "0", expr_dup(e->expr0));
				constraint_input(s, "1", 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:
				/*
				 * addw %2, %0
				 * adcw %w2, %w0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * addw %2, %0
				 * adcw %w2, %w0
				 * adcw %u2, %u0
				 * adcw %v2, %v0
				 */
				goto sub_int;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			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:
				/*
				 * subw %2, %0
				 * sbbw %w2, %w0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * subw %2, %0
				 * sbbw %w2, %w0
				 * sbbw %u2, %u0
				 * sbbw %v2, %v0
				 */
			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:
				assert(0); /* 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
				 */
				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:
				arch_op_func(e, arch_mul_int32);
				goto func;
			case TYPE_UINT32:
				arch_op_func(e, arch_mul_uint32);
				goto func;
			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:
				arch_op_func(e, arch_div_int32);
				goto func;
			case TYPE_UINT32:
				arch_op_func(e, arch_div_uint32);
				goto func;
			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:
				arch_op_func(e, arch_mod_int32);
				goto func;
			case TYPE_UINT32:
				arch_op_func(e, arch_mod_uint32);
				goto func;
			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:
				/*
				 * andw %2, %0
				 * andw %w2, %w0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * andw %2, %0
				 * andw %w2, %w0
				 * andw %u2, %u0
				 * andw %v2, %v0
				 */
				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:
				/*
				 * orw %2, %0
				 * orw %w2, %w0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * orw %2, %0
				 * orw %w2, %w0
				 * orw %u2, %u0
				 * orw %v2, %v0
				 */
				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:
				/*
				 * xorw %2, %0
				 * xorw %w2, %w0
				 */
				goto sub_int;
			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * xorw %2, %0
				 * xorw %w2, %w0
				 * xorw %u2, %u0
				 * xorw %v2, %v0
				 */
				goto sub_int;
			default:
				assert(0);
			}
			break;

		case EXPR_FUNC:
		func:	;
			/*
			 * pushl %N-1
			 * pushl %N-2
			 * ...
			 * pushl %1
			 * [l]call %0
			 * addw $c, %esp
			 */
			regs_change = REG_CALLER;
			if (lhs) {
				switch (t->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
				case TYPE_INT16:
				case TYPE_UINT16:
					constraint_output(s, "=a", expr_dup(lhs));
					regs_change &= ~(1ULL << AX);
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					constraint_output(s, "=A", expr_dup(lhs));
					regs_change &= ~((1ULL << AX) | (1ULL << DX));
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					constraint_output(s, "=r", expr_dup(lhs));
					regs_change &= ~((1ULL << AX) | (1ULL << DX)
							| (1ULL << DI) | (1ULL << SI));
					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);
				}
			}

			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;
	struct declaration *dion;
	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);

		if (seg_enabled()) {
			strcpy(buf0, "\tmovw %w1, %0\n");
			strcat(buf0, "\tmovw %0, %%es\n");
		} else {
			strcpy(buf0, "");
		}
		
		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			if (seg_enabled()) {
				strcat(buf0, "\tmovb %2, %%es:%1\n");
			} else {
				strcat(buf0, "\tmovb %1, %0\n");
			}
			break;
		case TYPE_INT16:
		case TYPE_UINT16:
			if (seg_enabled()) {
				strcat(buf0, "\tmovw %2, %%es:%1\n");
			} else {
				strcat(buf0, "\tmovw %1, %0\n");
			}
			break;
		case TYPE_INT32:
		case TYPE_UINT32:
			if (seg_enabled()) {
				strcat(buf0, "\tmovw %2, %%es:%1\n");
				strcat(buf0, "\tmovw %w2, %%es:2+%1\n");
			} else {
				strcat(buf0, "\tmovw %1, %0\n");
				strcat(buf0, "\tmovw %w1, 2+%0\n");
			}
			break;
		case TYPE_INT64:
		case TYPE_UINT64:
			if (seg_enabled()) {
				strcat(buf0, "\tmovw %2, %%es:%1\n");
				strcat(buf0, "\tmovw %w2, %%es:2+%1\n");
				strcat(buf0, "\tmovw %u2, %%es:4+%1\n");
				strcat(buf0, "\tmovw %v2, %%es:6+%1\n");
			} else {
				strcat(buf0, "\tmovw %1, %0\n");
				strcat(buf0, "\tmovw %w1, 2+%0\n");
				strcat(buf0, "\tmovw %u1, 4+%0\n");
				strcat(buf0, "\tmovw %v1, 6+%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);

		if (seg_enabled()) {
			strcpy(buf0, "\tmovw %w2, %1\n");
			strcat(buf0, "\tmovw %1, %%es\n");
		} else {
			strcpy(buf0, "");
		}
		
		switch (t->type) {
		case TYPE_INT8:
		case TYPE_UINT8:
			if (seg_enabled()) {
				strcat(buf0, "\tmovb %%es:%2, %0\n");
			} else {
				strcat(buf0, "\tmovb %1, %0\n");
			}
			break;
		case TYPE_INT16:
		case TYPE_UINT16:
			if (seg_enabled()) {
				strcat(buf0, "\tmovw %%es:%2, %0\n");
			} else {
				strcat(buf0, "\tmovw %1, %0\n");
			}
			break;
		case TYPE_INT32:
		case TYPE_UINT32:
			if (seg_enabled()) {
				strcat(buf0, "\tmovw %%es:%2, %0\n");
				strcat(buf0, "\tmovw %%es:2+%2, %w0\n");
			} else {
				strcat(buf0, "\tmovw %1, %0\n");
				strcat(buf0, "\tmovw 2+%1, %w0\n");
			}
			break;
		case TYPE_INT64:
		case TYPE_UINT64:
			if (seg_enabled()) {
				strcat(buf0, "\tmovw %%es:%2, %0\n");
				strcat(buf0, "\tmovw %%es:2+%2, %w0\n");
				strcat(buf0, "\tmovw %%es:4+%2, %u0\n");
				strcat(buf0, "\tmovw %%es:6+%2, %v0\n");
			} else {
				strcat(buf0, "\tmovw %1, %0\n");
				strcat(buf0, "\tmovw 2+%1, %w0\n");
				strcat(buf0, "\tmovw 4+%1, %u0\n");
				strcat(buf0, "\tmovw 6+%1, %v0\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, "\tmovw %1, %0\n");
				strcat(buf0, "\tmovw %w1, %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tmovw %1, %0\n");
				strcat(buf0, "\tmovw %w1, %w0\n");
				strcat(buf0, "\tmovw %u1, %u0\n");
				strcat(buf0, "\tmovw %v1, %v0\n");
				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:
				strcpy(buf0, "\tmovw %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_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_INT32:
			case TYPE_UINT32:
				/*
				 * We have *two* registers in
				 * *two* real registers
				 * (e.g. AX_BX and BX_CX).
				 */
				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, "\txchgw %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, "\tmovw %w1, %w0\n");

				} else if (arch_gen_reg_parts[reglhs].reg[1]
						== arch_gen_reg_parts[reg].reg[1]) {
					/* A_B <- C_B. */
					strcpy(buf0, "\tmovw %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, "\tmovw %w1, %w0\n");
					strcat(buf0, "\tmovw %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, "\tmovw %1, %0\n");
					strcat(buf0, "\tmovw %w1, %w0\n");

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

			case TYPE_INT64:
			case TYPE_UINT64:
				/*
				 * We only have *one* 64-bit register.
				 */
				assert(reglhs == reg);
				strcpy(buf0, "");
				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 %%ss:(%1), %0\n");
				strcat(buf0, "\taddw $1, %1\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcpy(buf0, "\tmovw %%ss:(%1), %0\n");
				strcat(buf0, "\taddw $2, %1\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcpy(buf0, "\tmovw %%ss:(%1), %0\n");
				strcat(buf0, "\tmovw %%ss:2(%1), %w0\n");
				strcat(buf0, "\taddw $4, %1\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tmovw %%ss:(%1), %0\n");
				strcat(buf0, "\tmovw %%ss:2(%1), %w0\n");
				strcat(buf0, "\tmovw %%ss:4(%1), %u0\n");
				strcat(buf0, "\tmovw %%ss:6(%1), %v0\n");
				strcat(buf0, "\taddw $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
				 */
				dion = e->expr0->expr0->declaration;
				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;
			special:
				if (dion->storage == STORAGE_NONE
				 || dion->storage == STORAGE_EXTERN
				 || dion->storage == STORAGE_STATIC) {
					if (seg_enabled()) {
						strcpy(buf0, "\tmovw $%1, %0\n");
						strcat(buf0, "\tmovw $%w1, %w0\n");
					} else {
						strcpy(buf0, "\tmovw $%1, %0\n");
					}
				} else {
					if (seg_enabled()) {
						strcpy(buf0, "\tleaw %1, %0\n");
						strcat(buf0, "\tmovw %%ss, %w0\n");
					} else {
						strcpy(buf0, "\tleaw %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, "\tcbw\n");
					break;
				case TYPE_UINT8:
					strcpy(buf0, "\tmovb %1, %b0\n");
					strcat(buf0, "\txorb %h0, %h0\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 %1, %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_INT32:
			case TYPE_UINT32:
				switch (t1->type) {
				case TYPE_INT8:
					strcpy(buf0, "\tcbw\n");
					strcat(buf0, "\tcwd\n");
					break;
				case TYPE_UINT8:
					strcpy(buf0, "\tmovb %1, %b0\n");
					strcat(buf0, "\txorb %h0, %h0\n");
					strcat(buf0, "\txorw %w0, %w0\n");
					break;
				case TYPE_INT16:
					strcpy(buf0, "\tmovw %1, %0\n");
					strcat(buf0, "\tcwd\n");
					break;
				case TYPE_UINT16:
					strcpy(buf0, "\tmovw %1, %0\n");
					strcat(buf0, "\txorw %w0, %w0\n");
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					strcpy(buf0, "");
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					strcpy(buf0, "\tmovw %1, %0\n");
					strcat(buf0, "\tmovw %w1, %w0\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_INT64:
			case TYPE_UINT64:
				switch (t1->type) {
				case TYPE_INT8:
					strcpy(buf0, "\tcbw\n");
					strcat(buf0, "\tcwd\n");
					strcat(buf0, "\tmovw %%dx, %u0\n");
					strcat(buf0, "\tmovw %%dx, %v0\n");
					break;
				case TYPE_UINT8:
					strcpy(buf0, "\tmovb %1, %b0\n");
					strcat(buf0, "\txorb %h0, %h0\n");
					strcat(buf0, "\txorw %w0, %w0\n");
					strcat(buf0, "\txorw %u0, %u0\n");
					strcat(buf0, "\txorw %v0, %v0\n");
					break;
				case TYPE_INT16:
					strcpy(buf0, "\tcwd\n");
					strcat(buf0, "\tmovw %%dx, %u0\n");
					strcat(buf0, "\tmovw %%dx, %v0\n");
					break;
				case TYPE_UINT16:
					strcpy(buf0, "\tmovw %1, %0\n");
					strcat(buf0, "\txorw %w0, %w0\n");
					strcat(buf0, "\txorw %u0, %u0\n");
					strcat(buf0, "\txorw %v0, %v0\n");
					break;
				case TYPE_INT32:
					strcpy(buf0, "\tmovw %1, %v0\n");
					strcat(buf0, "\tmovw %w1, %0\n");
					strcat(buf0, "\tcwd\n");
					strcat(buf0, "\txchgw %w0, %v0\n");
					strcat(buf0, "\txchgw %w0, %0\n");
					strcat(buf0, "\tmovw %v0, %u0\n");
					break;
				case TYPE_UINT32:
					strcpy(buf0, "\tmovw %1, %0\n");
					strcat(buf0, "\tmovw %w1, %w0\n");
					strcat(buf0, "\txorw %u0, %u0\n");
					strcat(buf0, "\txorw %v0, %v0\n");
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					strcpy(buf0, "");
					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_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			break;

		case EXPR_STAR:
			assert(lhs);

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				if (seg_enabled()) {
					strcpy(buf0, "\tmovw %w1, %%es\n");
					strcat(buf0, "\tmovb %%es:(%1), %0\n");
				} else {
					strcpy(buf0, "\tmovb (%1), %0\n");
				}
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				if (seg_enabled()) {
					strcpy(buf0, "\tmovw %w1, %%es\n");
					strcat(buf0, "\tmovw %%es:(%1), %0\n");
				} else {
					strcpy(buf0, "\tmovw (%1), %0\n");
				}
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				if (seg_enabled()) {
					strcpy(buf0, "\tmovw %w1, %%es\n");
					strcat(buf0, "\tmovw %%es:(%1), %0\n");
					strcat(buf0, "\tmovw %%es:2(%1), %w0\n");
				} else {
					strcpy(buf0, "\tmovw (%1), %0\n");
					strcat(buf0, "\tmovw 2(%1), %w0\n");
				}
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				if (seg_enabled()) {
					strcpy(buf0, "\tmovw %w1, %%es\n");
					strcat(buf0, "\tmovw %%es:(%1), %0\n");
					strcat(buf0, "\tmovw %%es:2(%1), %w0\n");
					strcat(buf0, "\tmovw %%es:4(%1), %u0\n");
					strcat(buf0, "\tmovw %%es:6(%1), %v0\n");
				} else {
					strcpy(buf0, "\tmovw (%1), %0\n");
					strcat(buf0, "\tmovw 2(%1), %w0\n");
					strcat(buf0, "\tmovw 4(%1), %u0\n");
					strcat(buf0, "\tmovw 6(%1), %v0\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_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, "\tnegw %0\n");
				strcat(buf0, "\tadcw $0, %w0\n");
				strcat(buf0, "\tnegw %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tnegw %0\n");
				strcat(buf0, "\tadcw $0, %w0\n");
				strcat(buf0, "\tadcw $0, %u0\n");
				strcat(buf0, "\tadcw $0, %v0\n");
				strcat(buf0, "\tnegw %w0\n");
				strcat(buf0, "\tadcw $0, %u0\n");
				strcat(buf0, "\tadcw $0, %v0\n");
				strcat(buf0, "\tnegw %u0\n");
				strcat(buf0, "\tadcw $0, %v0\n");
				strcat(buf0, "\tnegw %v0\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, "\txorw $-1, %0\n");
				strcat(buf0, "\txorw $-1, %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\txorw $-1, %0\n");
				strcat(buf0, "\txorw $-1, %w0\n");
				strcat(buf0, "\txorw $-1, %u0\n");
				strcat(buf0, "\txorw $-1, %v0\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:
				switch (t1->type) {
				case TYPE_INT8:
				case TYPE_UINT8:
					strcpy(buf0, "\tcmpb %2, %1\n");
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
					strcpy(buf0, "\tcmpw %2, %1\n");
					break;
				default:
					assert(0);
				}
				switch (t1->type) {
				case TYPE_INT8:
				case TYPE_INT16:
					switch (e->type) {
					case EXPR_EQUAL:
						strcat(buf0, "\tjne 2f\n");
						break;
					case EXPR_NOT_EQUAL:
						strcat(buf0, "\tje 2f\n");
						break;
					case EXPR_LESS:
						strcat(buf0, "\tjge 2f\n");
						break;
					case EXPR_LESS_EQUAL:
						strcat(buf0, "\tjg 2f\n");
						break;
					case EXPR_GREATER:
						strcat(buf0, "\tjle 2f\n");
						break;
					case EXPR_GREATER_EQUAL:
						strcat(buf0, "\tjl 2f\n");
						break;
					default:
						assert(0);
					}
					break;
				case TYPE_UINT8:
				case TYPE_UINT16:
					switch (e->type) {
					case EXPR_EQUAL:
						strcat(buf0, "\tjne 2f\n");
						break;
					case EXPR_NOT_EQUAL:
						strcat(buf0, "\tje 2f\n");
						break;
					case EXPR_LESS:
						strcat(buf0, "\tjge 2f\n");
						break;
					case EXPR_LESS_EQUAL:
						strcat(buf0, "\tjg 2f\n");
						break;
					case EXPR_GREATER:
						strcat(buf0, "\tjle 2f\n");
						break;
					case EXPR_GREATER_EQUAL:
						strcat(buf0, "\tjl 2f\n");
						break;
					default:
						assert(0);
					}
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT32:
				switch (e->type) {
				case EXPR_EQUAL: 
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjne 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjne 2f\n");
					break;
				case EXPR_NOT_EQUAL:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjne 1f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tje 2f\n");
					break;
				case EXPR_LESS:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjge 2f\n");
					break;
				case EXPR_LESS_EQUAL:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjg 2f\n");
					break;
				case EXPR_GREATER:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjle 2f\n");
					break;
				case EXPR_GREATER_EQUAL:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjl 2f\n");
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_UINT32:
				switch (e->type) {
				case EXPR_EQUAL:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjne 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjne 2f\n");
					break;
				case EXPR_NOT_EQUAL:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjne 1f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tje 2f\n");
					break;
				case EXPR_LESS:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjge 2f\n");
					break;
				case EXPR_LESS_EQUAL:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjg 2f\n");
					break;
				case EXPR_GREATER:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjle 2f\n");
					break;
				case EXPR_GREATER_EQUAL:
					strcpy(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjl 2f\n");
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_INT64:
				switch (e->type) {
				case EXPR_EQUAL: 
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjne 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjne 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjne 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjne 2f\n");
					break;
				case EXPR_NOT_EQUAL:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjne 1f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjne 1f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjne 1f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tje 2f\n");
					break;
				case EXPR_LESS:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjge 2f\n");
					break;
				case EXPR_LESS_EQUAL:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjl 1f\n");
					strcat(buf0, "\tjg 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjg 2f\n");
					break;
				case EXPR_GREATER:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjle 2f\n");
					break;
				case EXPR_GREATER_EQUAL:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjg 1f\n");
					strcat(buf0, "\tjl 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjl 2f\n");
					break;
				default:
					assert(0);
				}
				break;

			case TYPE_UINT64:
				switch (e->type) {
				case EXPR_EQUAL:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjne 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjne 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjne 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjne 2f\n");
					break;
				case EXPR_NOT_EQUAL:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjne 1f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjne 1f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjne 1f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tje 1f\n");
					break;
				case EXPR_LESS:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjb 1f\n");
					strcat(buf0, "\tja 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjb 1f\n");
					strcat(buf0, "\tja 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjb 1f\n");
					strcat(buf0, "\tja 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjae 2f\n");
					break;
				case EXPR_LESS_EQUAL:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tjb 1f\n");
					strcat(buf0, "\tja 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tjb 1f\n");
					strcat(buf0, "\tja 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tjb 1f\n");
					strcat(buf0, "\tja 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tja 2f\n");
					break;
				case EXPR_GREATER:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tja 1f\n");
					strcat(buf0, "\tjb 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tja 1f\n");
					strcat(buf0, "\tjb 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tja 1f\n");
					strcat(buf0, "\tjb 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjbe 2f\n");
					break;
				case EXPR_GREATER_EQUAL:
					strcpy(buf0, "\tcmpw %v2, %v1\n");
					strcat(buf0, "\tja 1f\n");
					strcat(buf0, "\tjb 2f\n");
					strcat(buf0, "\tcmpw %u2, %u1\n");
					strcat(buf0, "\tja 1f\n");
					strcat(buf0, "\tjb 2f\n");
					strcat(buf0, "\tcmpw %w2, %w1\n");
					strcat(buf0, "\tja 1f\n");
					strcat(buf0, "\tjb 2f\n");
					strcat(buf0, "\tcmpw %2, %1\n");
					strcat(buf0, "\tjb 2f\n");
					break;
				default:
					assert(0);
				}
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				break;
			case TYPE_VA_LIST:
				assert(0); /* FIXME */
				break;
			case TYPE_POINTER:
				assert(0); /* FIXME */
				break;
			default:
				assert(0);
			}
			strcat(buf0, "1:\n");
			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, "\tmovw $1, %0\n");
				strcat(buf0, "\txorw %w0, %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcat(buf0, "\tmovw $1, %0\n");
				strcat(buf0, "\txorw %w0, %w0\n");
				strcat(buf0, "\txorw %u0, %u0\n");
				strcat(buf0, "\txorw %v0, %v0\n");
				break;
			default:
				assert(0);
			}
			strcat(buf0, "\tjmp 3f\n");
			strcat(buf0, "2:\n");
			switch (t->type) {
			case TYPE_INT8:
			case TYPE_UINT8:
				strcat(buf0, "\txorb %0, %0\n");
				break;
			case TYPE_INT16:
			case TYPE_UINT16:
				strcat(buf0, "\txorw %0, %0\n");
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				strcat(buf0, "\txorw %0, %0\n");
				strcat(buf0, "\txorw %w0, %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcat(buf0, "\txorw %0, %0\n");
				strcat(buf0, "\txorw %w0, %w0\n");
				strcat(buf0, "\txorw %u0, %u0\n");
				strcat(buf0, "\txorw %v0, %v0\n");
				break;
			default:
				assert(0);
			}
			strcat(buf0, "3:\n");
			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, "\tjmp 2f\n");
				strcat(buf0, "1:\n");
				strcat(buf0, "\tshlw $1, %0\n");
				strcat(buf0, "\trclw $1, %w0\n");
				strcat(buf0, "\tdecb %b1\n");
				strcat(buf0, "2:\n");
				strcat(buf0, "\tcmpb $0, %b1\n");
				strcat(buf0, "\tjne 1b\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tjmp 2f\n");
				strcat(buf0, "1:\n");
				strcat(buf0, "\tshlw $1, %0\n");
				strcat(buf0, "\trclw $1, %w0\n");
				strcat(buf0, "\trclw $1, %u0\n");
				strcat(buf0, "\trclw $1, %v0\n");
				strcat(buf0, "\tdecb %b1\n");
				strcat(buf0, "2:\n");
				strcat(buf0, "\tcmpb $0, %b1\n");
				strcat(buf0, "\tjne 1b\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, "\tjmp 2f\n");
				strcat(buf0, "1:\n");
				strcat(buf0, "\tsarw $1, %w0\n");
				strcat(buf0, "\trcrw $1, %0\n");
				strcat(buf0, "\tdecb %b1\n");
				strcat(buf0, "2:\n");
				strcat(buf0, "\tcmpb $0, %b1\n");
				strcat(buf0, "\tjne 1b\n");
				break;
			case TYPE_UINT32:
				strcpy(buf0, "\tjmp 2f\n");
				strcat(buf0, "1:\n");
				strcat(buf0, "\tshrw $1, %w0\n");
				strcat(buf0, "\trcrw $1, %0\n");
				strcat(buf0, "\tdecb %b1\n");
				strcat(buf0, "2:\n");
				strcat(buf0, "\tcmpb $0, %b1\n");
				strcat(buf0, "\tjne 1b\n");
				break;
			case TYPE_INT64:
				strcpy(buf0, "\tjmp 2f\n");
				strcat(buf0, "1:\n");
				strcat(buf0, "\tsarw $1, %v0\n");
				strcat(buf0, "\trcrw $1, %u0\n");
				strcat(buf0, "\trcrw $1, %w0\n");
				strcat(buf0, "\trcrw $1, %0\n");
				strcat(buf0, "\tdecb %b1\n");
				strcat(buf0, "2:\n");
				strcat(buf0, "\tcmpb $0, %b1\n");
				strcat(buf0, "\tjne 1b\n");
				break;
			case TYPE_UINT64:
				strcpy(buf0, "\tjmp 2f\n");
				strcat(buf0, "1:\n");
				strcat(buf0, "\tshrw $1, %v0\n");
				strcat(buf0, "\trcrw $1, %u0\n");
				strcat(buf0, "\trcrw $1, %w0\n");
				strcat(buf0, "\trcrw $1, %0\n");
				strcat(buf0, "\tdecb %b1\n");
				strcat(buf0, "2:\n");
				strcat(buf0, "\tcmpb $0, %b1\n");
				strcat(buf0, "\tjne 1b\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, "\taddw %2, %0\n");
				strcat(buf0, "\tadcw %w2, %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\taddw %2, %0\n");
				strcat(buf0, "\tadcw %w2, %w0\n");
				strcat(buf0, "\tadcw %u2, %u0\n");
				strcat(buf0, "\tadcw %v2, %v0\n");
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				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, "\tsubw %2, %0\n");
				strcat(buf0, "\tsbbw %w2, %w0\n");
				break;
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tsubw %2, %0\n");
				strcat(buf0, "\tsbbw %w2, %w0\n");
				strcat(buf0, "\tsbbw %u2, %u0\n");
				strcat(buf0, "\tsbbw %v2, %v0\n");
				break;
			case TYPE_FLOAT32:
			case TYPE_FLOAT64:
			case TYPE_FLOAT80:
				assert(0); /* FIXME */
				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:
				assert(0); /* Operator replaced by function call. */
			case TYPE_UINT32:
				assert(0); /* Operator replaced by function call. */
			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:
				assert(0); /* Operator replaced by function call. */
			case TYPE_UINT32:
				assert(0); /* Operator replaced by function call. */
			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:
				assert(0); /* Operator replaced by function call. */
			case TYPE_UINT32:
				assert(0); /* Operator replaced by function call. */
			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, "\tandw %2, %0\n");
				strcat(buf0, "\tandw %w2, %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\tandw %2, %0\n");
				strcat(buf0, "\tandw %w2, %w0\n");
				strcat(buf0, "\tandw %u2, %u0\n");
				strcat(buf0, "\tandw %v2, %v0\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, "\torw %2, %0\n");
				strcat(buf0, "\torw %w2, %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\torw %2, %0\n");
				strcat(buf0, "\torw %w2, %w0\n");
				strcat(buf0, "\torw %u2, %u0\n");
				strcat(buf0, "\torw %v2, %v0\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, "\txorw %2, %0\n");
				strcat(buf0, "\txorw %w2, %w0\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				strcpy(buf0, "\txorw %2, %0\n");
				strcat(buf0, "\txorw %w2, %w0\n");
				strcat(buf0, "\txorw %u2, %u0\n");
				strcat(buf0, "\txorw %v2, %v0\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
			 * [l]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) {
					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) {
						if (seg_enabled()) {
							sprintf(buf0 + strlen(buf0), "\tpushw %%w%u\n", n);
							sprintf(buf0 + strlen(buf0), "\tpushw %%%u\n", n);
						} else {
							sprintf(buf0 + strlen(buf0), "\tpushw %%%u\n", n);
						}
					} else {
						if (seg_enabled()) {
							sprintf(buf0 + strlen(buf0), "\tpushw %%%%ss\n");
							sprintf(buf0 + strlen(buf0), "\tleaw %%%u, %%%%ax\n", n); /* FIXME */
							sprintf(buf0 + strlen(buf0), "\tpushw %%%%ax\n"); /* FIXME */
						} else {
							sprintf(buf0 + strlen(buf0), "\tleaw %%%u, %%%%ax\n", n); /* FIXME */
							sprintf(buf0 + strlen(buf0), "\tpushw %%%%ax\n"); /* FIXME */
						}
					}
					if (seg_enabled()) {
						size += 4;
					} else {
						size += 2;
					}
					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), "\tpushw %%w%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushw %%%u\n", n);
					size += 4;
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					sprintf(buf0 + strlen(buf0), "\tpushw %%v%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushw %%u%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushw %%w%u\n", n);
					sprintf(buf0 + strlen(buf0), "\tpushw %%%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 %%l%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), "\tpushw %%%u\n", n);
					size += 2;
					break;
				default:
					assert(0);
				}
			}
			switch (e->expr0->type) {
			case EXPR_IDENTIFIER:
				if (seg_enabled()) {
					/*
					 * gas cannot handle
					 * "lcall $SEG_x, $OFF_x". It reports
					 *
					 * Error: can't handle non absolute segment
					 * in `lcall'
					 *
					 * So use code directly.
					 */
					strcat(buf0, "\t.byte 0x9a /* lcall */\n");
					if (e->expr0->declaration->storage == STORAGE_STATIC) {
						sprintf(buf0 + strlen(buf0), "\t.word %s, %s\n",
								arch_gen_local_off(e->expr0->declaration->identifier),
								arch_gen_local_seg(e->expr0->declaration->identifier));
					} else {
						sprintf(buf0 + strlen(buf0), "\t.word %s, %s\n",
								arch_gen_global_off(e->expr0->declaration->identifier),
								arch_gen_global_seg(e->expr0->declaration->identifier));
					}
				} else {
					sprintf(buf0 + strlen(buf0), "\tcall %s\n",
							e->expr0->declaration->identifier);
				}
				break;
			case EXPR_STAR:
				if (seg_enabled()) {
					assert(0); /* FIXME */
				} else {
					sprintf(buf0 + strlen(buf0), "\tcall *%%%u\n", n++);
				}
				break;
			default:
				assert(0);
			}
			if (0 < size) {
				sprintf(buf0 + strlen(buf0), "\taddw $%u, %%%%sp\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:
					/*
					 * [movw %w0, %%es]
					 * movb %1, [%%es:](%0)
					 */
					/*FALLTHROUGH*/
				case TYPE_INT16:
				case TYPE_UINT16:
					/*
					 * [movw %w0, %%es]
					 * movw %1, [%%es:](%0)
					 */
					/*FALLTHROUGH*/
				case TYPE_INT32:
				case TYPE_UINT32:
					/*
					 * [movw %w0, %%es]
					 * movw %1, [%%es:](%0)
					 * movw %w1, [%%es:]2(%0)
					 */
					/*FALLTHROUGH*/
				case TYPE_INT64:
				case TYPE_UINT64:
					/*
					 * [movw %w0, %%es]
					 * movw %1, [%%es:](%0)
					 * movw %w1, [%%es:]2(%0)
					 * movw %u1, [%%es:]4(%0)
					 * movw %v1, [%%es:]6(%0)
					 */
					constraint_input(s, "l",
						expr_dup(s->expr0->expr0->expr0->expr0));
					constraint_input(s, "r",
						expr_dup(s->expr0->expr1));
					break;
				case TYPE_FLOAT32:
				case TYPE_FLOAT64:
				case TYPE_FLOAT80:
					assert(0); /* FIXME */
					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:
			/*
			 * ...
			 */
			/*FALLTHROUGH*/
		case TYPE_INT64:
		case TYPE_UINT64:
			/*
			 * ...
			 */
			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} $c0, %0
		 * je l0
		 * cmp{b,w} $c1, %0
		 * je l1
		 * cmp{b,w} $c2, %0
		 * je l2
		 * ...
		 * cmp{b,w} $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:
				constraint_input(s, "a", expr_dup(s->expr0));
				break;
			case TYPE_INT32:
			case TYPE_UINT32:
				constraint_input(s, "A", expr_dup(s->expr0));
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				constraint_input(s, "r", 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:
		/*
		 * lea X(%bp), %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:
					if (seg_enabled()) {
						strcpy(buf0, "\tmovw %w0, %%es\n");
						strcat(buf0, "\tmovb %1, %%es:(%0)\n");
					} else {
						strcpy(buf0, "\tmovb %1, (%0)\n");
					}
					break;
				case TYPE_INT16:
				case TYPE_UINT16:
					if (seg_enabled()) {
						strcpy(buf0, "\tmovw %w0, %%es\n");
						strcat(buf0, "\tmovw %1, %%es:(%0)\n");
					} else {
						strcpy(buf0, "\tmovw %1, (%0)\n");
					}
					break;
				case TYPE_INT32:
				case TYPE_UINT32:
					if (seg_enabled()) {
						strcpy(buf0, "\tmovw %w0, %%es\n");
						strcat(buf0, "\tmovw %1, %%es:(%0)\n");
						strcat(buf0, "\tmovw %w1, %%es:2(%0)\n");
					} else {
						strcpy(buf0, "\tmovw %1, (%0)\n");
						strcat(buf0, "\tmovw %w1, 2(%0)\n");
					}
					break;
				case TYPE_INT64:
				case TYPE_UINT64:
					if (seg_enabled()) {
						strcpy(buf0, "\tmovw %w0, %%es\n");
						strcat(buf0, "\tmovw %1, %%es:(%0)\n");
						strcat(buf0, "\tmovw %w1, %%es:2(%0)\n");
						strcat(buf0, "\tmovw %u1, %%es:4(%0)\n");
						strcat(buf0, "\tmovw %v1, %%es:6(%0)\n");
					} else {
						strcpy(buf0, "\tmovw %1, (%0)\n");
						strcat(buf0, "\tmovw %w1, 2(%0)\n");
						strcat(buf0, "\tmovw %u1, 4(%0)\n");
						strcat(buf0, "\tmovw %v1, 6(%0)\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;
			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:
			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;
			default:
				assert(0);
			}

			switch (t->type) {
			case TYPE_INT8:
			case TYPE_INT16:
				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:
				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_INT32:
			switch (s->expr0->type) {
			case EXPR_EQUAL:
				strcpy(buf0, "\tcmpw %w1, %w0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpw %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				break;
			case EXPR_LESS:
				strcpy(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpw %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjle %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_GREATER:
				strcpy(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpw %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_UINT32:
			switch (s->expr0->type) {
			case EXPR_EQUAL:
				strcpy(buf0, "\tcmpw %w1, %w0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpw %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				break;
			case EXPR_LESS:
				strcpy(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjbe %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_GREATER:
				strcpy(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %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_INT64:
			switch (s->expr0->type) {
			case EXPR_EQUAL:
				strcpy(buf0, "\tcmpw %v1, %v0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpw %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				break;
			case EXPR_LESS:
				strcpy(buf0, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjl %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjg 1f\n");
				strcat(buf0, "\tcmpw %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjle %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_GREATER:
				strcpy(buf0, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjg %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjl 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %v1, %v0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				strcat(buf0, "\tjne 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tcmpw %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjne %s\n",
						s->stmt0->label->identifier);
				break;
			case EXPR_LESS:
				strcpy(buf0, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjb %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tja 1f\n");
				strcat(buf0, "\tcmpw %1, %0\n");
				sprintf(buf0 + strlen(buf0),
						"\tjbe %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case EXPR_GREATER:
				strcpy(buf0, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpw %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, "\tcmpw %v1, %v0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpw %u1, %u0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpw %w1, %w0\n");
				sprintf(buf0 + strlen(buf0),
						"\tja %s\n",
						s->stmt0->label->identifier);
				strcat(buf0, "\tjb 1f\n");
				strcat(buf0, "\tcmpw %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),
						"\tcmpw $%u, %%0\n",
						(uint16_t) (cs->expr0->integer >> 0));
				strcat(buf0, "\tjne 1f\n");
				sprintf(buf0 + strlen(buf0),
						"\tcmpw $%u, %%w0\n",
						(uint16_t) (cs->expr0->integer >> 16));
				sprintf(buf0 + strlen(buf0),
						"\tje %s\n",
						cs->stmt0->label->identifier);
				strcat(buf0, "1:\n");
				break;
			case TYPE_INT64:
			case TYPE_UINT64:
				sprintf(buf0 + strlen(buf0),
						"\tcmpw $%u, %%0\n",
						(uint16_t) (cs->expr0->integer >> 0));
				strcat(buf0, "\tjne 1f\n");
				sprintf(buf0 + strlen(buf0),
						"\tcmpw $%u, %%w0\n",
						(uint16_t) (cs->expr0->integer >> 16));
				strcat(buf0, "\tjne 1f\n");
				sprintf(buf0 + strlen(buf0),
						"\tcmpw $%u, %%u0\n",
						(uint16_t) (cs->expr0->integer >> 32));
				strcat(buf0, "\tjne 1f\n");
				sprintf(buf0 + strlen(buf0),
						"\tcmpw $%u, %%v0\n",
						(uint16_t) (cs->expr0->integer >> 48));
				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, "\tleaw %u(%%%%bp), %%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 = 2;
			from++;
			break;
		case 'u':
			mod = REG_MOD_u;
			offset = 4;
			from++;
			break;
		case 'v':
			mod = REG_MOD_v;
			offset = 6;
			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 &= 0xffff;
				break;
			case REG_MOD_b:
				val &= 0xff;
				break;
			case REG_MOD_h:
				val >>= 8;
				val &= 0xff;
				break;
			case REG_MOD_u:
				val >>= 32;
				val &= 0xff;
				break;
			case REG_MOD_v:
				val >>= 48;
				val &= 0xff;
				break;
			case REG_MOD_w:
				val >>= 16;
				val &= 0xffff;
				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(%%bp)",
						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;
				case REG_MOD_v:
					reg = arch_reg_name_v[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 (seg_enabled()) {
					if (offset == 0) {
						sprintf(to, "$%s",
								arch_gen_local_off(dion->identifier));
					} else { assert(offset == 2);
						sprintf(to, "$%s",
								arch_gen_local_seg(dion->identifier));
					}
				} else {
					assert(offset == 0);
					sprintf(to, "$%s",
							arch_gen_local_off(dion->identifier));
				}
			} else if (dion->storage == STORAGE_NONE
				|| dion->storage == STORAGE_EXTERN) {
				if (seg_enabled()) {
					if (offset == 0) {
						sprintf(to, "$%s",
								arch_gen_global_off(dion->identifier));
					} else { assert(offset == 2);
						sprintf(to, "$%s",
								arch_gen_global_seg(dion->identifier));
					}
				} else {
					assert(offset == 0);
					sprintf(to, "$%s",
							arch_gen_global_off(dion->identifier));
				}
			} else if (dion->storage == STORAGE_AUTO) {
				sprintf(to, "%d(%%bp)",
						dion->offset + offset);
			} else if (dion->storage == STORAGE_PARAM) {
				sprintf(to, "%d(%%bp)",
						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;

		if (dion->stmt->stmt_last->expr0 != NULL) {
			struct expr *e;
			struct type *t;

			e = dion->stmt->stmt_last->expr0;
			t = e->type_name;

			t = expr_typeof(dion->stmt->scope, e);
			t = type_pure(t);
	
			switch (t->type) {
			case TYPE_INT64:
			case TYPE_UINT64:
				regs_to_save &= ~((1 << SI) | (1 << DI));
				break;
			default:
				break;
			};
		}

		assert((regs_to_save & ~REG_16) == 0);

		/* Save registers */
		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_uint16(), identifier_tmp());
			save[reg]->storage = STORAGE_AUTO;
			switch (reg) {
			case AX: constraint = "=a"; break;
			case BX: constraint = "=b"; break;
			case CX: constraint = "=c"; break;
			case DX: constraint = "=d"; break;
			case DI: constraint = "=D"; break;
			case SI: 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 AX: constraint = "a"; break;
			case BX: constraint = "b"; break;
			case CX: constraint = "c"; break;
			case DX: constraint = "d"; break;
			case DI: constraint = "D"; break;
			case SI: 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 &= ~(2 - 1); /* 2 Byte Alignment */
	autosize = -autosize;

	offset = 2; /* Base Pointer */
	if (seg_enabled()) {
		offset += 4; /* Return Address */
	} else {
		offset += 2; /* 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 += 2 - 1; /* 2 Byte Alignment */
	paramsize &= ~(2 - 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.code16\n");
	fprintf(fp, "\t.arch i286\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) {
		if (seg_enabled()) {
			/*
			 * 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, "\tpushw %%bp\n");
	fprintf(fp, "\tmovw %%sp, %%bp\n");
	if (0 < autosize) {
		fprintf(fp, "\tsubw $%d, %%sp\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
	 */
	if (0 < autosize) {
		fprintf(fp, "\taddw $%d, %%sp\n", autosize);	
	}
	fprintf(fp, "\tpopw %%bp\n");
	if (seg_enabled()) {
		fprintf(fp, "\tlret\n");
	} else {
		fprintf(fp, "\tret\n");
	}
	fprintf(fp, "\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_i286_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_int32 = declaration_identifier("__i286_mul_int32");
	arch_mul_int32->type_name = type_new();
	arch_mul_int32->type_name->type = TYPE_FUNCTION;
	arch_mul_int32->type_name->declarator = type_new();
	arch_mul_int32->type_name->declarator->type = TYPE_INT32;

	arch_mul_uint32 = declaration_identifier("__i286_mul_uint32");
	arch_mul_uint32->type_name = type_new();
	arch_mul_uint32->type_name->type = TYPE_FUNCTION;
	arch_mul_uint32->type_name->declarator = type_new();
	arch_mul_uint32->type_name->declarator->type = TYPE_UINT32;

	arch_div_int32 = declaration_identifier("__i286_div_int32");
	arch_div_int32->type_name = type_new();
	arch_div_int32->type_name->type = TYPE_FUNCTION;
	arch_div_int32->type_name->declarator = type_new();
	arch_div_int32->type_name->declarator->type = TYPE_INT32;

	arch_div_uint32 = declaration_identifier("__i286_div_uint32");
	arch_div_uint32->type_name = type_new();
	arch_div_uint32->type_name->type = TYPE_FUNCTION;
	arch_div_uint32->type_name->declarator = type_new();
	arch_div_uint32->type_name->declarator->type = TYPE_UINT32;

	arch_mod_int32 = declaration_identifier("__i286_mod_int32");
	arch_mod_int32->type_name = type_new();
	arch_mod_int32->type_name->type = TYPE_FUNCTION;
	arch_mod_int32->type_name->declarator = type_new();
	arch_mod_int32->type_name->declarator->type = TYPE_INT32;

	arch_mod_uint32 = declaration_identifier("__i286_mod_uint32");
	arch_mod_uint32->type_name = type_new();
	arch_mod_uint32->type_name->type = TYPE_FUNCTION;
	arch_mod_uint32->type_name->declarator = type_new();
	arch_mod_uint32->type_name->declarator->type = TYPE_UINT32;

	arch_mul_int64 = declaration_identifier("__i286_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("__i286_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("__i286_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("__i286_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("__i286_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("__i286_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);
}
