view dmd2/e2ir.c.nolink @ 1083:c1e9f612e2e2

Fix for dual operand form of fistp, also make reg ST(0) explicit and fix lindquists previous code that allowed dual operand form of fstp but dissallowed the single operand form accidently
author Kelly Wilson <wilsonk cpsc.ucalgary.ca>
date Tue, 10 Mar 2009 06:23:26 -0600
parents f04dde6e882c
children
line wrap: on
line source


// Compiler implementation of the D programming language
// Copyright (c) 1999-2008 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.

#include	<stdio.h>
#include	<string.h>
#include	<time.h>
#include	<complex.h>

#include	"lexer.h"
#include	"expression.h"
#include	"mtype.h"
#include	"dsymbol.h"
#include	"declaration.h"
#include	"enum.h"
#include	"aggregate.h"
#include	"attrib.h"
#include	"module.h"
#include	"init.h"
#include	"template.h"

#if _WIN32
#include	"..\tk\mem.h"	// for mem_malloc
#elif linux
#include	"../tk/mem.h"	// for mem_malloc
#endif

#include	"cc.h"
#include	"el.h"
#include	"oper.h"
#include	"global.h"
#include	"code.h"
#include	"type.h"
#include	"dt.h"
#include	"irstate.h"
#include	"id.h"
#include	"type.h"
#include	"toir.h"

static char __file__[] = __FILE__;	/* for tassert.h		*/
#include	"tassert.h"


elem *addressElem(elem *e, Type *t);
elem *array_toPtr(Type *t, elem *e);
elem *bit_assign(enum OPER op, elem *eb, elem *ei, elem *ev, int result);
elem *bit_read(elem *eb, elem *ei, int result);
elem *exp2_copytotemp(elem *e);

#define el_setLoc(e,loc)	((e)->Esrcpos.Sfilename = (loc).filename, \
				 (e)->Esrcpos.Slinnum = (loc).linnum)

/************************************
 * Call a function.
 */

elem *callfunc(Loc loc,
	IRState *irs,
	int directcall,		// 1: don't do virtual call
	Type *tret,		// return type
	elem *ec,		// evaluates to function address
	Type *ectype,		// original type of ec
	FuncDeclaration *fd,	// if !=NULL, this is the function being called
	Type *t,		// TypeDelegate or TypeFunction for this function
	elem *ehidden,		// if !=NULL, this is the 'hidden' argument
	Array *arguments)
{
    elem *ep;
    elem *e;
    elem *ethis = NULL;
    elem *eside = NULL;
    int i;
    tym_t ty;
    tym_t tyret;
    enum RET retmethod;
    int reverse;
    TypeFunction *tf;
    int op;

#if 0
    printf("callfunc(directcall = %d, tret = '%s', ec = %p, fd = %p)\n",
	directcall, tret->toChars(), ec, fd);
    printf("ec: "); elem_print(ec);
    if (fd)
	printf("fd = '%s'\n", fd->toChars());
#endif

    t = t->toBasetype();
    if (t->ty == Tdelegate)
    {
	// A delegate consists of:
	//	{ Object *this; Function *funcptr; }
	assert(!fd);
	assert(t->nextOf()->ty == Tfunction);
	tf = (TypeFunction *)(t->nextOf());
	ethis = ec;
	ec = el_same(&ethis);
	ethis = el_una(OP64_32, TYnptr, ethis);	// get this
	ec = array_toPtr(t, ec);		// get funcptr
	ec = el_una(OPind, tf->totym(), ec);
    }
    else
    {	assert(t->ty == Tfunction);
	tf = (TypeFunction *)(t);
    }
    retmethod = tf->retStyle();
    ty = ec->Ety;
    if (fd)
	ty = fd->toSymbol()->Stype->Tty;
    reverse = tyrevfunc(ty);
    ep = NULL;
    if (arguments)
    {
	// j=1 if _arguments[] is first argument
	int j = (tf->linkage == LINKd && tf->varargs == 1);

	for (i = 0; i < arguments->dim ; i++)
	{   Expression *arg;
	    elem *ea;

	    arg = (Expression *)arguments->data[i];
	    //printf("\targ[%d]: %s\n", i, arg->toChars());

	    size_t nparams = Argument::dim(tf->parameters);
	    if (i - j < nparams && i >= j)
	    {
		Argument *p = Argument::getNth(tf->parameters, i - j);

		if (p->storageClass & (STCout | STCref))
		{
		    // Convert argument to a pointer,
		    // use AddrExp::toElem()
		    Expression *ae = arg->addressOf(NULL);
		    ea = ae->toElem(irs);
		    goto L1;
		}
	    }
	    ea = arg->toElem(irs);
	L1:
	    if (ea->Ety == TYstruct)
	    {
		ea = el_una(OPstrpar, TYstruct, ea);
		ea->Enumbytes = ea->E1->Enumbytes;
		assert(ea->Enumbytes);
	    }
	    if (reverse)
		ep = el_param(ep,ea);
	    else
		ep = el_param(ea,ep);
	}
    }

    if (retmethod == RETstack)
    {
	if (!ehidden)
	{   // Don't have one, so create one
	    type *t;

	    if (tf->next->toBasetype()->ty == Tstruct)
		t = tf->next->toCtype();
	    else
		t = type_fake(tf->next->totym());
	    Symbol *stmp = symbol_genauto(t);
	    ehidden = el_ptr(stmp);
	}
	if (ep)
	{
#if 0 // BUG: implement
	    if (reverse && type_mangle(tfunc) == mTYman_cpp)
		ep = el_param(ehidden,ep);
	    else
#endif
		ep = el_param(ep,ehidden);
	}
	else
	    ep = ehidden;
	ehidden = NULL;
    }
    assert(ehidden == NULL);

    if (fd && fd->isMember2())
    {
	InterfaceDeclaration *intd;
	Symbol *sfunc;
	AggregateDeclaration *ad;

	ad = fd->isThis();
	if (ad)
	{
	    ethis = ec;
	    if (ad->handle->ty == Tpointer && tybasic(ec->Ety) != TYnptr)
	    {
		ethis = addressElem(ec, ectype);
	    }
	}
	else
	{
	    // Evaluate ec for side effects
	    eside = ec;
	}
	sfunc = fd->toSymbol();

	if (!fd->isVirtual() ||
	    directcall ||		// BUG: fix
	    fd->isFinal()
	   )
	{
	    // make static call
	    ec = el_var(sfunc);
	}
	else
	{
	    // make virtual call
	    elem *ev;
	    unsigned vindex;

	    assert(ethis);
	    ev = el_same(&ethis);
	    ev = el_una(OPind, TYnptr, ev);
	    vindex = fd->vtblIndex;

	    // Build *(ev + vindex * 4)
	    ec = el_bin(OPadd,TYnptr,ev,el_long(TYint, vindex * 4));
	    ec = el_una(OPind,TYnptr,ec);
	    ec = el_una(OPind,tybasic(sfunc->Stype->Tty),ec);
	}
    }
    else if (fd && fd->isNested())
    {
	assert(!ethis);
	ethis = getEthis(0, irs, fd);

    }

    ep = el_param(ep, ethis);

    tyret = tret->totym();

    // Look for intrinsic functions
    if (ec->Eoper == OPvar && (op = intrinsic_op(ec->EV.sp.Vsym->Sident)) != -1)
    {
	el_free(ec);
	if (OTbinary(op))
	{
	    ep->Eoper = op;
	    ep->Ety = tyret;
	    e = ep;
	    if (op == OPscale)
	    {	elem *et;

		et = e->E1;
		e->E1 = el_una(OPs32_d, TYdouble, e->E2);
		e->E1 = el_una(OPd_ld, TYldouble, e->E1);
		e->E2 = et;
		e->Ety = tyret;
	    }
	}
	else
	    e = el_una(op,tyret,ep);
    }
    else if (ep)
	e = el_bin(OPcall,tyret,ec,ep);
    else
	e = el_una(OPucall,tyret,ec);

    if (retmethod == RETstack)
    {
	e->Ety = TYnptr;
	e = el_una(OPind, tyret, e);
    }

#if DMDV2
    if (tf->isref)
    {
	e->Ety = TYnptr;
	e = el_una(OPind, tyret, e);
    }
#endif

    if (tybasic(tyret) == TYstruct)
    {
	e->Enumbytes = tret->size();
    }
    e = el_combine(eside, e);
    return e;
}

/*******************************************
 * Take address of an elem.
 */

elem *addressElem(elem *e, Type *t)
{
    elem **pe;

    //printf("addressElem()\n");

    for (pe = &e; (*pe)->Eoper == OPcomma; pe = &(*pe)->E2)
	;
    if ((*pe)->Eoper != OPvar && (*pe)->Eoper != OPind)
    {	Symbol *stmp;
	elem *eeq;
	elem *e = *pe;
	type *tx;

	// Convert to ((tmp=e),tmp)
	TY ty;
	if (t && ((ty = t->toBasetype()->ty) == Tstruct || ty == Tsarray))
	    tx = t->toCtype();
	else
	    tx = type_fake(e->Ety);
	stmp = symbol_genauto(tx);
	eeq = el_bin(OPeq,e->Ety,el_var(stmp),e);
	if (e->Ety == TYstruct)
	{
	    eeq->Eoper = OPstreq;
	    eeq->Enumbytes = e->Enumbytes;
	}
	else if (e->Ety == TYarray)
	{
	    eeq->Eoper = OPstreq;
	    eeq->Ejty = eeq->Ety = TYstruct;
	    eeq->Enumbytes = t->size();
	}
	*pe = el_bin(OPcomma,e->Ety,eeq,el_var(stmp));
    }
    e = el_una(OPaddr,TYnptr,e);
    return e;
}

/*****************************************
 * Convert array to a pointer to the data.
 */

elem *array_toPtr(Type *t, elem *e)
{
    //printf("array_toPtr()\n");
    //elem_print(e);
    t = t->toBasetype();
    switch (t->ty)
    {
	case Tpointer:
	    break;

	case Tarray:
	case Tdelegate:
	    if (e->Eoper == OPcomma)
	    {
		e->Ety = TYnptr;
		e->E2 = array_toPtr(t, e->E2);
	    }
	    else if (e->Eoper == OPpair)
	    {
		e->Eoper = OPcomma;
		e->Ety = TYnptr;
	    }
	    else
	    {
#if 1
		e = el_una(OPmsw, TYnptr, e);
#else
		e = el_una(OPaddr, TYnptr, e);
		e = el_bin(OPadd, TYnptr, e, el_long(TYint, 4));
		e = el_una(OPind, TYnptr, e);
#endif
	    }
	    break;

	case Tsarray:
	    e = el_una(OPaddr, TYnptr, e);
	    break;

	default:
	    t->print();
	    assert(0);
    }
    return e;
}

/*****************************************
 * Convert array to a dynamic array.
 */

elem *array_toDarray(Type *t, elem *e)
{
    unsigned dim;
    elem *ef = NULL;
    elem *ex;

    //printf("array_toDarray(t = %s)\n", t->toChars());
    //elem_print(e);
    t = t->toBasetype();
    switch (t->ty)
    {
	case Tarray:
	    break;

	case Tsarray:
	    e = el_una(OPaddr, TYnptr, e);
	    dim = ((TypeSArray *)t)->dim->toInteger();
	    e = el_pair(TYullong, el_long(TYint, dim), e);
	    break;

	default:
	L1:
	    switch (e->Eoper)
	    {
		case OPconst:
		{
		    size_t len = tysize[tybasic(e->Ety)];
		    elem *es = el_calloc();
		    es->Eoper = OPstring;

		    // Match MEM_PH_FREE for OPstring in ztc\el.c
		    es->EV.ss.Vstring = (char *)mem_malloc(len);
		    memcpy(es->EV.ss.Vstring, &e->EV, len);

		    es->EV.ss.Vstrlen = len;
		    es->Ety = TYnptr;
		    e = es;
		    break;
		}

		case OPvar:
		    e = el_una(OPaddr, TYnptr, e);
		    break;

		case OPcomma:
		    ef = el_combine(ef, e->E1);
		    ex = e;
		    e = e->E2;
		    ex->E1 = NULL;
		    ex->E2 = NULL;
		    el_free(ex);
		    goto L1;

		case OPind:
		    ex = e;
		    e = e->E1;
		    ex->E1 = NULL;
		    ex->E2 = NULL;
		    el_free(ex);
		    break;

		default:
		{
		    // Copy expression to a variable and take the
		    // address of that variable.
		    Symbol *stmp;
		    tym_t ty = tybasic(e->Ety);

		    if (ty == TYstruct)
		    {
			if (e->Enumbytes == 4)
			    ty = TYint;
			else if (e->Enumbytes == 8)
			    ty = TYllong;
		    }
		    e->Ety = ty;
		    stmp = symbol_genauto(type_fake(ty));
		    e = el_bin(OPeq, e->Ety, el_var(stmp), e);
		    e = el_bin(OPcomma, TYnptr, e, el_una(OPaddr, TYnptr, el_var(stmp)));
		    break;
		}
	    }
	    dim = 1;
	    e = el_pair(TYullong, el_long(TYint, dim), e);
	    break;
    }
    return el_combine(ef, e);
}

/*****************************************
 * Evaluate elem and convert to dynamic array.
 */

elem *eval_Darray(IRState *irs, Expression *e)
{
    elem *ex;

    ex = e->toElem(irs);
    return array_toDarray(e->type, ex);
}

/************************************
 */

elem *sarray_toDarray(Loc loc, Type *tfrom, Type *tto, elem *e)
{
    //printf("sarray_toDarray()\n");
    //elem_print(e);

    elem *elen;
    unsigned dim = ((TypeSArray *)tfrom)->dim->toInteger();

    if (tto)
    {
	unsigned fsize = tfrom->nextOf()->size();
	unsigned tsize = tto->nextOf()->size();

	if ((dim * fsize) % tsize != 0)
	{
	  Lerr:
	    error(loc, "cannot cast %s to %s since sizes don't line up", tfrom->toChars(), tto->toChars());
	}
	dim = (dim * fsize) / tsize;
    }
  L1:
    elen = el_long(TYint, dim);
    e = el_una(OPaddr, TYnptr, e);
    e = el_pair(TYullong, elen, e);
    return e;
}

/*******************************************
 * Set an array pointed to by eptr to evalue:
 *	eptr[0..edim] = evalue;
 * Input:
 *	eptr	where to write the data to
 *	evalue	value to write
 *	edim	number of times to write evalue to eptr[]
 *	tb	type of evalue
 */

elem *setArray(elem *eptr, elem *edim, Type *tb, elem *evalue, IRState *irs, int op)
{   int r;
    elem *e;
    int sz = tb->size();

    if (tb->ty == Tfloat80 || tb->ty == Timaginary80)
	r = RTLSYM_MEMSET80;
    else if (tb->ty == Tcomplex80)
	r = RTLSYM_MEMSET160;
    else if (tb->ty == Tcomplex64)
	r = RTLSYM_MEMSET128;
    else
    {
	switch (sz)
	{
	    case 1:	 r = RTLSYM_MEMSET8;	break;
	    case 2:	 r = RTLSYM_MEMSET16;	break;
	    case 4:	 r = RTLSYM_MEMSET32;	break;
	    case 8:	 r = RTLSYM_MEMSET64;	break;
	    default:	 r = RTLSYM_MEMSETN;	break;
	}

	/* Determine if we need to do postblit
	 */
	if (op != TOKblit)
	{
	    Type *t = tb;
	    while (t->ty == Tsarray)
		t = t->nextOf()->toBasetype();
	    if (t->ty == Tstruct)
	    {   StructDeclaration *sd = ((TypeStruct *)t)->sym;
		if (sd->postblit)
		{   /* Need to do postblit.
		     *   void *_d_arraysetassign(void *p, void *value, int dim, TypeInfo ti);
		     */
		    r = (op == TOKconstruct) ? RTLSYM_ARRAYSETCTOR : RTLSYM_ARRAYSETASSIGN;
		    evalue = el_una(OPaddr, TYnptr, evalue);
		    Expression *ti = tb->getTypeInfo(NULL);
		    elem *eti = ti->toElem(irs);
		    e = el_params(eti, edim, evalue, eptr, NULL);
		    e = el_bin(OPcall,TYnptr,el_var(rtlsym[r]),e);
		    return e;
		}
	    }
	}

	if (r == RTLSYM_MEMSETN)
	{
	    // void *_memsetn(void *p, void *value, int dim, int sizelem)
	    evalue = el_una(OPaddr, TYnptr, evalue);
	    elem *esz = el_long(TYint, sz);
	    e = el_params(esz, edim, evalue, eptr, NULL);
	    e = el_bin(OPcall,TYnptr,el_var(rtlsym[r]),e);
	    return e;
	}
    }
    if (sz > 1 && sz <= 8 &&
	evalue->Eoper == OPconst && el_allbits(evalue, 0))
    {
	r = RTLSYM_MEMSET8;
	edim = el_bin(OPmul, TYuint, edim, el_long(TYuint, sz));
    }

    if (evalue->Ety == TYstruct)
    {
	evalue = el_una(OPstrpar, TYstruct, evalue);
	evalue->Enumbytes = evalue->E1->Enumbytes;
	assert(evalue->Enumbytes);
    }

    // Be careful about parameter side effect ordering
    if (r == RTLSYM_MEMSET8)
    {
	e = el_param(edim, evalue);
	e = el_bin(OPmemset,TYnptr,eptr,e);
    }
    else
    {
	e = el_params(edim, evalue, eptr, NULL);
	e = el_bin(OPcall,TYnptr,el_var(rtlsym[r]),e);
    }
    return e;
}

/***************************************
 */

elem *Expression::toElem(IRState *irs)
{
    print();
    assert(0);
    return NULL;
}

/************************************
 */

elem *SymbolExp::toElem(IRState *irs)
{   Symbol *s;
    elem *e;
    tym_t tym;
    Type *tb = (op == TOKsymoff) ? var->type->toBasetype() : type->toBasetype();
    int offset = (op == TOKsymoff) ? ((SymOffExp*)this)->offset : 0;
    FuncDeclaration *fd;
    VarDeclaration *v = var->isVarDeclaration();

    //printf("SymbolExp::toElem('%s') %p\n", toChars(), this);
    //printf("\tparent = '%s'\n", var->parent ? var->parent->toChars() : "null");
    if (op == TOKvar && var->needThis())
    {
	error("need 'this' to access member %s", toChars());
	return el_long(TYint, 0);
    }
    s = var->toSymbol();
    fd = NULL;
    if (var->toParent2())
	fd = var->toParent2()->isFuncDeclaration();

    int nrvo = 0;
    if (fd && fd->nrvo_can && fd->nrvo_var == var)
    {
	s = fd->shidden;
	nrvo = 1;
    }

    if (s->Sclass == SCauto || s->Sclass == SCparameter)
    {
	if (fd && fd != irs->getFunc())
	{   // 'var' is a variable in an enclosing function.
	    elem *ethis;
	    int soffset;

	    ethis = getEthis(loc, irs, fd);
	    ethis = el_una(OPaddr, TYnptr, ethis);

	    if (v && v->offset)
		soffset = v->offset;
	    else
	    {
		soffset = s->Soffset;
		/* If fd is a non-static member function of a class or struct,
		 * then ethis isn't the frame pointer.
		 * ethis is the 'this' pointer to the class/struct instance.
		 * We must offset it.
		 */
		if (fd->vthis)
		{
		    soffset -= fd->vthis->toSymbol()->Soffset;
		}
		//printf("\tSoffset = x%x, sthis->Soffset = x%x\n", s->Soffset, irs->sthis->Soffset);
	    }

	    if (!nrvo)
		soffset += offset;

	    e = el_bin(OPadd, TYnptr, ethis, el_long(TYnptr, soffset));
	    if (op == TOKvar)
		e = el_una(OPind, TYnptr, e);
	    if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
		e = el_una(OPind, s->ty(), e);
	    else if (op == TOKsymoff && nrvo)
            {   e = el_una(OPind, TYnptr, e);
                e = el_bin(OPadd, e->Ety, e, el_long(TYint, offset));
            }
	    goto L1;
	}
    }

    /* If var is a member of a closure
     */
    if (v && v->offset)
    {	assert(irs->sclosure);
	e = el_var(irs->sclosure);
	e = el_bin(OPadd, TYnptr, e, el_long(TYint, v->offset));
	if (op == TOKvar)
	{   e = el_una(OPind, type->totym(), e);
	    if (tybasic(e->Ety) == TYstruct)
		e->Enumbytes = type->size();
	    el_setLoc(e, loc);
	}
	if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
	{   e->Ety = TYnptr;
	    e = el_una(OPind, s->ty(), e);
	}
	else if (op == TOKsymoff && nrvo)
	{   e = el_una(OPind, TYnptr, e);
	    e = el_bin(OPadd, e->Ety, e, el_long(TYint, offset));
	}
	else if (op == TOKsymoff)
	{
	    e = el_bin(OPadd, e->Ety, e, el_long(TYint, offset));
	}
	goto L1;
    }

    if (s->Sclass == SCauto && s->Ssymnum == -1)
    {
	//printf("\tadding symbol\n");
	symbol_add(s);
    }

    if (var->isImportedSymbol())
    {
	assert(op == TOKvar);
	e = el_var(var->toImport());
	e = el_una(OPind,s->ty(),e);
    }
    else if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
    {	// Static arrays are really passed as pointers to the array
	// Out parameters are really references
	e = el_var(s);
	e->Ety = TYnptr;
	if (op == TOKvar)
	    e = el_una(OPind, s->ty(), e);
	else if (offset)
            e = el_bin(OPadd, TYnptr, e, el_long(TYint, offset));
    }
    else if (op == TOKvar)
	e = el_var(s);
    else
    {   e = nrvo ? el_var(s) : el_ptr(s);
        e = el_bin(OPadd, e->Ety, e, el_long(TYint, offset));
    }
L1:
    if (op == TOKvar)
    {
	if (nrvo)
	{
	    e->Ety = TYnptr;
	    e = el_una(OPind, 0, e);
	}
	if (tb->ty == Tfunction)
	{
	    tym = s->Stype->Tty;
	}
	else
	    tym = type->totym();
	e->Ejty = e->Ety = tym;
	if (tybasic(tym) == TYstruct)
	{
	    e->Enumbytes = type->size();
	}
	else if (tybasic(tym) == TYarray)
	{
	    e->Ejty = e->Ety = TYstruct;
	    e->Enumbytes = type->size();
	}
    }
    el_setLoc(e,loc);
    return e;
}

#if 0
elem *VarExp::toElem(IRState *irs)
{   Symbol *s;
    elem *e;
    tym_t tym;
    Type *tb = type->toBasetype();
    FuncDeclaration *fd;
    VarDeclaration *v = var->isVarDeclaration();

    //printf("VarExp::toElem('%s') %p\n", toChars(), this);
    //printf("\tparent = '%s'\n", var->parent ? var->parent->toChars() : "null");
    if (var->needThis())
    {
	error("need 'this' to access member %s", toChars());
	return el_long(TYint, 0);
    }
    s = var->toSymbol();
    fd = NULL;
    if (var->toParent2())
	fd = var->toParent2()->isFuncDeclaration();

    int nrvo = 0;
    if (fd && fd->nrvo_can && fd->nrvo_var == var)
    {
	s = fd->shidden;
	nrvo = 1;
    }

    if (s->Sclass == SCauto || s->Sclass == SCparameter)
    {
	if (fd && fd != irs->getFunc())
	{   // 'var' is a variable in an enclosing function.
	    elem *ethis;
	    int soffset;

	    ethis = getEthis(loc, irs, fd);
	    ethis = el_una(OPaddr, TYnptr, ethis);

	    if (v && v->offset)
		soffset = v->offset;
	    else
	    {
		soffset = s->Soffset;
		/* If fd is a non-static member function of a class or struct,
		 * then ethis isn't the frame pointer.
		 * ethis is the 'this' pointer to the class/struct instance.
		 * We must offset it.
		 */
		if (fd->vthis)
		{
		    soffset -= fd->vthis->toSymbol()->Soffset;
		}
		//printf("\tSoffset = x%x, sthis->Soffset = x%x\n", s->Soffset, irs->sthis->Soffset);
	    }

	    ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYnptr, soffset));
	    e = el_una(OPind, 0, ethis);
	    if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
		goto L2;
	    goto L1;
	}
    }

    /* If var is a member of a closure
     */
    if (v && v->offset)
    {	assert(irs->sclosure);
	e = el_var(irs->sclosure);
	e = el_bin(OPadd, TYnptr, e, el_long(TYint, v->offset));
	e = el_una(OPind, type->totym(), e);
	if (tybasic(e->Ety) == TYstruct)
	    e->Enumbytes = type->size();
	el_setLoc(e, loc);

	if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
	    goto L2;
	goto L1;
    }

    if (s->Sclass == SCauto && s->Ssymnum == -1)
    {
	//printf("\tadding symbol\n");
	symbol_add(s);
    }

    if (var->isImportedSymbol())
    {
	e = el_var(var->toImport());
	e = el_una(OPind,s->ty(),e);
    }
    else if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
    {	// Static arrays are really passed as pointers to the array
	// Out parameters are really references
	e = el_var(s);
L2:
	e->Ety = TYnptr;
	e = el_una(OPind, s->ty(), e);
    }
    else
	e = el_var(s);
L1:
    if (nrvo)
    {
	e->Ety = TYnptr;
	e = el_una(OPind, 0, e);
    }
    if (tb->ty == Tfunction)
    {
	tym = s->Stype->Tty;
    }
    else
	tym = type->totym();
    e->Ejty = e->Ety = tym;
    if (tybasic(tym) == TYstruct)
    {
	e->Enumbytes = type->size();
    }
    else if (tybasic(tym) == TYarray)
    {
	e->Ejty = e->Ety = TYstruct;
	e->Enumbytes = type->size();
    }
    el_setLoc(e,loc);
    return e;
}
#endif

#if 0
elem *SymOffExp::toElem(IRState *irs)
{   Symbol *s;
    elem *e;
    Type *tb = var->type->toBasetype();
    VarDeclaration *v = var->isVarDeclaration();
    FuncDeclaration *fd = NULL;
    if (var->toParent2())
	fd = var->toParent2()->isFuncDeclaration();

    //printf("SymOffExp::toElem(): %s\n", toChars());
    s = var->toSymbol();

    int nrvo = 0;
    if (fd && fd->nrvo_can && fd->nrvo_var == var)
    { 	s = fd->shidden;
	nrvo = 1;
    }

    if (s->Sclass == SCauto && s->Ssymnum == -1)
	symbol_add(s);
    assert(!var->isImportedSymbol());

    // This code closely parallels that in VarExp::toElem()
    if (s->Sclass == SCauto || s->Sclass == SCparameter)
    {
	if (fd && fd != irs->getFunc())
	{   // 'var' is a variable in an enclosing function.
	    elem *ethis;
	    int soffset;

	    ethis = getEthis(loc, irs, fd);
	    ethis = el_una(OPaddr, TYnptr, ethis);

	    if (v && v->offset)
		soffset = v->offset;
	    else
	    {
		soffset = s->Soffset;
		/* If fd is a non-static member function of a class or struct,
		 * then ethis isn't the frame pointer.
		 * ethis is the 'this' pointer to the class/struct instance.
		 * We must offset it.
		 */
		if (fd->vthis)
		{
		    soffset -= fd->vthis->toSymbol()->Soffset;
		}
		//printf("\tSoffset = x%x, sthis->Soffset = x%x\n", s->Soffset, irs->sthis->Soffset);
	    }

	    if (!nrvo)
		soffset += offset;
	    e = el_bin(OPadd, TYnptr, ethis, el_long(TYnptr, soffset));
	    if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
		e = el_una(OPind, s->ty(), e);
	    else if (nrvo)
	    {	e = el_una(OPind, TYnptr, e);
		e = el_bin(OPadd, e->Ety, e, el_long(TYint, offset));
	    }
	    goto L1;
	}
    }

    /* If var is a member of a closure
     */
    if (v && v->offset)
    {	assert(irs->sclosure);
	e = el_var(irs->sclosure);
	e = el_bin(OPadd, TYnptr, e, el_long(TYint, v->offset));
	if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
	    e = el_una(OPind, s->ty(), e);
	else if (nrvo)
	{   e = el_una(OPind, TYnptr, e);
	    e = el_bin(OPadd, e->Ety, e, el_long(TYint, offset));
	}
	goto L1;
    }

    if ((var->isParameter() && tb->ty == Tsarray) || var->isOut() || var->isRef())
    {   // Static arrays are really passed as pointers to the array
        // Out parameters are really references
        e = el_var(s);
        e->Ety = TYnptr;
	if (offset)
	    e = el_bin(OPadd, TYnptr, e, el_long(TYint, offset));
    }
    else
    {	e = nrvo ? el_var(s) : el_ptr(s);
	e = el_bin(OPadd, e->Ety, e, el_long(TYint, offset));
    }

L1:
    el_setLoc(e,loc);
    return e;
}
#endif

/**************************************
 */

elem *FuncExp::toElem(IRState *irs)
{
    elem *e;
    Symbol *s;

    //printf("FuncExp::toElem() %s\n", toChars());
    s = fd->toSymbol();
    e = el_ptr(s);
    if (fd->isNested())
    {
	elem *ethis = getEthis(loc, irs, fd);
	e = el_pair(TYullong, ethis, e);
    }

    irs->deferToObj->push(fd);
    el_setLoc(e,loc);
    return e;
}

/**************************************
 */

elem *Dsymbol_toElem(Dsymbol *s, IRState *irs)
{
    elem *e = NULL;
    Symbol *sp;
    AttribDeclaration *ad;
    VarDeclaration *vd;
    ClassDeclaration *cd;
    StructDeclaration *sd;
    FuncDeclaration *fd;
    TemplateMixin *tm;
    TupleDeclaration *td;
    TypedefDeclaration *tyd;

    //printf("Dsymbol_toElem() %s\n", s->toChars());
    ad = s->isAttribDeclaration();
    if (ad)
    {
	Array *decl = ad->include(NULL, NULL);
	if (decl && decl->dim)
	{
	    for (size_t i = 0; i < decl->dim; i++)
	    {
		s = (Dsymbol *)decl->data[i];
		e = el_combine(e, Dsymbol_toElem(s, irs));
	    }
	}
    }
    else if ((vd = s->isVarDeclaration()) != NULL)
    {
	s = s->toAlias();
	if (s != vd)
	    return Dsymbol_toElem(s, irs);
	if (vd->isStatic() || vd->storage_class & STCextern)
	    vd->toObjFile(0);
	else
	{
	    sp = s->toSymbol();
	    symbol_add(sp);
	    //printf("\tadding symbol '%s'\n", sp->Sident);
	    if (vd->init)
	    {
		ExpInitializer *ie;

		ie = vd->init->isExpInitializer();
		if (ie)
		    e = ie->exp->toElem(irs);
	    }
	}
    }
    else if ((cd = s->isClassDeclaration()) != NULL)
    {
	irs->deferToObj->push(s);
    }
    else if ((sd = s->isStructDeclaration()) != NULL)
    {
	irs->deferToObj->push(sd);
    }
    else if ((fd = s->isFuncDeclaration()) != NULL)
    {
	//printf("function %s\n", fd->toChars());
	irs->deferToObj->push(fd);
    }
    else if ((tm = s->isTemplateMixin()) != NULL)
    {
	//printf("%s\n", tm->toChars());
	if (tm->members)
	{
	    for (size_t i = 0; i < tm->members->dim; i++)
	    {
		Dsymbol *sm = (Dsymbol *)tm->members->data[i];
		e = el_combine(e, Dsymbol_toElem(sm, irs));
	    }
	}
    }
    else if ((td = s->isTupleDeclaration()) != NULL)
    {
	for (size_t i = 0; i < td->objects->dim; i++)
	{   Object *o = (Object *)td->objects->data[i];
	    if (o->dyncast() == DYNCAST_EXPRESSION)
	    {	Expression *eo = (Expression *)o;
		if (eo->op == TOKdsymbol)
		{   DsymbolExp *se = (DsymbolExp *)eo;
		    e = el_combine(e, Dsymbol_toElem(se->s, irs));
		}
	    }
	}
    }
    else if ((tyd = s->isTypedefDeclaration()) != NULL)
    {
	irs->deferToObj->push(tyd);
    }
    return e;
}

elem *DeclarationExp::toElem(IRState *irs)
{   elem *e;

    //printf("DeclarationExp::toElem() %s\n", toChars());
    e = Dsymbol_toElem(declaration, irs);
    return e;
}

/***************************************
 */

elem *ThisExp::toElem(IRState *irs)
{   elem *ethis;
    FuncDeclaration *fd;

    //printf("ThisExp::toElem()\n");
    assert(irs->sthis);

    if (var)
    {
	assert(var->parent);
	fd = var->toParent2()->isFuncDeclaration();
	assert(fd);
	ethis = getEthis(loc, irs, fd);
    }
    else
	ethis = el_var(irs->sthis);

    el_setLoc(ethis,loc);
    return ethis;
}

/***************************************
 */

elem *IntegerExp::toElem(IRState *irs)
{   elem *e;

    e = el_long(type->totym(), value);
    el_setLoc(e,loc);
    return e;
}

/***************************************
 */

elem *RealExp::toElem(IRState *irs)
{   union eve c;
    tym_t ty;

    //printf("RealExp::toElem(%p)\n", this);
    memset(&c, 0, sizeof(c));
    ty = type->toBasetype()->totym();
    switch (ty)
    {
	case TYfloat:
	case TYifloat:
	    c.Vfloat = value;
	    break;

	case TYdouble:
	case TYidouble:
	    c.Vdouble = value;
	    break;

	case TYldouble:
	case TYildouble:
	    c.Vldouble = value;
	    break;

	default:
	    print();
	    type->print();
	    type->toBasetype()->print();
	    printf("ty = %d, tym = %x\n", type->ty, ty);
	    assert(0);
    }
    return el_const(ty, &c);
}


/***************************************
 */

elem *ComplexExp::toElem(IRState *irs)
{   union eve c;
    tym_t ty;
    real_t re;
    real_t im;

    re = creall(value);
    im = cimagl(value);

    memset(&c, 0, sizeof(c));
    ty = type->totym();
    switch (ty)
    {
	case TYcfloat:
	    c.Vcfloat.re = (float) re;
	    c.Vcfloat.im = (float) im;
	    break;

	case TYcdouble:
	    c.Vcdouble.re = (double) re;
	    c.Vcdouble.im = (double) im;
	    break;

	case TYcldouble:
	    c.Vcldouble.re = re;
	    c.Vcldouble.im = im;
	    break;

	default:
	    assert(0);
    }
    return el_const(ty, &c);
}

/***************************************
 */

elem *NullExp::toElem(IRState *irs)
{
    return el_long(type->totym(), 0);
}

/***************************************
 */

struct StringTab
{
    Module *m;		// module we're generating code for
    Symbol *si;
    void *string;
    size_t sz;
    size_t len;
};

#define STSIZE 16
StringTab stringTab[STSIZE];
size_t stidx;

static Symbol *assertexp_sfilename = NULL;
static char *assertexp_name = NULL;
static Module *assertexp_mn = NULL;

void clearStringTab()
{
    //printf("clearStringTab()\n");
    memset(stringTab, 0, sizeof(stringTab));
    stidx = 0;

    assertexp_sfilename = NULL;
    assertexp_name = NULL;
    assertexp_mn = NULL;
}

elem *StringExp::toElem(IRState *irs)
{
    elem *e;
    Type *tb= type->toBasetype();


#if 0
    printf("StringExp::toElem() %s, type = %s\n", toChars(), type->toChars());
#endif

    if (tb->ty == Tarray)
    {
	Symbol *si;
	dt_t *dt;
	StringTab *st;

#if 0
	printf("irs->m = %p\n", irs->m);
	printf(" m   = %s\n", irs->m->toChars());
	printf(" len = %d\n", len);
	printf(" sz  = %d\n", sz);
#endif
	for (size_t i = 0; i < STSIZE; i++)
	{
	    st = &stringTab[(stidx + i) % STSIZE];
	    //if (!st->m) continue;
	    //printf(" st.m   = %s\n", st->m->toChars());
	    //printf(" st.len = %d\n", st->len);
	    //printf(" st.sz  = %d\n", st->sz);
	    if (st->m == irs->m &&
		st->si &&
		st->len == len &&
		st->sz == sz &&
		memcmp(st->string, string, sz * len) == 0)
	    {
		//printf("use cached value\n");
		si = st->si;	// use cached value
		goto L1;
	    }
	}

	stidx = (stidx + 1) % STSIZE;
	st = &stringTab[stidx];

	dt = NULL;
	toDt(&dt);

	si = symbol_generate(SCstatic,type_fake(TYdarray));
	si->Sdt = dt;
	si->Sfl = FLdata;
#if ELFOBJ // Burton
	si->Sseg = CDATA;
#endif
	outdata(si);

	st->m = irs->m;
	st->si = si;
	st->string = string;
	st->len = len;
	st->sz = sz;
    L1:
	e = el_var(si);
    }
    else if (tb->ty == Tsarray)
    {
	Symbol *si;
	dt_t *dt = NULL;

	toDt(&dt);
	dtnzeros(&dt, sz);		// leave terminating 0

	si = symbol_generate(SCstatic,type_allocn(TYarray, tschar));
	si->Sdt = dt;
	si->Sfl = FLdata;

#if ELFOBJ // Burton
	si->Sseg = CDATA;
#endif
	outdata(si);

	e = el_var(si);
    }
    else if (tb->ty == Tpointer)
    {
	e = el_calloc();
	e->Eoper = OPstring;
#if 1
	// Match MEM_PH_FREE for OPstring in ztc\el.c
	e->EV.ss.Vstring = (char *)mem_malloc((len + 1) * sz);
	memcpy(e->EV.ss.Vstring, string, (len + 1) * sz);
#else
	e->EV.ss.Vstring = (char *)string;
#endif
	e->EV.ss.Vstrlen = (len + 1) * sz;
	e->Ety = TYnptr;
    }
    else
    {
	printf("type is %s\n", type->toChars());
	assert(0);
    }
    el_setLoc(e,loc);
    return e;
}

elem *NewExp::toElem(IRState *irs)
{   elem *e;
    Type *t;
    Type *ectype;

    //printf("NewExp::toElem() %s\n", toChars());
    t = type->toBasetype();
    //printf("\ttype = %s\n", t->toChars());
    //if (member)
	//printf("\tmember = %s\n", member->toChars());
    if (t->ty == Tclass)
    {
	Symbol *csym;

	t = newtype->toBasetype();
	assert(t->ty == Tclass);
	TypeClass *tclass = (TypeClass *)(t);
	ClassDeclaration *cd = tclass->sym;

	/* Things to do:
	 * 1) ex: call allocator
	 * 2) ey: set vthis for nested classes
	 * 3) ez: call constructor
	 */

	elem *ex = NULL;
	elem *ey = NULL;
	elem *ez = NULL;

	if (allocator || onstack)
	{   elem *ei;
	    Symbol *si;

	    if (onstack)
	    {
		/* Create an instance of the class on the stack,
		 * and call it stmp.
		 * Set ex to be the &stmp.
		 */
		Symbol *s = symbol_calloc(tclass->sym->toChars());
		s->Sclass = SCstruct;
		s->Sstruct = struct_calloc();
		s->Sstruct->Sflags |= 0;
		s->Sstruct->Salignsize = tclass->sym->alignsize;
		s->Sstruct->Sstructalign = tclass->sym->structalign;
		s->Sstruct->Sstructsize = tclass->sym->structsize;

		::type *tc = type_alloc(TYstruct);
		tc->Ttag = (Classsym *)s;                // structure tag name
		tc->Tcount++;
		s->Stype = tc;

		Symbol *stmp = symbol_genauto(tc);
		ex = el_ptr(stmp);
	    }
	    else
	    {
		ex = el_var(allocator->toSymbol());
		ex = callfunc(loc, irs, 1, type, ex, allocator->type,
			allocator, allocator->type, NULL, newargs);
	    }

	    si = tclass->sym->toInitializer();
	    ei = el_var(si);

	    if (cd->isNested())
	    {
		ey = el_same(&ex);
		ez = el_copytree(ey);
	    }
	    else if (member)
		ez = el_same(&ex);

	    ex = el_una(OPind, TYstruct, ex);
	    ex = el_bin(OPstreq, TYnptr, ex, ei);
	    ex->Enumbytes = cd->size(loc);
	    ex = el_una(OPaddr, TYnptr, ex);
	    ectype = tclass;
	}
	else
	{
	    csym = cd->toSymbol();
	    ex = el_bin(OPcall,TYnptr,el_var(rtlsym[RTLSYM_NEWCLASS]),el_ptr(csym));
	    ectype = NULL;

	    if (cd->isNested())
	    {
		ey = el_same(&ex);
		ez = el_copytree(ey);
	    }
	    else if (member)
		ez = el_same(&ex);
//elem_print(ex);
//elem_print(ey);
//elem_print(ez);
	}

	if (thisexp)
	{   ClassDeclaration *cdthis = thisexp->type->isClassHandle();
	    assert(cdthis);
	    //printf("cd = %s\n", cd->toChars());
	    //printf("cdthis = %s\n", cdthis->toChars());
	    assert(cd->isNested());
	    int offset = 0;
	    Dsymbol *cdp = cd->toParent2();	// class we're nested in
	    elem *ethis;

//printf("member = %p\n", member);
//printf("cdp = %s\n", cdp->toChars());
//printf("cdthis = %s\n", cdthis->toChars());
	    if (cdp != cdthis)
	    {	int i = cdp->isClassDeclaration()->isBaseOf(cdthis, &offset);
		assert(i);
	    }
	    ethis = thisexp->toElem(irs);
	    if (offset)
		ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYint, offset));

	    ey = el_bin(OPadd, TYnptr, ey, el_long(TYint, cd->vthis->offset));
	    ey = el_una(OPind, TYnptr, ey);
	    ey = el_bin(OPeq, TYnptr, ey, ethis);

//printf("ex: "); elem_print(ex);
//printf("ey: "); elem_print(ey);
//printf("ez: "); elem_print(ez);
	}
	else if (cd->isNested())
	{   /* Initialize cd->vthis:
	     *	*(ey + cd.vthis.offset) = this;
	     */
	    elem *ethis;
	    FuncDeclaration *thisfd = irs->getFunc();
	    int offset = 0;
	    Dsymbol *cdp = cd->toParent2();	// class/func we're nested in

	    if (cdp == thisfd)
	    {	/* Class we're new'ing is a local class in this function:
		 *	void thisfd() { class cd { } }
		 */
		if (irs->sclosure)
		    ethis = el_var(irs->sclosure);
		else if (irs->sthis)
		{
#if DMDV2
		    if (thisfd->closureVars.dim)
#else
		    if (thisfd->nestedFrameRef)
#endif
			ethis = el_ptr(irs->sthis);
		    else
			ethis = el_var(irs->sthis);
		}
		else
		{
		    ethis = el_long(TYnptr, 0);
#if DMDV2
		    if (thisfd->closureVars.dim)
#else
		    if (thisfd->nestedFrameRef)
#endif
			ethis->Eoper = OPframeptr;
		}
	    }
	    else if (thisfd->vthis &&
		  (cdp == thisfd->toParent2() ||
		   (cdp->isClassDeclaration() &&
		    cdp->isClassDeclaration()->isBaseOf(thisfd->toParent2()->isClassDeclaration(), &offset)
		   )
		  )
		)
	    {	/* Class we're new'ing is at the same level as thisfd
		 */
		assert(offset == 0);	// BUG: should handle this case
		ethis = el_var(irs->sthis);
	    }
	    else
	    {
		ethis = getEthis(loc, irs, cd->toParent2());
		ethis = el_una(OPaddr, TYnptr, ethis);
	    }

	    ey = el_bin(OPadd, TYnptr, ey, el_long(TYint, cd->vthis->offset));
	    ey = el_una(OPind, TYnptr, ey);
	    ey = el_bin(OPeq, TYnptr, ey, ethis);

	}

	if (member)
	    // Call constructor
	    ez = callfunc(loc, irs, 1, type, ez, ectype, member, member->type, NULL, arguments);

	e = el_combine(ex, ey);
	e = el_combine(e, ez);
    }
    else if (t->ty == Tpointer && t->nextOf()->toBasetype()->ty == Tstruct)
    {
	Symbol *csym;

	t = newtype->toBasetype();
	assert(t->ty == Tstruct);
	TypeStruct *tclass = (TypeStruct *)(t);
	StructDeclaration *cd = tclass->sym;

	/* Things to do:
	 * 1) ex: call allocator
	 * 2) ey: set vthis for nested classes
	 * 3) ez: call constructor
	 */

	elem *ex = NULL;
	elem *ey = NULL;
	elem *ez = NULL;

	if (allocator)
	{   elem *ei;
	    Symbol *si;

	    ex = el_var(allocator->toSymbol());
	    ex = callfunc(loc, irs, 1, type, ex, allocator->type,
			allocator, allocator->type, NULL, newargs);

	    si = tclass->sym->toInitializer();
	    ei = el_var(si);

	    if (member)
		ez = el_same(&ex);
	    else
	    {	/* Statically intialize with default initializer
		 */
		ex = el_una(OPind, TYstruct, ex);
		ex = el_bin(OPstreq, TYnptr, ex, ei);
		ex->Enumbytes = cd->size(loc);
		ex = el_una(OPaddr, TYnptr, ex);
	    }
	    ectype = tclass;
	}
	else
	{
	    d_uns64 elemsize = cd->size(loc);

	    // call _d_newarrayT(ti, 1)
	    e = el_long(TYsize_t, 1);
	    e = el_param(e, type->getTypeInfo(NULL)->toElem(irs));

	    int rtl = t->isZeroInit() ? RTLSYM_NEWARRAYT : RTLSYM_NEWARRAYIT;
	    e = el_bin(OPcall,TYdarray,el_var(rtlsym[rtl]),e);

	    // The new functions return an array, so convert to a pointer
	    // ex -> (unsigned)(e >> 32)
	    e = el_bin(OPshr, TYdarray, e, el_long(TYint, 32));
	    ex = el_una(OP64_32, TYnptr, e);

	    ectype = NULL;

	    if (member)
		ez = el_same(&ex);
//elem_print(ex);
//elem_print(ey);
//elem_print(ez);
	}

	if (member)
	    // Call constructor
	    ez = callfunc(loc, irs, 1, type, ez, ectype, member, member->type, NULL, arguments);

	e = el_combine(ex, ey);
	e = el_combine(e, ez);
    }
    else if (t->ty == Tarray)
    {
	TypeDArray *tda = (TypeDArray *)(t);

	assert(arguments && arguments->dim >= 1);
	if (arguments->dim == 1)
	{   // Single dimension array allocations
	    Expression *arg = (Expression *)arguments->data[0];	// gives array length
	    e = arg->toElem(irs);
	    d_uns64 elemsize = tda->next->size();

	    // call _d_newT(ti, arg)
	    e = el_param(e, type->getTypeInfo(NULL)->toElem(irs));
	    int rtl = tda->next->isZeroInit() ? RTLSYM_NEWARRAYT : RTLSYM_NEWARRAYIT;
	    e = el_bin(OPcall,TYdarray,el_var(rtlsym[rtl]),e);
	}
	else
	{   // Multidimensional array allocations
	    e = el_long(TYint, arguments->dim);
	    for (size_t i = 0; i < arguments->dim; i++)
	    {
		Expression *arg = (Expression *)arguments->data[i];	// gives array length
		e = el_param(arg->toElem(irs), e);
		assert(t->ty == Tarray);
		t = t->nextOf();
		assert(t);
	    }

	    e = el_param(e, type->getTypeInfo(NULL)->toElem(irs));

	    int rtl = t->isZeroInit() ? RTLSYM_NEWARRAYMT : RTLSYM_NEWARRAYMIT;
	    e = el_bin(OPcall,TYdarray,el_var(rtlsym[rtl]),e);
	}
    }
    else if (t->ty == Tpointer)
    {
	TypePointer *tp = (TypePointer *)t;
	d_uns64 elemsize = tp->next->size();
	Expression *di = tp->next->defaultInit();
	d_uns64 disize = di->type->size();

	// call _d_newarrayT(ti, 1)
	e = el_long(TYsize_t, 1);
	e = el_param(e, type->getTypeInfo(NULL)->toElem(irs));

	int rtl = tp->next->isZeroInit() ? RTLSYM_NEWARRAYT : RTLSYM_NEWARRAYIT;
	e = el_bin(OPcall,TYdarray,el_var(rtlsym[rtl]),e);

	// The new functions return an array, so convert to a pointer
	// e -> (unsigned)(e >> 32)
	e = el_bin(OPshr, TYdarray, e, el_long(TYint, 32));
	e = el_una(OP64_32, t->totym(), e);
    }
    else
    {
	assert(0);
    }

    el_setLoc(e,loc);
    return e;
}

//////////////////////////// Unary ///////////////////////////////

/***************************************
 */

elem *NegExp::toElem(IRState *irs)
{
    elem *e = el_una(OPneg, type->totym(), e1->toElem(irs));
    el_setLoc(e,loc);
    return e;
}

/***************************************
 */

elem *ComExp::toElem(IRState *irs)
{   elem *e;

    elem *e1 = this->e1->toElem(irs);
    tym_t ty = type->totym();
    if (this->e1->type->toBasetype()->ty == Tbool)
	e = el_bin(OPxor, ty, e1, el_long(ty, 1));
    else
	e = el_una(OPcom,ty,e1);
    el_setLoc(e,loc);
    return e;
}

/***************************************
 */

elem *NotExp::toElem(IRState *irs)
{
    elem *e = el_una(OPnot, type->totym(), e1->toElem(irs));
    el_setLoc(e,loc);
    return e;
}


/***************************************
 */

elem *HaltExp::toElem(IRState *irs)
{   elem *e;

    e = el_calloc();
    e->Ety = TYvoid;
    e->Eoper = OPhalt;
    el_setLoc(e,loc);
    return e;
}

/********************************************
 */

elem *AssertExp::toElem(IRState *irs)
{   elem *e;
    elem *ea;
    Type *t1 = e1->type->toBasetype();

    //printf("AssertExp::toElem() %s\n", toChars());
    if (global.params.useAssert)
    {
	e = e1->toElem(irs);

	InvariantDeclaration *inv = (InvariantDeclaration *)(void *)1;

	// If e1 is a class object, call the class invariant on it
	if (global.params.useInvariants && t1->ty == Tclass &&
	    !((TypeClass *)t1)->sym->isInterfaceDeclaration())
	{
#if TARGET_LINUX
	    e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM__DINVARIANT]), e);
#else
	    e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DINVARIANT]), e);
#endif
	}
	// If e1 is a struct object, call the struct invariant on it
	else if (global.params.useInvariants &&
	    t1->ty == Tpointer &&
	    t1->nextOf()->ty == Tstruct &&
	    (inv = ((TypeStruct *)t1->nextOf())->sym->inv) != NULL)
	{
	    e = callfunc(loc, irs, 1, inv->type->nextOf(), e, e1->type, inv, inv->type, NULL, NULL);
	}
	else
	{
	    // Construct: (e1 || ModuleAssert(line))
	    Symbol *sassert;
	    Module *m = irs->blx->module;
	    char *mname = m->srcfile->toChars();

	    //printf("filename = '%s'\n", loc.filename);
	    //printf("module = '%s'\n", m->srcfile->toChars());

	    /* If the source file name has changed, probably due
	     * to a #line directive.
	     */
	    if (loc.filename && (msg || strcmp(loc.filename, mname) != 0))
	    {	elem *efilename;

		/* Cache values.
		 */
		//static Symbol *assertexp_sfilename = NULL;
		//static char *assertexp_name = NULL;
		//static Module *assertexp_mn = NULL;

		if (!assertexp_sfilename || strcmp(loc.filename, assertexp_name) != 0 || assertexp_mn != m)
		{
		    dt_t *dt = NULL;
		    char *id;
		    int len;

		    id = loc.filename;
		    len = strlen(id);
		    dtdword(&dt, len);
		    dtabytes(&dt,TYnptr, 0, len + 1, id);

		    assertexp_sfilename = symbol_generate(SCstatic,type_fake(TYdarray));
		    assertexp_sfilename->Sdt = dt;
		    assertexp_sfilename->Sfl = FLdata;
#if ELFOBJ
		    assertexp_sfilename->Sseg = CDATA;
#endif
		    outdata(assertexp_sfilename);

		    assertexp_mn = m;
		    assertexp_name = id;
		}

		efilename = el_var(assertexp_sfilename);

		if (msg)
		{   elem *emsg = msg->toElem(irs);
		    ea = el_var(rtlsym[RTLSYM_DASSERT_MSG]);
		    ea = el_bin(OPcall, TYvoid, ea, el_params(el_long(TYint, loc.linnum), efilename, emsg, NULL));
		}
		else
		{
		    ea = el_var(rtlsym[RTLSYM_DASSERT]);
		    ea = el_bin(OPcall, TYvoid, ea, el_param(el_long(TYint, loc.linnum), efilename));
		}
	    }
	    else
	    {
		sassert = m->toModuleAssert();
		ea = el_bin(OPcall,TYvoid,el_var(sassert),
		    el_long(TYint, loc.linnum));
	    }
	    e = el_bin(OPoror,TYvoid,e,ea);
	}
    }
    else
    {	// BUG: should replace assert(0); with a HLT instruction
	e = el_long(TYint, 0);
    }
    el_setLoc(e,loc);
    return e;
}

elem *PostExp::toElem(IRState *irs)
{   elem *e;
    elem *einc;

    e = e1->toElem(irs);
    einc = e2->toElem(irs);
    e = el_bin((op == TOKplusplus) ? OPpostinc : OPpostdec,
		e->Ety,e,einc);
    el_setLoc(e,loc);
    return e;
}

//////////////////////////// Binary ///////////////////////////////

/********************************************
 */

elem *BinExp::toElemBin(IRState *irs,int op)
{
    //printf("toElemBin() '%s'\n", toChars());

    tym_t tym = type->totym();

    elem *el = e1->toElem(irs);
    elem *er = e2->toElem(irs);
    elem *e = el_bin(op,tym,el,er);
    el_setLoc(e,loc);
    return e;
}


/***************************************
 */

elem *AddExp::toElem(IRState *irs)
{   elem *e;
    Type *tb1 = e1->type->toBasetype();
    Type *tb2 = e2->type->toBasetype();

    if ((tb1->ty == Tarray || tb1->ty == Tsarray) &&
	(tb2->ty == Tarray || tb2->ty == Tsarray)
       )
    {
	error("Array operation %s not implemented", toChars());
    }
    else
	e = toElemBin(irs,OPadd);
    return e;
}

/***************************************
 */

elem *MinExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPmin);
}

/***************************************
 */

elem *CatExp::toElem(IRState *irs)
{   elem *e;

#if 0
    printf("CatExp::toElem()\n");
    print();
#endif

    Type *tb1 = e1->type->toBasetype();
    Type *tb2 = e2->type->toBasetype();
    Type *tn;

#if 0
    if ((tb1->ty == Tarray || tb1->ty == Tsarray) &&
	(tb2->ty == Tarray || tb2->ty == Tsarray)
       )
#endif

    Type *ta = tb1->nextOf() ? e1->type : e2->type;
    tn = tb1->nextOf() ? tb1->nextOf() : tb2->nextOf();
    {
	if (e1->op == TOKcat)
	{
	    elem *ep;
	    CatExp *ce = this;
	    int n = 2;

	    ep = eval_Darray(irs, ce->e2);
	    do
	    {
		n++;
		ce = (CatExp *)ce->e1;
		ep = el_param(ep, eval_Darray(irs, ce->e2));
	    } while (ce->e1->op == TOKcat);
	    ep = el_param(ep, eval_Darray(irs, ce->e1));
#if 1
	    ep = el_params(
			   ep,
			   el_long(TYint, n),
			   ta->getTypeInfo(NULL)->toElem(irs),
			   NULL);
	    e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYCATNT]), ep);
#else
	    ep = el_params(
			   ep,
			   el_long(TYint, n),
			   el_long(TYint, tn->size()),
			   NULL);
	    e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYCATN]), ep);
#endif
	}
	else
	{
	    elem *e1;
	    elem *e2;
	    elem *ep;

	    e1 = eval_Darray(irs, this->e1);
	    e2 = eval_Darray(irs, this->e2);
#if 1
	    ep = el_params(e2, e1, ta->getTypeInfo(NULL)->toElem(irs), NULL);
	    e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYCATT]), ep);
#else
	    ep = el_params(el_long(TYint, tn->size()), e2, e1, NULL);
	    e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYCAT]), ep);
#endif
	}
	el_setLoc(e,loc);
    }
#if 0
    else if ((tb1->ty == Tarray || tb1->ty == Tsarray) &&
	     e2->type->equals(tb1->next))
    {
	error("array cat with element not implemented");
	e = el_long(TYint, 0);
    }
    else
	assert(0);
#endif
    return e;
}

/***************************************
 */

elem *MulExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPmul);
}

/************************************
 */

elem *DivExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPdiv);
}

/***************************************
 */

elem *ModExp::toElem(IRState *irs)
{
    elem *e;
    elem *e1;
    elem *e2;
    tym_t tym;

    tym = type->totym();

    e1 = this->e1->toElem(irs);
    e2 = this->e2->toElem(irs);

#if 0 // Now inlined
    if (this->e1->type->isfloating())
    {	elem *ep;

	switch (this->e1->type->ty)
	{
	    case Tfloat32:
	    case Timaginary32:
		e1 = el_una(OPf_d, TYdouble, e1);
		e2 = el_una(OPf_d, TYdouble, e2);
	    case Tfloat64:
	    case Timaginary64:
		e1 = el_una(OPd_ld, TYldouble, e1);
		e2 = el_una(OPd_ld, TYldouble, e2);
		break;
	    case Tfloat80:
	    case Timaginary80:
		break;
	    default:
		assert(0);
		break;
	}
	ep = el_param(e2,e1);
	e = el_bin(OPcall,tym,el_var(rtlsym[RTLSYM_MODULO]),ep);
    }
    else
#endif
	e = el_bin(OPmod,tym,e1,e2);
    el_setLoc(e,loc);
    return e;
}

/***************************************
 */

elem *CmpExp::toElem(IRState *irs)
{
    elem *e;
    enum OPER eop;
    Type *t1 = e1->type->toBasetype();
    Type *t2 = e2->type->toBasetype();

    switch (op)
    {
	case TOKlt:	eop = OPlt;	break;
	case TOKgt:	eop = OPgt;	break;
	case TOKle:	eop = OPle;	break;
	case TOKge:	eop = OPge;	break;
	case TOKequal:	eop = OPeqeq;	break;
	case TOKnotequal: eop = OPne;	break;

	// NCEG floating point compares
	case TOKunord:	eop = OPunord;	break;
	case TOKlg:	eop = OPlg;	break;
	case TOKleg:	eop = OPleg;	break;
	case TOKule:	eop = OPule;	break;
	case TOKul:	eop = OPul;	break;
	case TOKuge:	eop = OPuge;	break;
	case TOKug:	eop = OPug;	break;
	case TOKue:	eop = OPue;	break;
	default:
	    dump(0);
	    assert(0);
    }
    if (!t1->isfloating())
    {
	// Convert from floating point compare to equivalent
	// integral compare
	eop = (enum OPER)rel_integral(eop);
    }
    if ((int)eop > 1 && t1->ty == Tclass && t2->ty == Tclass)
    {
#if 1
	assert(0);
#else
	elem *ec1;
	elem *ec2;

	ec1 = e1->toElem(irs);
	ec2 = e2->toElem(irs);
	e = el_bin(OPcall,TYint,el_var(rtlsym[RTLSYM_OBJ_CMP]),el_param(ec1, ec2));
	e = el_bin(eop, TYint, e, el_long(TYint, 0));
#endif
    }
    else if ((int)eop > 1 &&
	     (t1->ty == Tarray || t1->ty == Tsarray) &&
	     (t2->ty == Tarray || t2->ty == Tsarray))
    {
	elem *ea1;
	elem *ea2;
	elem *ep;
	Type *telement = t1->nextOf()->toBasetype();
	int rtlfunc;

	ea1 = e1->toElem(irs);
	ea1 = array_toDarray(t1, ea1);
	ea2 = e2->toElem(irs);
	ea2 = array_toDarray(t2, ea2);

#if 1
	ep = el_params(telement->arrayOf()->getInternalTypeInfo(NULL)->toElem(irs),
		ea2, ea1, NULL);
	rtlfunc = RTLSYM_ARRAYCMP2;
#else
	ep = el_params(telement->getInternalTypeInfo(NULL)->toElem(irs), ea2, ea1, NULL);
	rtlfunc = RTLSYM_ARRAYCMP;
#endif
	e = el_bin(OPcall, TYint, el_var(rtlsym[rtlfunc]), ep);
	e = el_bin(eop, TYint, e, el_long(TYint, 0));
	el_setLoc(e,loc);
    }
    else
    {
	if ((int)eop <= 1)
	{
	    /* The result is determinate, create:
	     *   (e1 , e2) , eop
	     */
	    e = toElemBin(irs,OPcomma);
	    e = el_bin(OPcomma,e->Ety,e,el_long(e->Ety,(int)eop));
	}
	else
	    e = toElemBin(irs,eop);
    }
    return e;
}

elem *EqualExp::toElem(IRState *irs)
{
    //printf("EqualExp::toElem() %s\n", toChars());

    elem *e;
    enum OPER eop;
    Type *t1 = e1->type->toBasetype();
    Type *t2 = e2->type->toBasetype();

    switch (op)
    {
	case TOKequal:		eop = OPeqeq;	break;
	case TOKnotequal:	eop = OPne;	break;
	default:
	    dump(0);
	    assert(0);
    }

    //printf("EqualExp::toElem()\n");
    if (t1->ty == Tstruct)
    {	// Do bit compare of struct's
	elem *es1;
	elem *es2;
	elem *ecount;

	es1 = e1->toElem(irs);
	es2 = e2->toElem(irs);
#if 1
	es1 = addressElem(es1, t1);
	es2 = addressElem(es2, t2);
#else
	es1 = el_una(OPaddr, TYnptr, es1);
	es2 = el_una(OPaddr, TYnptr, es2);
#endif
	e = el_param(es1, es2);
	ecount = el_long(TYint, t1->size());
	e = el_bin(OPmemcmp, TYint, e, ecount);
	e = el_bin(eop, TYint, e, el_long(TYint, 0));
	el_setLoc(e,loc);
    }
#if 0
    else if (t1->ty == Tclass && t2->ty == Tclass)
    {
	elem *ec1;
	elem *ec2;

	ec1 = e1->toElem(irs);
	ec2 = e2->toElem(irs);
	e = el_bin(OPcall,TYint,el_var(rtlsym[RTLSYM_OBJ_EQ]),el_param(ec1, ec2));
    }
#endif
    else if ((t1->ty == Tarray || t1->ty == Tsarray) &&
	     (t2->ty == Tarray || t2->ty == Tsarray))
    {
	elem *ea1;
	elem *ea2;
	elem *ep;
	Type *telement = t1->nextOf()->toBasetype();
	int rtlfunc;

	ea1 = e1->toElem(irs);
	ea1 = array_toDarray(t1, ea1);
	ea2 = e2->toElem(irs);
	ea2 = array_toDarray(t2, ea2);

#if 1
	ep = el_params(telement->arrayOf()->getInternalTypeInfo(NULL)->toElem(irs),
		ea2, ea1, NULL);
	rtlfunc = RTLSYM_ARRAYEQ2;
#else
	ep = el_params(telement->getInternalTypeInfo(NULL)->toElem(irs), ea2, ea1, NULL);
	rtlfunc = RTLSYM_ARRAYEQ;
#endif
	e = el_bin(OPcall, TYint, el_var(rtlsym[rtlfunc]), ep);
	if (op == TOKnotequal)
	    e = el_bin(OPxor, TYint, e, el_long(TYint, 1));
	el_setLoc(e,loc);
    }
    else
	e = toElemBin(irs, eop);
    return e;
}

elem *IdentityExp::toElem(IRState *irs)
{
    elem *e;
    enum OPER eop;
    Type *t1 = e1->type->toBasetype();
    Type *t2 = e2->type->toBasetype();

    switch (op)
    {
	case TOKidentity:	eop = OPeqeq;	break;
	case TOKnotidentity:	eop = OPne;	break;
	default:
	    dump(0);
	    assert(0);
    }

    //printf("IdentityExp::toElem() %s\n", toChars());

    if (t1->ty == Tstruct)
    {	// Do bit compare of struct's
	elem *es1;
	elem *es2;
	elem *ecount;

	es1 = e1->toElem(irs);
	es1 = addressElem(es1, e1->type);
	//es1 = el_una(OPaddr, TYnptr, es1);
	es2 = e2->toElem(irs);
	es2 = addressElem(es2, e2->type);
	//es2 = el_una(OPaddr, TYnptr, es2);
	e = el_param(es1, es2);
	ecount = el_long(TYint, t1->size());
	e = el_bin(OPmemcmp, TYint, e, ecount);
	e = el_bin(eop, TYint, e, el_long(TYint, 0));
	el_setLoc(e,loc);
    }
    else if ((t1->ty == Tarray || t1->ty == Tsarray) &&
	     (t2->ty == Tarray || t2->ty == Tsarray))
    {
	elem *ea1;
	elem *ea2;

	ea1 = e1->toElem(irs);
	ea1 = array_toDarray(t1, ea1);
	ea2 = e2->toElem(irs);
	ea2 = array_toDarray(t2, ea2);

	e = el_bin(eop, type->totym(), ea1, ea2);
	el_setLoc(e,loc);
    }
    else
	e = toElemBin(irs, eop);

    return e;
}


/***************************************
 */

elem *InExp::toElem(IRState *irs)
{   elem *e;
    elem *key = e1->toElem(irs);
    elem *aa = e2->toElem(irs);
    elem *ep;
    elem *keyti;
    TypeAArray *taa = (TypeAArray *)e2->type->toBasetype();
    

    // set to:
    //	aaIn(aa, keyti, key);

    if (key->Ety == TYstruct)
    {
	key = el_una(OPstrpar, TYstruct, key);
	key->Enumbytes = key->E1->Enumbytes;
	assert(key->Enumbytes);
    }

    Symbol *s = taa->aaGetSymbol("In", 0);
    keyti = taa->index->getInternalTypeInfo(NULL)->toElem(irs);
    ep = el_params(key, keyti, aa, NULL);
    e = el_bin(OPcall, type->totym(), el_var(s), ep);

    el_setLoc(e,loc);
    return e;
}

/***************************************
 */

elem *RemoveExp::toElem(IRState *irs)
{   elem *e;
    Type *tb = e1->type->toBasetype();
    assert(tb->ty == Taarray);
    TypeAArray *taa = (TypeAArray *)tb;
    elem *ea = e1->toElem(irs);
    elem *ekey = e2->toElem(irs);
    elem *ep;
    elem *keyti;

    if (ekey->Ety == TYstruct)
    {
	ekey = el_una(OPstrpar, TYstruct, ekey);
	ekey->Enumbytes = ekey->E1->Enumbytes;
	assert(ekey->Enumbytes);
    }

    Symbol *s = taa->aaGetSymbol("Del", 0);
    keyti = taa->index->getInternalTypeInfo(NULL)->toElem(irs);
    ep = el_params(ekey, keyti, ea, NULL);
    e = el_bin(OPcall, TYnptr, el_var(s), ep);

    el_setLoc(e,loc);
    return e;
}

/***************************************
 */

elem *AssignExp::toElem(IRState *irs)
{   elem *e;
    IndexExp *ae;
    int r;
    Type *t1b;

    //printf("AssignExp::toElem('%s')\n", toChars());
    t1b = e1->type->toBasetype();

    // Look for array.length = n
    if (e1->op == TOKarraylength)
    {
	// Generate:
	//	_d_arraysetlength(e2, sizeelem, &ale->e1);

	ArrayLengthExp *ale = (ArrayLengthExp *)e1;
	elem *p1;
	elem *p2;
	elem *p3;
	elem *ep;
	Type *t1;

	p1 = e2->toElem(irs);
	p3 = ale->e1->toElem(irs);
	p3 = addressElem(p3, NULL);
	t1 = ale->e1->type->toBasetype();

#if 1
	// call _d_arraysetlengthT(ti, e2, &ale->e1);
	p2 = t1->getTypeInfo(NULL)->toElem(irs);
	ep = el_params(p3, p1, p2, NULL);	// c function
	r = t1->nextOf()->isZeroInit() ? RTLSYM_ARRAYSETLENGTHT : RTLSYM_ARRAYSETLENGTHIT;
#else
	if (t1->next->isZeroInit())
	{   p2 = t1->getTypeInfo(NULL)->toElem(irs);
	    ep = el_params(p3, p1, p2, NULL);	// c function
	    r = RTLSYM_ARRAYSETLENGTHT;
	}
	else
	{
	    p2 = el_long(TYint, t1->next->size());
	    ep = el_params(p3, p2, p1, NULL);	// c function
	    Expression *init = t1->next->defaultInit();
	    ep = el_param(el_long(TYint, init->type->size()), ep);
	    elem *ei = init->toElem(irs);
	    ep = el_param(ei, ep);
	    r = RTLSYM_ARRAYSETLENGTH3;
	}
#endif

	e = el_bin(OPcall, type->totym(), el_var(rtlsym[r]), ep);
	el_setLoc(e, loc);
	return e;
    }

    // Look for array[]=n
    if (e1->op == TOKslice)
    {
	SliceExp *are = (SliceExp *)(e1);
	Type *t1 = t1b;
	Type *t2 = e2->type->toBasetype();

	// which we do if the 'next' types match
	if (ismemset)
	{   // Do a memset for array[]=v
	    //printf("Lpair %s\n", toChars());
	    SliceExp *are = (SliceExp *)e1;
	    elem *elwr;
	    elem *eupr;
	    elem *n1;
	    elem *evalue;
	    elem *enbytes;
	    elem *elength;
	    elem *einit;
	    integer_t value;
	    Type *ta = are->e1->type->toBasetype();
	    Type *tb = ta->nextOf()->toBasetype();
	    int sz = tb->size();
	    tym_t tym = type->totym();

	    n1 = are->e1->toElem(irs);
	    elwr = are->lwr ? are->lwr->toElem(irs) : NULL;
	    eupr = are->upr ? are->upr->toElem(irs) : NULL;

	    elem *n1x = n1;

	    // Look for array[]=n
	    if (ta->ty == Tsarray)
	    {
		TypeSArray *ts;

		ts = (TypeSArray *) ta;
		n1 = array_toPtr(ta, n1);
		enbytes = ts->dim->toElem(irs);
		n1x = n1;
		n1 = el_same(&n1x);
		einit = resolveLengthVar(are->lengthVar, &n1, ta);
	    }
	    else if (ta->ty == Tarray)
	    {
		n1 = el_same(&n1x);
		einit = resolveLengthVar(are->lengthVar, &n1, ta);
		enbytes = el_copytree(n1);
		n1 = array_toPtr(ta, n1);
		enbytes = el_una(OP64_32, TYint, enbytes);
	    }
	    else if (ta->ty == Tpointer)
	    {
		n1 = el_same(&n1x);
		enbytes = el_long(TYint, -1);	// largest possible index
		einit = NULL;
	    }

	    // Enforce order of evaluation of n1[elwr..eupr] as n1,elwr,eupr
	    elem *elwrx = elwr;
	    if (elwr) elwr = el_same(&elwrx);
	    elem *euprx = eupr;
	    if (eupr) eupr = el_same(&euprx);

#if 0
	    printf("sz = %d\n", sz);
	    printf("n1x\n");
	    elem_print(n1x);
	    printf("einit\n");
	    elem_print(einit);
	    printf("elwrx\n");
	    elem_print(elwrx);
	    printf("euprx\n");
	    elem_print(euprx);
	    printf("n1\n");
	    elem_print(n1);
	    printf("elwr\n");
	    elem_print(elwr);
	    printf("eupr\n");
	    elem_print(eupr);
	    printf("enbytes\n");
	    elem_print(enbytes);
#endif
	    einit = el_combine(n1x, einit);
	    einit = el_combine(einit, elwrx);
	    einit = el_combine(einit, euprx);

	    evalue = this->e2->toElem(irs);

#if 0
	    printf("n1\n");
	    elem_print(n1);
	    printf("enbytes\n");
	    elem_print(enbytes);
#endif

	    if (global.params.useArrayBounds && eupr && ta->ty != Tpointer)
	    {
		elem *c1;
		elem *c2;
		elem *ea;
		elem *eb;
		elem *enbytesx;

		assert(elwr);
		enbytesx = enbytes;
		enbytes = el_same(&enbytesx);
		c1 = el_bin(OPle, TYint, el_copytree(eupr), enbytesx);
		c2 = el_bin(OPle, TYint, el_copytree(elwr), el_copytree(eupr));
		c1 = el_bin(OPandand, TYint, c1, c2);

		// Construct: (c1 || ModuleArray(line))
		Symbol *sassert;

		sassert = irs->blx->module->toModuleArray();
		ea = el_bin(OPcall,TYvoid,el_var(sassert), el_long(TYint, loc.linnum));
		eb = el_bin(OPoror,TYvoid,c1,ea);
		einit = el_combine(einit, eb);
	    }

	    if (elwr)
	    {   elem *elwr2;

		el_free(enbytes);
		elwr2 = el_copytree(elwr);
		elwr2 = el_bin(OPmul, TYint, elwr2, el_long(TYint, sz));
		n1 = el_bin(OPadd, TYnptr, n1, elwr2);
		enbytes = el_bin(OPmin, TYint, eupr, elwr);
		elength = el_copytree(enbytes);
	    }
	    else
		elength = el_copytree(enbytes);
	    e = setArray(n1, enbytes, tb, evalue, irs, op);
	Lpair:
	    e = el_pair(TYullong, elength, e);
	Lret2:
	    e = el_combine(einit, e);
	    //elem_print(e);
	    goto Lret;
	}
#if 0
	else if (e2->op == TOKadd || e2->op == TOKmin)
	{
	    /* It's ea[] = eb[] +- ec[]
	     */
	    BinExp *e2a = (BinExp *)e2;
	    Type *t = e2->type->toBasetype()->nextOf()->toBasetype();
	    if (t->ty != Tfloat32 && t->ty != Tfloat64 && t->ty != Tfloat80)
	    {
		e2->error("array add/min for %s not supported", t->toChars());
		return el_long(TYint, 0);
	    }
	    elem *ea = e1->toElem(irs);
	    ea = array_toDarray(e1->type, ea);
	    elem *eb = e2a->e1->toElem(irs);
	    eb = array_toDarray(e2a->e1->type, eb);
	    elem *ec = e2a->e2->toElem(irs);
	    ec = array_toDarray(e2a->e2->type, ec);

	    int rtl = RTLSYM_ARRAYASSADDFLOAT;
	    if (t->ty == Tfloat64)
		rtl = RTLSYM_ARRAYASSADDDOUBLE;
	    else if (t->ty == Tfloat80)
		rtl = RTLSYM_ARRAYASSADDREAL;
	    if (e2->op == TOKmin)
	    {
		rtl = RTLSYM_ARRAYASSMINFLOAT;
		if (t->ty == Tfloat64)
		    rtl = RTLSYM_ARRAYASSMINDOUBLE;
		else if (t->ty == Tfloat80)
		    rtl = RTLSYM_ARRAYASSMINREAL;
	    }

	    /* Set parameters so the order of evaluation is eb, ec, ea
	     */
	    elem *ep = el_params(eb, ec, ea, NULL);
	    e = el_bin(OPcall, type->totym(), el_var(rtlsym[rtl]), ep);
	    goto Lret;
	}
#endif
	else
	{
	    /* It's array1[]=array2[]
	     * which is a memcpy
	     */
	    elem *eto;
	    elem *efrom;
	    elem *esize;
	    elem *ep;

	    eto = e1->toElem(irs);
	    efrom = e2->toElem(irs);

	    unsigned size = t1->nextOf()->size();
	    esize = el_long(TYint, size);

	    /* Determine if we need to do postblit
	     */
	    int postblit = 0;
	    Type *t = t1;
	    do
		t = t->nextOf()->toBasetype();
	    while (t->ty == Tsarray);
	    if (t->ty == Tstruct)
	    {	StructDeclaration *sd = ((TypeStruct *)t)->sym;
		if (sd->postblit)
		    postblit = 1;
	    }

	    assert(e2->type->ty != Tpointer);

	    if (!postblit && !global.params.useArrayBounds)
	    {	elem *epto;
		elem *epfr;
		elem *elen;
		elem *ex;

		ex = el_same(&eto);

		// Determine if elen is a constant
		if (eto->Eoper == OPpair &&
		    eto->E1->Eoper == OPconst)
		{
		    elen = el_copytree(eto->E1);
		}
		else
		{
		    // It's not a constant, so pull it from the dynamic array
		    elen = el_una(OP64_32, TYint, el_copytree(ex));
		}

		esize = el_bin(OPmul, TYint, elen, esize);
		epto = array_toPtr(e1->type, ex);
		epfr = array_toPtr(e2->type, efrom);
		e = el_bin(OPmemcpy, TYnptr, epto, el_param(epfr, esize));
		e = el_pair(eto->Ety, el_copytree(elen), e);
		e = el_combine(eto, e);
	    }
#if DMDV2
	    else if (postblit && op != TOKblit)
	    {
		/* Generate:
		 *	_d_arrayassign(ti, efrom, eto)
		 * or:
		 *	_d_arrayctor(ti, efrom, eto)
		 */
		el_free(esize);
		Expression *ti = t1->nextOf()->toBasetype()->getTypeInfo(NULL);
		ep = el_params(eto, efrom, ti->toElem(irs), NULL);
		int rtl = (op == TOKconstruct) ? RTLSYM_ARRAYCTOR : RTLSYM_ARRAYASSIGN;
		e = el_bin(OPcall, type->totym(), el_var(rtlsym[rtl]), ep);
	    }
#endif
	    else
	    {
		// Generate:
		//	_d_arraycopy(eto, efrom, esize)

		ep = el_params(eto, efrom, esize, NULL);
		e = el_bin(OPcall, type->totym(), el_var(rtlsym[RTLSYM_ARRAYCOPY]), ep);
	    }
	    el_setLoc(e, loc);
	    return e;
	}
    }

    if (e1->op == TOKindex)
    {
	elem *eb;
	elem *ei;
	elem *ev;
	TY ty;
	Type *ta;

	ae = (IndexExp *)(e1);
	ta = ae->e1->type->toBasetype();
	ty = ta->ty;
    }
#if 1
    /* This will work if we can distinguish an assignment from
     * an initialization of the lvalue. It'll work if the latter.
     * If the former, because of aliasing of the return value with
     * function arguments, it'll fail.
     */
    if (op == TOKconstruct && e2->op == TOKcall)
    {	CallExp *ce = (CallExp *)e2;

	TypeFunction *tf = (TypeFunction *)ce->e1->type->toBasetype();
	if (tf->ty == Tfunction && tf->retStyle() == RETstack)
	{
	    elem *ehidden = e1->toElem(irs);
	    ehidden = el_una(OPaddr, TYnptr, ehidden);
	    assert(!irs->ehidden);
	    irs->ehidden = ehidden;
	    e = e2->toElem(irs);
	    goto Lret;
	}
    }
#endif
    if (t1b->ty == Tstruct)
    {
	if (e2->op == TOKint64)
	{   /* Implement:
	     *	(struct = 0)
	     * with:
	     *	memset(&struct, 0, struct.sizeof)
	     */
	    elem *el = e1->toElem(irs);
	    elem *enbytes = el_long(TYint, e1->type->size());
	    elem *evalue = el_long(TYint, 0);

	    el = el_una(OPaddr, TYnptr, el);
	    e = el_param(enbytes, evalue);
	    e = el_bin(OPmemset,TYnptr,el,e);
	    el_setLoc(e, loc);
	    //e = el_una(OPind, TYstruct, e);
	}
	else
	{
	    elem *e1;
	    elem *e2;
	    tym_t tym;

	    //printf("toElemBin() '%s'\n", toChars());

	    tym = type->totym();

	    e1 = this->e1->toElem(irs);
	    elem *ex = e1;
	    if (e1->Eoper == OPind)
		ex = e1->E1;
	    if (this->e2->op == TOKstructliteral &&
		ex->Eoper == OPvar && ex->EV.sp.Voffset == 0)
	    {	StructLiteralExp *se = (StructLiteralExp *)this->e2;

		Symbol *symSave = se->sym;
		size_t soffsetSave = se->soffset;
		int fillHolesSave = se->fillHoles;

		se->sym = ex->EV.sp.Vsym;
		se->soffset = 0;
		se->fillHoles = (op == TOKconstruct || op == TOKblit) ? 1 : 0;

		el_free(e1);
		e = this->e2->toElem(irs);

		se->sym = symSave;
		se->soffset = soffsetSave;
		se->fillHoles = fillHolesSave;
	    }
	    else
	    {
		e2 = this->e2->toElem(irs);
		e = el_bin(OPstreq,tym,e1,e2);
		e->Enumbytes = this->e1->type->size();
	    }
	    goto Lret;
	}
    }
    else
	e = toElemBin(irs,OPeq);
    return e;

  Lret:
    el_setLoc(e,loc);
    return e;
}

/***************************************
 */

elem *AddAssignExp::toElem(IRState *irs)
{
    //printf("AddAssignExp::toElem() %s\n", toChars());
    elem *e;
    Type *tb1 = e1->type->toBasetype();
    Type *tb2 = e2->type->toBasetype();

    if ((tb1->ty == Tarray || tb1->ty == Tsarray) &&
	(tb2->ty == Tarray || tb2->ty == Tsarray)
       )
    {
	error("Array operations not implemented");
    }
    else
	e = toElemBin(irs,OPaddass);
    return e;
}


/***************************************
 */

elem *MinAssignExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPminass);
}

/***************************************
 */

elem *CatAssignExp::toElem(IRState *irs)
{
    //printf("CatAssignExp::toElem('%s')\n", toChars());
    elem *e;
    Type *tb1 = e1->type->toBasetype();
    Type *tb2 = e2->type->toBasetype();

    if (tb1->ty == Tarray || tb2->ty == Tsarray)
    {   elem *e1;
	elem *e2;
	elem *ep;

	e1 = this->e1->toElem(irs);
	e1 = el_una(OPaddr, TYnptr, e1);

	e2 = this->e2->toElem(irs);
	if (e2->Ety == TYstruct)
	{
	    e2 = el_una(OPstrpar, TYstruct, e2);
	    e2->Enumbytes = e2->E1->Enumbytes;
	    assert(e2->Enumbytes);
	}

	Type *tb1n = tb1->nextOf()->toBasetype();
	if ((tb2->ty == Tarray || tb2->ty == Tsarray) &&
	    tb1n->equals(tb2->nextOf()->toBasetype()))
	{   // Append array
#if 1
	    ep = el_params(e2, e1, this->e1->type->getTypeInfo(NULL)->toElem(irs), NULL);
	    e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYAPPENDT]), ep);
#else
	    ep = el_params(el_long(TYint, tb1n->size()), e2, e1, NULL);
	    e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYAPPEND]), ep);
#endif
	}
	else
	{   // Append element
#if 1
	    ep = el_params(e2, e1, this->e1->type->getTypeInfo(NULL)->toElem(irs), NULL);
	    e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYAPPENDCT]), ep);
#else
	    ep = el_params(e2, el_long(TYint, tb1n->size()), e1, NULL);
	    e = el_bin(OPcall, TYdarray, el_var(rtlsym[RTLSYM_ARRAYAPPENDC]), ep);
#endif
	}
	el_setLoc(e,loc);
    }
    else
	assert(0);
    return e;
}


/***************************************
 */

elem *DivAssignExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPdivass);
}


/***************************************
 */

elem *ModAssignExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPmodass);
}


/***************************************
 */

elem *MulAssignExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPmulass);
}


/***************************************
 */

elem *ShlAssignExp::toElem(IRState *irs)
{   elem *e;

    e = toElemBin(irs,OPshlass);
    return e;
}


/***************************************
 */

elem *ShrAssignExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPshrass);
}


/***************************************
 */

elem *UshrAssignExp::toElem(IRState *irs)
{
    elem *eleft  = e1->toElem(irs);
    eleft->Ety = touns(eleft->Ety);
    elem *eright = e2->toElem(irs);
    elem *e = el_bin(OPshrass, type->totym(), eleft, eright);
    el_setLoc(e, loc);
    return e;
}


/***************************************
 */

elem *AndAssignExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPandass);
}


/***************************************
 */

elem *OrAssignExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPorass);
}


/***************************************
 */

elem *XorAssignExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPxorass);
}


/***************************************
 */

elem *AndAndExp::toElem(IRState *irs)
{
    elem *e = toElemBin(irs,OPandand);
    if (global.params.cov && e2->loc.linnum)
	e->E2 = el_combine(incUsageElem(irs, e2->loc), e->E2);
    return e;
}


/***************************************
 */

elem *OrOrExp::toElem(IRState *irs)
{
    elem *e = toElemBin(irs,OPoror);
    if (global.params.cov && e2->loc.linnum)
	e->E2 = el_combine(incUsageElem(irs, e2->loc), e->E2);
    return e;
}


/***************************************
 */

elem *XorExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPxor);
}


/***************************************
 */

elem *AndExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPand);
}


/***************************************
 */

elem *OrExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPor);
}


/***************************************
 */

elem *ShlExp::toElem(IRState *irs)
{
    return toElemBin(irs, OPshl);
}


/***************************************
 */

elem *ShrExp::toElem(IRState *irs)
{
    return toElemBin(irs,OPshr);
}


/***************************************
 */

elem *UshrExp::toElem(IRState *irs)
{
    elem *eleft  = e1->toElem(irs);
    eleft->Ety = touns(eleft->Ety);
    elem *eright = e2->toElem(irs);
    elem *e = el_bin(OPshr, type->totym(), eleft, eright);
    el_setLoc(e, loc);
    return e;
}

/****************************************
 */

elem *CommaExp::toElem(IRState *irs)
{
    assert(e1 && e2);
    elem *eleft  = e1->toElem(irs);
    elem *eright = e2->toElem(irs);
    elem *e = el_combine(eleft, eright);
    if (e)
	el_setLoc(e, loc);
    return e;
}


/***************************************
 */

elem *CondExp::toElem(IRState *irs)
{   elem *eleft;
    elem *eright;

    elem *ec = econd->toElem(irs);

    eleft = e1->toElem(irs);
    tym_t ty = eleft->Ety;
    if (global.params.cov && e1->loc.linnum)
	eleft = el_combine(incUsageElem(irs, e1->loc), eleft);

    eright = e2->toElem(irs);
    if (global.params.cov && e2->loc.linnum)
	eright = el_combine(incUsageElem(irs, e2->loc), eright);

    elem *e = el_bin(OPcond, ty, ec, el_bin(OPcolon, ty, eleft, eright));
    if (tybasic(ty) == TYstruct)
	e->Enumbytes = e1->type->size();
    el_setLoc(e, loc);
    return e;
}


/***************************************
 */

elem *TypeDotIdExp::toElem(IRState *irs)
{
    print();
    assert(0);
    return NULL;
}

elem *TypeExp::toElem(IRState *irs)
{
#ifdef DEBUG
    printf("TypeExp::toElem()\n");
#endif
    error("type %s is not an expression", toChars());
    return el_long(TYint, 0);
}

elem *ScopeExp::toElem(IRState *irs)
{
    error("%s is not an expression", sds->toChars());
    return el_long(TYint, 0);
}

elem *DotVarExp::toElem(IRState *irs)
{
    // *(&e + offset)

    //printf("DotVarExp::toElem('%s')\n", toChars());

    VarDeclaration *v = var->isVarDeclaration();
    if (!v)
    {
	error("%s is not a field", var->toChars());
    }

    elem *e = e1->toElem(irs);
    Type *tb1 = e1->type->toBasetype();
    if (tb1->ty != Tclass && tb1->ty != Tpointer)
	e = el_una(OPaddr, TYnptr, e);
    e = el_bin(OPadd, TYnptr, e, el_long(TYint, v ? v->offset : 0));
    e = el_una(OPind, type->totym(), e);
    if (tybasic(e->Ety) == TYstruct)
    {
	e->Enumbytes = type->size();
    }
    el_setLoc(e,loc);
    return e;
}

elem *DelegateExp::toElem(IRState *irs)
{
    elem *e;
    elem *ethis;
    elem *ep;
    Symbol *sfunc;
    int directcall = 0;

    //printf("DelegateExp::toElem() '%s'\n", toChars());
    sfunc = func->toSymbol();
    if (func->isNested())
    {
	ep = el_ptr(sfunc);
	ethis = getEthis(loc, irs, func);
    }
    else
    {
	ethis = e1->toElem(irs);
	if (e1->type->ty != Tclass && e1->type->ty != Tpointer)
	    ethis = el_una(OPaddr, TYnptr, ethis);

	if (e1->op == TOKsuper)
	    directcall = 1;

	if (!func->isThis())
	    error("delegates are only for non-static functions");

	if (!func->isVirtual() ||
	    directcall ||
	    func->isFinal())
	{
	    ep = el_ptr(sfunc);
	}
	else
	{
	    // Get pointer to function out of virtual table
	    unsigned vindex;

	    assert(ethis);
	    ep = el_same(&ethis);
	    ep = el_una(OPind, TYnptr, ep);
	    vindex = func->vtblIndex;

	    // Build *(ep + vindex * 4)
	    ep = el_bin(OPadd,TYnptr,ep,el_long(TYint, vindex * 4));
	    ep = el_una(OPind,TYnptr,ep);
	}

//	if (func->tintro)
//	    func->error(loc, "cannot form delegate due to covariant return type");
    }
    if (ethis->Eoper == OPcomma)
    {
	ethis->E2 = el_pair(TYullong, ethis->E2, ep);
	ethis->Ety = TYullong;
	e = ethis;
    }
    else
	e = el_pair(TYullong, ethis, ep);
    el_setLoc(e,loc);
    return e;
}

elem *DotTypeExp::toElem(IRState *irs)
{
    // Just a pass-thru to e1
    elem *e;

    //printf("DotTypeExp::toElem() %s\n", toChars());
    e = e1->toElem(irs);
    el_setLoc(e,loc);
    return e;
}

elem *CallExp::toElem(IRState *irs)
{
    //printf("CallExp::toElem('%s')\n", toChars());
    assert(e1->type);
    elem *ec;
    int directcall;
    FuncDeclaration *fd;
    Type *t1 = e1->type->toBasetype();
    Type *ectype = t1;

    elem *ehidden = irs->ehidden;
    irs->ehidden = NULL;

    directcall = 0;
    fd = NULL;
    if (e1->op == TOKdotvar && t1->ty != Tdelegate)
    {	DotVarExp *dve = (DotVarExp *)e1;

	fd = dve->var->isFuncDeclaration();
	Expression *ex = dve->e1;
	while (1)
	{
	    switch (ex->op)
	    {
		case TOKsuper:		// super.member() calls directly
		case TOKdottype:	// type.member() calls directly
		    directcall = 1;
		    break;

		case TOKcast:
		    ex = ((CastExp *)ex)->e1;
		    continue;

		default:
		    //ex->dump(0);
		    break;
	    }
	    break;
	}
	ec = dve->e1->toElem(irs);
	ectype = dve->e1->type->toBasetype();
    }
    else if (e1->op == TOKvar)
    {
	fd = ((VarExp *)e1)->var->isFuncDeclaration();

	if (fd && fd->ident == Id::alloca &&
	    !fd->fbody && fd->linkage == LINKc &&
	    arguments && arguments->dim == 1)
	{   Expression *arg = (Expression *)arguments->data[0];
	    arg = arg->optimize(WANTvalue);
	    if (arg->isConst() && arg->type->isintegral())
	    {	integer_t sz = arg->toInteger();
		if (sz > 0 && sz < 0x40000)
		{
		    // It's an alloca(sz) of a fixed amount.
		    // Replace with an array allocated on the stack
		    // of the same size: char[sz] tmp;

		    Symbol *stmp;
		    ::type *t;

		    assert(!ehidden);
		    t = type_allocn(TYarray, tschar);
		    t->Tdim = sz;
		    stmp = symbol_genauto(t);
		    ec = el_ptr(stmp);
		    el_setLoc(ec,loc);
		    return ec;
		}
	    }
	}

	ec = e1->toElem(irs);
    }
    else
    {
	ec = e1->toElem(irs);
    }
    ec = callfunc(loc, irs, directcall, type, ec, ectype, fd, t1, ehidden, arguments);
    el_setLoc(ec,loc);
    return ec;
}

elem *AddrExp::toElem(IRState *irs)
{   elem *e;
    elem **pe;

    //printf("AddrExp::toElem('%s')\n", toChars());

    e = e1->toElem(irs);
    e = addressElem(e, e1->type);
L2:
    e->Ety = type->totym();
    el_setLoc(e,loc);
    return e;
}

elem *PtrExp::toElem(IRState *irs)
{   elem *e;

    //printf("PtrExp::toElem() %s\n", toChars());
    e = e1->toElem(irs);
    e = el_una(OPind,type->totym(),e);
    if (tybasic(e->Ety) == TYstruct)
    {
	e->Enumbytes = type->size();
    }
    el_setLoc(e,loc);
    return e;
}

elem *BoolExp::toElem(IRState *irs)
{   elem *e1;

    e1 = this->e1->toElem(irs);
    return el_una(OPbool,type->totym(),e1);
}

elem *DeleteExp::toElem(IRState *irs)
{   elem *e;
    int rtl;
    Type *tb;

    //printf("DeleteExp::toElem()\n");
    if (e1->op == TOKindex)
    {
	IndexExp *ae = (IndexExp *)(e1);
	tb = ae->e1->type->toBasetype();
	if (tb->ty == Taarray)
	{
	    TypeAArray *taa = (TypeAArray *)tb;
	    elem *ea = ae->e1->toElem(irs);
	    elem *ekey = ae->e2->toElem(irs);
	    elem *ep;
	    elem *keyti;

	    if (ekey->Ety == TYstruct)
	    {
		ekey = el_una(OPstrpar, TYstruct, ekey);
		ekey->Enumbytes = ekey->E1->Enumbytes;
		assert(ekey->Enumbytes);
	    }

	    Symbol *s = taa->aaGetSymbol("Del", 0);
	    keyti = taa->index->getInternalTypeInfo(NULL)->toElem(irs);
	    ep = el_params(ekey, keyti, ea, NULL);
	    e = el_bin(OPcall, TYnptr, el_var(s), ep);
	    goto Lret;
	}
    }
    //e1->type->print();
    e = e1->toElem(irs);
    tb = e1->type->toBasetype();
    switch (tb->ty)
    {
	case Tarray:
	{   e = addressElem(e, e1->type);
	    rtl = RTLSYM_DELARRAYT;

	    /* See if we need to run destructors on the array contents
	     */
	    elem *et = NULL;
	    Type *tv = tb->nextOf()->toBasetype();
	    while (tv->ty == Tsarray)
	    {   TypeSArray *ta = (TypeSArray *)tv;
		tv = tv->nextOf()->toBasetype();
	    }
	    if (tv->ty == Tstruct)
	    {   TypeStruct *ts = (TypeStruct *)tv;
		StructDeclaration *sd = ts->sym;
		if (sd->dtor)
		    et = tb->nextOf()->getTypeInfo(NULL)->toElem(irs);
	    }
	    if (!et)				// if no destructors needed
		et = el_long(TYnptr, 0);	// pass null for TypeInfo
	    e = el_params(et, e, NULL);
	    // call _d_delarray_t(e, et);
	    e = el_bin(OPcall, TYvoid, el_var(rtlsym[rtl]), e);
	    goto Lret;
	}
	case Tclass:
	    if (e1->op == TOKvar)
	    {	VarExp *ve = (VarExp *)e1;
		if (ve->var->isVarDeclaration() &&
		    ve->var->isVarDeclaration()->onstack)
		{
		    rtl = RTLSYM_CALLFINALIZER;
		    if (tb->isClassHandle()->isInterfaceDeclaration())
			rtl = RTLSYM_CALLINTERFACEFINALIZER;
		    break;
		}
	    }
	    e = addressElem(e, e1->type);
	    rtl = RTLSYM_DELCLASS;
	    if (tb->isClassHandle()->isInterfaceDeclaration())
		rtl = RTLSYM_DELINTERFACE;
	    break;

	case Tpointer:
	    e = addressElem(e, e1->type);
	    rtl = RTLSYM_DELMEMORY;
	    break;

	default:
	    assert(0);
	    break;
    }
    e = el_bin(OPcall, TYvoid, el_var(rtlsym[rtl]), e);

  Lret:
    el_setLoc(e,loc);
    return e;
}

elem *CastExp::toElem(IRState *irs)
{   elem *e;
    TY fty;
    TY tty;
    tym_t ftym;
    tym_t ttym;
    enum OPER eop;
    Type *t;
    Type *tfrom;

#if 0
    printf("CastExp::toElem()\n");
    print();
    printf("\tfrom: %s\n", e1->type->toChars());
    printf("\tto  : %s\n", to->toChars());
#endif

    e = e1->toElem(irs);
    tfrom = e1->type->toBasetype();
    t = to->toBasetype();		// skip over typedef's
    if (t->equals(tfrom))
	goto Lret;

    fty = tfrom->ty;
    //printf("fty = %d\n", fty);
    tty = t->ty;

    if (tty == Tpointer && fty == Tarray
#if 0
	&& (t->next->ty == Tvoid || t->next->equals(e1->type->next))
#endif
       )
    {
	if (e->Eoper == OPvar)
	{
	    // e1 -> *(&e1 + 4)
	    e = el_una(OPaddr, TYnptr, e);
	    e = el_bin(OPadd, TYnptr, e, el_long(TYint, 4));
	    e = el_una(OPind,t->totym(),e);
	}
	else
	{
	    // e1 -> (unsigned)(e1 >> 32)
	    e = el_bin(OPshr, TYullong, e, el_long(TYint, 32));
	    e = el_una(OP64_32, t->totym(), e);
	}
	goto Lret;
    }

    if (tty == Tpointer && fty == Tsarray
#if 0
	&& (t->next->ty == Tvoid || t->next->equals(e1->type->next))
#endif
	)
    {
	// e1 -> &e1
	e = el_una(OPaddr, TYnptr, e);
	goto Lret;
    }

    // Convert from static array to dynamic array
    if (tty == Tarray && fty == Tsarray)
    {
	e = sarray_toDarray(loc, tfrom, t, e);
	goto Lret;
    }

    // Convert from dynamic array to dynamic array
    if (tty == Tarray && fty == Tarray)
    {
	unsigned fsize = tfrom->nextOf()->size();
	unsigned tsize = t->nextOf()->size();

	if (fsize != tsize)
	{
	    elem *ep;

	    ep = el_params(e, el_long(TYint, fsize), el_long(TYint, tsize), NULL);
	    e = el_bin(OPcall, type->totym(), el_var(rtlsym[RTLSYM_ARRAYCAST]), ep);
	}
	goto Lret;
    }

    // Casting from base class to derived class requires a runtime check
    if (fty == Tclass && tty == Tclass)
    {
	// Casting from derived class to base class is a no-op
	ClassDeclaration *cdfrom;
	ClassDeclaration *cdto;
	int offset;
	int rtl = RTLSYM_DYNAMIC_CAST;

	cdfrom = e1->type->isClassHandle();
	cdto   = t->isClassHandle();
	if (cdfrom->isInterfaceDeclaration())
	{
	    rtl = RTLSYM_INTERFACE_CAST;
	    if (cdfrom->isCPPinterface())
	    {
		if (cdto->isCPPinterface())
		{
		    /* Casting from a C++ interface to a C++ interface
		     * is always a 'paint' operation
		     */
		    goto Lret;			// no-op
		}

		/* Casting from a C++ interface to a class
		 * always results in null because there is no runtime
		 * information available to do it.
		 *
		 * Casting from a C++ interface to a non-C++ interface
		 * always results in null because there's no way one
		 * can be derived from the other.
		 */
		e = el_bin(OPcomma, TYnptr, e, el_long(TYnptr, 0));
		goto Lret;
	    }
	}
	if (cdto->isBaseOf(cdfrom, &offset) && offset != OFFSET_RUNTIME)
	{
	    /* The offset from cdfrom=>cdto is known at compile time.
	     */
	
	    //printf("offset = %d\n", offset);
	    if (offset)
	    {	/* Rewrite cast as (e ? e + offset : null)
		 */
		elem *etmp;
		elem *ex;

		if (e1->op == TOKthis)
		{   // Assume 'this' is never null, so skip null check
		    e = el_bin(OPadd, TYnptr, e, el_long(TYint, offset));
		}
		else
		{
		    etmp = el_same(&e);
		    ex = el_bin(OPadd, TYnptr, etmp, el_long(TYint, offset));
		    ex = el_bin(OPcolon, TYnptr, ex, el_long(TYnptr, 0));
		    e = el_bin(OPcond, TYnptr, e, ex);
		}
	    }
	    goto Lret;			// no-op
	}

	/* The offset from cdfrom=>cdto can only be determined at runtime.
	 */
	elem *ep;

	ep = el_param(el_ptr(cdto->toSymbol()), e);
	e = el_bin(OPcall, TYnptr, el_var(rtlsym[rtl]), ep);
	goto Lret;
    }

    ftym = e->Ety;
    ttym = t->totym();
    if (ftym == ttym)
	goto Lret;

    switch (tty)
    {
	case Tpointer:
	    if (fty == Tdelegate)
		goto Lpaint;
	    tty = Tuns32;
	    break;

	case Tchar:	tty = Tuns8;	break;
	case Twchar:	tty = Tuns16;	break;
	case Tdchar:	tty = Tuns32;	break;
	case Tvoid:	goto Lpaint;

	case Tbool:
	{
	    // Construct e?true:false
	    elem *eq;

	    e = el_una(OPbool, ttym, e);
	    goto Lret;
	}
    }

    switch (fty)
    {
	case Tpointer:	fty = Tuns32;	break;
	case Tchar:	fty = Tuns8;	break;
	case Twchar:	fty = Tuns16;	break;
	case Tdchar:	fty = Tuns32;	break;
    }

    #define X(fty, tty) ((fty) * TMAX + (tty))
Lagain:
    switch (X(fty,tty))
    {
#if 0
	case X(Tbit,Tint8):
	case X(Tbit,Tuns8):
				goto Lpaint;
	case X(Tbit,Tint16):
	case X(Tbit,Tuns16):
	case X(Tbit,Tint32):
	case X(Tbit,Tuns32):	eop = OPu8_16;	goto Leop;
	case X(Tbit,Tint64):
	case X(Tbit,Tuns64):
	case X(Tbit,Tfloat32):
	case X(Tbit,Tfloat64):
	case X(Tbit,Tfloat80):
	case X(Tbit,Tcomplex32):
	case X(Tbit,Tcomplex64):
	case X(Tbit,Tcomplex80):
				e = el_una(OPu8_16, TYuint, e);
				fty = Tuns32;
				goto Lagain;
	case X(Tbit,Timaginary32):
	case X(Tbit,Timaginary64):
	case X(Tbit,Timaginary80): goto Lzero;
#endif
	/* ============================= */

	case X(Tbool,Tint8):
	case X(Tbool,Tuns8):
				goto Lpaint;
	case X(Tbool,Tint16):
	case X(Tbool,Tuns16):
	case X(Tbool,Tint32):
	case X(Tbool,Tuns32):	eop = OPu8_16;	goto Leop;
	case X(Tbool,Tint64):
	case X(Tbool,Tuns64):
	case X(Tbool,Tfloat32):
	case X(Tbool,Tfloat64):
	case X(Tbool,Tfloat80):
	case X(Tbool,Tcomplex32):
	case X(Tbool,Tcomplex64):
	case X(Tbool,Tcomplex80):
				e = el_una(OPu8_16, TYuint, e);
				fty = Tuns32;
				goto Lagain;
	case X(Tbool,Timaginary32):
	case X(Tbool,Timaginary64):
	case X(Tbool,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tint8,Tuns8):	goto Lpaint;
	case X(Tint8,Tint16):
	case X(Tint8,Tuns16):
	case X(Tint8,Tint32):
	case X(Tint8,Tuns32):	eop = OPs8_16;	goto Leop;
	case X(Tint8,Tint64):
	case X(Tint8,Tuns64):
	case X(Tint8,Tfloat32):
	case X(Tint8,Tfloat64):
	case X(Tint8,Tfloat80):
	case X(Tint8,Tcomplex32):
	case X(Tint8,Tcomplex64):
	case X(Tint8,Tcomplex80):
				e = el_una(OPs8_16, TYint, e);
				fty = Tint32;
				goto Lagain;
	case X(Tint8,Timaginary32):
	case X(Tint8,Timaginary64):
	case X(Tint8,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tuns8,Tint8):	goto Lpaint;
	case X(Tuns8,Tint16):
	case X(Tuns8,Tuns16):
	case X(Tuns8,Tint32):
	case X(Tuns8,Tuns32):	eop = OPu8_16;	goto Leop;
	case X(Tuns8,Tint64):
	case X(Tuns8,Tuns64):
	case X(Tuns8,Tfloat32):
	case X(Tuns8,Tfloat64):
	case X(Tuns8,Tfloat80):
	case X(Tuns8,Tcomplex32):
	case X(Tuns8,Tcomplex64):
	case X(Tuns8,Tcomplex80):
				e = el_una(OPu8_16, TYuint, e);
				fty = Tuns32;
				goto Lagain;
	case X(Tuns8,Timaginary32):
	case X(Tuns8,Timaginary64):
	case X(Tuns8,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tint16,Tint8):
	case X(Tint16,Tuns8):	eop = OP16_8;	goto Leop;
	case X(Tint16,Tuns16):	goto Lpaint;
	case X(Tint16,Tint32):
	case X(Tint16,Tuns32):	eop = OPs16_32;	goto Leop;
	case X(Tint16,Tint64):
	case X(Tint16,Tuns64):	e = el_una(OPs16_32, TYint, e);
				fty = Tint32;
				goto Lagain;
	case X(Tint16,Tfloat32):
	case X(Tint16,Tfloat64):
	case X(Tint16,Tfloat80):
	case X(Tint16,Tcomplex32):
	case X(Tint16,Tcomplex64):
	case X(Tint16,Tcomplex80):
				e = el_una(OPs16_d, TYdouble, e);
				fty = Tfloat64;
				goto Lagain;
	case X(Tint16,Timaginary32):
	case X(Tint16,Timaginary64):
	case X(Tint16,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tuns16,Tint8):
	case X(Tuns16,Tuns8):	eop = OP16_8;	goto Leop;
	case X(Tuns16,Tint16):	goto Lpaint;
	case X(Tuns16,Tint32):
	case X(Tuns16,Tuns32):	eop = OPu16_32;	goto Leop;
	case X(Tuns16,Tint64):
	case X(Tuns16,Tuns64):
	case X(Tuns16,Tfloat64):
	case X(Tuns16,Tfloat32):
	case X(Tuns16,Tfloat80):
	case X(Tuns16,Tcomplex32):
	case X(Tuns16,Tcomplex64):
	case X(Tuns16,Tcomplex80):
				e = el_una(OPu16_32, TYuint, e);
				fty = Tuns32;
				goto Lagain;
	case X(Tuns16,Timaginary32):
	case X(Tuns16,Timaginary64):
	case X(Tuns16,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tint32,Tint8):
	case X(Tint32,Tuns8):	e = el_una(OP32_16, TYshort, e);
				fty = Tint16;
				goto Lagain;
	case X(Tint32,Tint16):
	case X(Tint32,Tuns16):	eop = OP32_16;	goto Leop;
	case X(Tint32,Tuns32):	goto Lpaint;
	case X(Tint32,Tint64):
	case X(Tint32,Tuns64):	eop = OPs32_64;	goto Leop;
	case X(Tint32,Tfloat32):
	case X(Tint32,Tfloat64):
	case X(Tint32,Tfloat80):
	case X(Tint32,Tcomplex32):
	case X(Tint32,Tcomplex64):
	case X(Tint32,Tcomplex80):
				e = el_una(OPs32_d, TYdouble, e);
				fty = Tfloat64;
				goto Lagain;
	case X(Tint32,Timaginary32):
	case X(Tint32,Timaginary64):
	case X(Tint32,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tuns32,Tint8):
	case X(Tuns32,Tuns8):	e = el_una(OP32_16, TYshort, e);
				fty = Tuns16;
				goto Lagain;
	case X(Tuns32,Tint16):
	case X(Tuns32,Tuns16):	eop = OP32_16;	goto Leop;
	case X(Tuns32,Tint32):	goto Lpaint;
	case X(Tuns32,Tint64):
	case X(Tuns32,Tuns64):	eop = OPu32_64;	goto Leop;
	case X(Tuns32,Tfloat32):
	case X(Tuns32,Tfloat64):
	case X(Tuns32,Tfloat80):
	case X(Tuns32,Tcomplex32):
	case X(Tuns32,Tcomplex64):
	case X(Tuns32,Tcomplex80):
				e = el_una(OPu32_d, TYdouble, e);
				fty = Tfloat64;
				goto Lagain;
	case X(Tuns32,Timaginary32):
	case X(Tuns32,Timaginary64):
	case X(Tuns32,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tint64,Tint8):
	case X(Tint64,Tuns8):
	case X(Tint64,Tint16):
	case X(Tint64,Tuns16):	e = el_una(OP64_32, TYint, e);
				fty = Tint32;
				goto Lagain;
	case X(Tint64,Tint32):
	case X(Tint64,Tuns32):	eop = OP64_32; goto Leop;
	case X(Tint64,Tuns64):	goto Lpaint;
	case X(Tint64,Tfloat32):
	case X(Tint64,Tfloat64):
	case X(Tint64,Tfloat80):
	case X(Tint64,Tcomplex32):
	case X(Tint64,Tcomplex64):
	case X(Tint64,Tcomplex80):
				e = el_una(OPs64_d, TYdouble, e);
				fty = Tfloat64;
				goto Lagain;
	case X(Tint64,Timaginary32):
	case X(Tint64,Timaginary64):
	case X(Tint64,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tuns64,Tint8):
	case X(Tuns64,Tuns8):
	case X(Tuns64,Tint16):
	case X(Tuns64,Tuns16):	e = el_una(OP64_32, TYint, e);
				fty = Tint32;
				goto Lagain;
	case X(Tuns64,Tint32):
	case X(Tuns64,Tuns32):	eop = OP64_32;	goto Leop;
	case X(Tuns64,Tint64):	goto Lpaint;
	case X(Tuns64,Tfloat32):
	case X(Tuns64,Tfloat64):
	case X(Tuns64,Tfloat80):
	case X(Tuns64,Tcomplex32):
	case X(Tuns64,Tcomplex64):
	case X(Tuns64,Tcomplex80):
				 e = el_una(OPu64_d, TYdouble, e);
				 fty = Tfloat64;
				 goto Lagain;
	case X(Tuns64,Timaginary32):
	case X(Tuns64,Timaginary64):
	case X(Tuns64,Timaginary80): goto Lzero;

	/* ============================= */

	case X(Tfloat32,Tint8):
	case X(Tfloat32,Tuns8):
	case X(Tfloat32,Tint16):
	case X(Tfloat32,Tuns16):
	case X(Tfloat32,Tint32):
	case X(Tfloat32,Tuns32):
	case X(Tfloat32,Tint64):
	case X(Tfloat32,Tuns64):
	case X(Tfloat32,Tfloat80): e = el_una(OPf_d, TYdouble, e);
				   fty = Tfloat64;
				   goto Lagain;
	case X(Tfloat32,Tfloat64): eop = OPf_d;	goto Leop;
	case X(Tfloat32,Timaginary32): goto Lzero;
	case X(Tfloat32,Timaginary64): goto Lzero;
	case X(Tfloat32,Timaginary80): goto Lzero;
	case X(Tfloat32,Tcomplex32):
	case X(Tfloat32,Tcomplex64):
	case X(Tfloat32,Tcomplex80):
	    e = el_bin(OPadd,TYcfloat,el_long(TYifloat,0),e);
	    fty = Tcomplex32;
	    goto Lagain;

	/* ============================= */

	case X(Tfloat64,Tint8):
	case X(Tfloat64,Tuns8):    e = el_una(OPd_s16, TYshort, e);
				   fty = Tint16;
				   goto Lagain;
	case X(Tfloat64,Tint16):   eop = OPd_s16; goto Leop;
	case X(Tfloat64,Tuns16):   eop = OPd_u16; goto Leop;
	case X(Tfloat64,Tint32):   eop = OPd_s32; goto Leop;
	case X(Tfloat64,Tuns32):   eop = OPd_u32; goto Leop;
	case X(Tfloat64,Tint64):   eop = OPd_s64; goto Leop;
	case X(Tfloat64,Tuns64):   eop = OPd_u64; goto Leop;
	case X(Tfloat64,Tfloat32): eop = OPd_f;   goto Leop;
	case X(Tfloat64,Tfloat80): eop = OPd_ld;  goto Leop;
	case X(Tfloat64,Timaginary32):	goto Lzero;
	case X(Tfloat64,Timaginary64):	goto Lzero;
	case X(Tfloat64,Timaginary80):	goto Lzero;
	case X(Tfloat64,Tcomplex32):
	case X(Tfloat64,Tcomplex64):
	case X(Tfloat64,Tcomplex80):
	    e = el_bin(OPadd,TYcfloat,el_long(TYidouble,0),e);
	    fty = Tcomplex64;
	    goto Lagain;

	/* ============================= */

	case X(Tfloat80,Tint8):
	case X(Tfloat80,Tuns8):
	case X(Tfloat80,Tint16):
	case X(Tfloat80,Tuns16):
	case X(Tfloat80,Tint32):
	case X(Tfloat80,Tuns32):
	case X(Tfloat80,Tint64):
	case X(Tfloat80,Tuns64):
	case X(Tfloat80,Tfloat32): e = el_una(OPld_d, TYdouble, e);
				   fty = Tfloat64;
				   goto Lagain;
	case X(Tfloat80,Tfloat64): eop = OPld_d; goto Leop;
	case X(Tfloat80,Timaginary32): goto Lzero;
	case X(Tfloat80,Timaginary64): goto Lzero;
	case X(Tfloat80,Timaginary80): goto Lzero;
	case X(Tfloat80,Tcomplex32):
	case X(Tfloat80,Tcomplex64):
	case X(Tfloat80,Tcomplex80):
	    e = el_bin(OPadd,TYcldouble,e,el_long(TYildouble,0));
	    fty = Tcomplex80;
	    goto Lagain;

	/* ============================= */

	case X(Timaginary32,Tint8):
	case X(Timaginary32,Tuns8):
	case X(Timaginary32,Tint16):
	case X(Timaginary32,Tuns16):
	case X(Timaginary32,Tint32):
	case X(Timaginary32,Tuns32):
	case X(Timaginary32,Tint64):
	case X(Timaginary32,Tuns64):
	case X(Timaginary32,Tfloat32):
	case X(Timaginary32,Tfloat64):
	case X(Timaginary32,Tfloat80):	goto Lzero;
	case X(Timaginary32,Timaginary64): eop = OPf_d;	goto Leop;
	case X(Timaginary32,Timaginary80):
				   e = el_una(OPf_d, TYidouble, e);
				   fty = Timaginary64;
				   goto Lagain;
	case X(Timaginary32,Tcomplex32):
	case X(Timaginary32,Tcomplex64):
	case X(Timaginary32,Tcomplex80):
	    e = el_bin(OPadd,TYcfloat,el_long(TYfloat,0),e);
	    fty = Tcomplex32;
	    goto Lagain;

	/* ============================= */

	case X(Timaginary64,Tint8):
	case X(Timaginary64,Tuns8):
	case X(Timaginary64,Tint16):
	case X(Timaginary64,Tuns16):
	case X(Timaginary64,Tint32):
	case X(Timaginary64,Tuns32):
	case X(Timaginary64,Tint64):
	case X(Timaginary64,Tuns64):
	case X(Timaginary64,Tfloat32):
	case X(Timaginary64,Tfloat64):
	case X(Timaginary64,Tfloat80):	goto Lzero;
	case X(Timaginary64,Timaginary32): eop = OPd_f;   goto Leop;
	case X(Timaginary64,Timaginary80): eop = OPd_ld;  goto Leop;
	case X(Timaginary64,Tcomplex32):
	case X(Timaginary64,Tcomplex64):
	case X(Timaginary64,Tcomplex80):
	    e = el_bin(OPadd,TYcdouble,el_long(TYdouble,0),e);
	    fty = Tcomplex64;
	    goto Lagain;

	/* ============================= */

	case X(Timaginary80,Tint8):
	case X(Timaginary80,Tuns8):
	case X(Timaginary80,Tint16):
	case X(Timaginary80,Tuns16):
	case X(Timaginary80,Tint32):
	case X(Timaginary80,Tuns32):
	case X(Timaginary80,Tint64):
	case X(Timaginary80,Tuns64):
	case X(Timaginary80,Tfloat32):
	case X(Timaginary80,Tfloat64):
	case X(Timaginary80,Tfloat80):	goto Lzero;
	case X(Timaginary80,Timaginary32): e = el_una(OPf_d, TYidouble, e);
				   fty = Timaginary64;
				   goto Lagain;
	case X(Timaginary80,Timaginary64): eop = OPld_d; goto Leop;
	case X(Timaginary80,Tcomplex32):
	case X(Timaginary80,Tcomplex64):
	case X(Timaginary80,Tcomplex80):
	    e = el_bin(OPadd,TYcldouble,el_long(TYldouble,0),e);
	    fty = Tcomplex80;
	    goto Lagain;

	/* ============================= */

	case X(Tcomplex32,Tint8):
	case X(Tcomplex32,Tuns8):
	case X(Tcomplex32,Tint16):
	case X(Tcomplex32,Tuns16):
	case X(Tcomplex32,Tint32):
	case X(Tcomplex32,Tuns32):
	case X(Tcomplex32,Tint64):
	case X(Tcomplex32,Tuns64):
	case X(Tcomplex32,Tfloat32):
	case X(Tcomplex32,Tfloat64):
	case X(Tcomplex32,Tfloat80):
		e = el_una(OPc_r, TYfloat, e);
		fty = Tfloat32;
		goto Lagain;
	case X(Tcomplex32,Timaginary32):
	case X(Tcomplex32,Timaginary64):
	case X(Tcomplex32,Timaginary80):
		e = el_una(OPc_i, TYifloat, e);
		fty = Timaginary32;
		goto Lagain;
	case X(Tcomplex32,Tcomplex64):
	case X(Tcomplex32,Tcomplex80):
		e = el_una(OPf_d, TYcdouble, e);
		fty = Tcomplex64;
		goto Lagain;

	/* ============================= */

	case X(Tcomplex64,Tint8):
	case X(Tcomplex64,Tuns8):
	case X(Tcomplex64,Tint16):
	case X(Tcomplex64,Tuns16):
	case X(Tcomplex64,Tint32):
	case X(Tcomplex64,Tuns32):
	case X(Tcomplex64,Tint64):
	case X(Tcomplex64,Tuns64):
	case X(Tcomplex64,Tfloat32):
	case X(Tcomplex64,Tfloat64):
	case X(Tcomplex64,Tfloat80):
		e = el_una(OPc_r, TYdouble, e);
		fty = Tfloat64;
		goto Lagain;
	case X(Tcomplex64,Timaginary32):
	case X(Tcomplex64,Timaginary64):
	case X(Tcomplex64,Timaginary80):
		e = el_una(OPc_i, TYidouble, e);
		fty = Timaginary64;
		goto Lagain;
	case X(Tcomplex64,Tcomplex32):	 eop = OPd_f;	goto Leop;
	case X(Tcomplex64,Tcomplex80):	 eop = OPd_ld;	goto Leop;

	/* ============================= */

	case X(Tcomplex80,Tint8):
	case X(Tcomplex80,Tuns8):
	case X(Tcomplex80,Tint16):
	case X(Tcomplex80,Tuns16):
	case X(Tcomplex80,Tint32):
	case X(Tcomplex80,Tuns32):
	case X(Tcomplex80,Tint64):
	case X(Tcomplex80,Tuns64):
	case X(Tcomplex80,Tfloat32):
	case X(Tcomplex80,Tfloat64):
	case X(Tcomplex80,Tfloat80):
		e = el_una(OPc_r, TYldouble, e);
		fty = Tfloat80;
		goto Lagain;
	case X(Tcomplex80,Timaginary32):
	case X(Tcomplex80,Timaginary64):
	case X(Tcomplex80,Timaginary80):
		e = el_una(OPc_i, TYildouble, e);
		fty = Timaginary80;
		goto Lagain;
	case X(Tcomplex80,Tcomplex32):
	case X(Tcomplex80,Tcomplex64):
		e = el_una(OPld_d, TYcdouble, e);
		fty = Tcomplex64;
		goto Lagain;

	/* ============================= */

	default:
	    if (fty == tty)
		goto Lpaint;
	    //dump(0);
	    //printf("fty = %d, tty = %d\n", fty, tty);
	    error("e2ir: cannot cast from %s to %s", e1->type->toChars(), t->toChars());
	    goto Lzero;

	Lzero:
	    e = el_long(ttym, 0);
	    break;

	Lpaint:
	    e->Ety = ttym;
	    break;

	Leop:
	    e = el_una(eop, ttym, e);
	    break;
    }
Lret:
    // Adjust for any type paints
    t = type->toBasetype();
    e->Ety = t->totym();

    el_setLoc(e,loc);
    return e;
}

elem *ArrayLengthExp::toElem(IRState *irs)
{
    elem *e = e1->toElem(irs);
    e = el_una(OP64_32, type->totym(), e);
    el_setLoc(e,loc);
    return e;
}

elem *SliceExp::toElem(IRState *irs)
{   elem *e;
    Type *t1;

    //printf("SliceExp::toElem()\n");
    t1 = e1->type->toBasetype();
    e = e1->toElem(irs);
    if (lwr)
    {	elem *elwr;
	elem *elwr2;
	elem *eupr;
	elem *eptr;
	elem *einit;
	int sz;

	einit = resolveLengthVar(lengthVar, &e, t1);

	sz = t1->nextOf()->size();

	elwr = lwr->toElem(irs);
	eupr = upr->toElem(irs);

	elwr2 = el_same(&elwr);

	// Create an array reference where:
	// length is (upr - lwr)
	// pointer is (ptr + lwr*sz)
	// Combine as (length pair ptr)

	if (global.params.useArrayBounds)
	{
	    // Checks (unsigned compares):
	    //	upr <= array.length
	    //	lwr <= upr

	    elem *c1;
	    elem *c2;
	    elem *ea;
	    elem *eb;
	    elem *eupr2;
	    elem *elength;

	    if (t1->ty == Tpointer)
	    {
		// Just do lwr <= upr check

		eupr2 = el_same(&eupr);
		eupr2->Ety = TYuint;			// make sure unsigned comparison
		c1 = el_bin(OPle, TYint, elwr2, eupr2);
		c1 = el_combine(eupr, c1);
		goto L2;
	    }
	    else if (t1->ty == Tsarray)
	    {	TypeSArray *tsa = (TypeSArray *)t1;
		integer_t length = tsa->dim->toInteger();

		elength = el_long(TYuint, length);
		goto L1;
	    }
	    else if (t1->ty == Tarray)
	    {
		if (lengthVar)
		    elength = el_var(lengthVar->toSymbol());
		else
		{
		    elength = e;
		    e = el_same(&elength);
		    elength = el_una(OP64_32, TYuint, elength);
		}
	    L1:
		eupr2 = el_same(&eupr);
		c1 = el_bin(OPle, TYint, eupr, elength);
		eupr2->Ety = TYuint;			// make sure unsigned comparison
		c2 = el_bin(OPle, TYint, elwr2, eupr2);
		c1 = el_bin(OPandand, TYint, c1, c2);	// (c1 && c2)

	    L2:
		// Construct: (c1 || ModuleArray(line))
		Symbol *sassert;

		sassert = irs->blx->module->toModuleArray();
		ea = el_bin(OPcall,TYvoid,el_var(sassert), el_long(TYint, loc.linnum));
		eb = el_bin(OPoror,TYvoid,c1,ea);
		elwr = el_combine(elwr, eb);

		elwr2 = el_copytree(elwr2);
		eupr = el_copytree(eupr2);
	    }
	}

	eptr = array_toPtr(e1->type, e);

	elem *elength = el_bin(OPmin, TYint, eupr, elwr2);
	eptr = el_bin(OPadd, TYnptr, eptr, el_bin(OPmul, TYint, el_copytree(elwr2), el_long(TYint, sz)));

	e = el_pair(TYullong, elength, eptr);
	e = el_combine(elwr, e);
	e = el_combine(einit, e);
    }
    else if (t1->ty == Tsarray)
    {
	e = sarray_toDarray(loc, t1, NULL, e);
    }
    el_setLoc(e,loc);
    return e;
}

elem *IndexExp::toElem(IRState *irs)
{   elem *e;
    elem *n1 = e1->toElem(irs);
    elem *n2;
    elem *eb = NULL;
    Type *t1;

    //printf("IndexExp::toElem() %s\n", toChars());
    t1 = e1->type->toBasetype();
    if (t1->ty == Taarray)
    {
	// set to:
	//	*aaGet(aa, keyti, valuesize, index);

	TypeAArray *taa = (TypeAArray *)t1;
	elem *keyti;
	elem *ep;
	int vsize = taa->next->size();
	elem *valuesize;
	Symbol *s;

	// n2 becomes the index, also known as the key
	n2 = e2->toElem(irs);
	if (n2->Ety == TYstruct || n2->Ety == TYarray)
	{
	    n2 = el_una(OPstrpar, TYstruct, n2);
	    n2->Enumbytes = n2->E1->Enumbytes;
	    //printf("numbytes = %d\n", n2->Enumbytes);
	    assert(n2->Enumbytes);
	}
	valuesize = el_long(TYuint, vsize);	// BUG: should be TYsize_t
	//printf("valuesize: "); elem_print(valuesize);
	if (modifiable)
	{
	    n1 = el_una(OPaddr, TYnptr, n1);
	    s = taa->aaGetSymbol("Get", 1);
	}
	else
	{
	    s = taa->aaGetSymbol("GetRvalue", 1);
	}
	//printf("taa->index = %s\n", taa->index->toChars());
	keyti = taa->index->getInternalTypeInfo(NULL)->toElem(irs);
	//keyti = taa->index->getTypeInfo(NULL)->toElem(irs);
	//printf("keyti:\n");
	//elem_print(keyti);
	ep = el_params(n2, valuesize, keyti, n1, NULL);
	e = el_bin(OPcall, TYnptr, el_var(s), ep);
	if (global.params.useArrayBounds)
	{
	    elem *n;
	    elem *ea;

	    n = el_same(&e);

	    // Construct: ((e || ModuleAssert(line)),n)
	    Symbol *sassert;

	    sassert = irs->blx->module->toModuleArray();
	    ea = el_bin(OPcall,TYvoid,el_var(sassert),
		el_long(TYint, loc.linnum));
	    e = el_bin(OPoror,TYvoid,e,ea);
	    e = el_bin(OPcomma, TYnptr, e, n);
	}
	e = el_una(OPind, type->totym(), e);
	if (tybasic(e->Ety) == TYstruct)
	    e->Enumbytes = type->size();
    }
    else
    {	elem *einit;

	einit = resolveLengthVar(lengthVar, &n1, t1);
	n2 = e2->toElem(irs);

	if (global.params.useArrayBounds)
	{
	    elem *elength;
	    elem *n2x;
	    elem *ea;

	    if (t1->ty == Tsarray)
	    {	TypeSArray *tsa = (TypeSArray *)t1;
		integer_t length = tsa->dim->toInteger();

		elength = el_long(TYuint, length);
		goto L1;
	    }
	    else if (t1->ty == Tarray)
	    {
		elength = n1;
		n1 = el_same(&elength);
		elength = el_una(OP64_32, TYuint, elength);
	    L1:
		n2x = n2;
		n2 = el_same(&n2x);
		n2x = el_bin(OPlt, TYint, n2x, elength);

		// Construct: (n2x || ModuleAssert(line))
		Symbol *sassert;

		sassert = irs->blx->module->toModuleArray();
		ea = el_bin(OPcall,TYvoid,el_var(sassert),
		    el_long(TYint, loc.linnum));
		eb = el_bin(OPoror,TYvoid,n2x,ea);
	    }
	}

	n1 = array_toPtr(t1, n1);

	{   elem *escale;

	    escale = el_long(TYint, t1->nextOf()->size());
	    n2 = el_bin(OPmul, TYint, n2, escale);
	    e = el_bin(OPadd, TYnptr, n1, n2);
	    e = el_una(OPind, type->totym(), e);
	    if (tybasic(e->Ety) == TYstruct || tybasic(e->Ety) == TYarray)
	    {	e->Ety = TYstruct;
		e->Enumbytes = type->size();
	    }
	}

	eb = el_combine(einit, eb);
	e = el_combine(eb, e);
    }
    el_setLoc(e,loc);
    return e;
}


elem *TupleExp::toElem(IRState *irs)
{   elem *e = NULL;

    //printf("TupleExp::toElem() %s\n", toChars());
    for (size_t i = 0; i < exps->dim; i++)
    {	Expression *el = (Expression *)exps->data[i];
	elem *ep = el->toElem(irs);

	e = el_combine(e, ep);
    }
    return e;
}


elem *ArrayLiteralExp::toElem(IRState *irs)
{   elem *e;
    size_t dim;

    //printf("ArrayLiteralExp::toElem() %s\n", toChars());
    if (elements)
    {
	dim = elements->dim;
	e = el_long(TYint, dim);
	for (size_t i = 0; i < dim; i++)
	{   Expression *el = (Expression *)elements->data[i];
	    elem *ep = el->toElem(irs);

	    if (tybasic(ep->Ety) == TYstruct || tybasic(ep->Ety) == TYarray)
	    {
		ep = el_una(OPstrpar, TYstruct, ep);
		ep->Enumbytes = el->type->size();
	    }
	    e = el_param(ep, e);
	}
    }
    else
    {	dim = 0;
	e = el_long(TYint, 0);
    }
    Type *tb = type->toBasetype();
#if 1
    e = el_param(e, type->getTypeInfo(NULL)->toElem(irs));

    // call _d_arrayliteralT(ti, dim, ...)
    e = el_bin(OPcall,TYnptr,el_var(rtlsym[RTLSYM_ARRAYLITERALT]),e);
#else
    e = el_param(e, el_long(TYint, tb->next->size()));

    // call _d_arrayliteral(size, dim, ...)
    e = el_bin(OPcall,TYnptr,el_var(rtlsym[RTLSYM_ARRAYLITERAL]),e);
#endif
    if (tb->ty == Tarray)
    {
	e = el_pair(TYullong, el_long(TYint, dim), e);
    }
    else if (tb->ty == Tpointer)
    {
    }
    else
    {
	e = el_una(OPind,TYstruct,e);
	e->Enumbytes = type->size();
    }

    el_setLoc(e,loc);
    return e;
}


elem *AssocArrayLiteralExp::toElem(IRState *irs)
{   elem *e;
    size_t dim;

    //printf("AssocArrayLiteralExp::toElem() %s\n", toChars());
    dim = keys->dim;
    e = el_long(TYint, dim);
    for (size_t i = 0; i < dim; i++)
    {   Expression *el = (Expression *)keys->data[i];

	for (int j = 0; j < 2; j++)
	{
	    elem *ep = el->toElem(irs);

	    if (tybasic(ep->Ety) == TYstruct || tybasic(ep->Ety) == TYarray)
	    {
		ep = el_una(OPstrpar, TYstruct, ep);
		ep->Enumbytes = el->type->size();
	    }
//printf("[%d] %s\n", i, el->toChars());
//elem_print(ep);
	    e = el_param(ep, e);
	    el = (Expression *)values->data[i];
	}
    }

    Type *t = type->toBasetype()->mutableOf();
    assert(t->ty == Taarray);
    TypeAArray *ta = (TypeAArray *)t;

    /* Unfortunately, the hash function for Aa (array of chars) is custom and
     * different from Axa and Aya, which get the generic hash function.
     * So, rewrite the type of the AArray so that if it's key type
     * is an array of const or invariant, make it an array of mutable.
     */
    Type *tkey = ta->index->toBasetype();
    if (tkey->ty == Tarray)
    {
	tkey = tkey->nextOf()->mutableOf()->arrayOf();
	tkey = tkey->semantic(0, NULL);
	ta = new TypeAArray(ta->nextOf(), tkey);
	ta = (TypeAArray *)ta->merge();
    }

    e = el_param(e, ta->getTypeInfo(NULL)->toElem(irs));

    // call _d_assocarrayliteralT(ti, dim, ...)
    e = el_bin(OPcall,TYnptr,el_var(rtlsym[RTLSYM_ASSOCARRAYLITERALT]),e);

    el_setLoc(e,loc);
    return e;
}


/*******************************************
 * Generate elem to zero fill contents of Symbol stmp
 * from *poffset..offset2.
 * May store anywhere from 0..maxoff, as this function
 * tries to use aligned int stores whereever possible.
 * Update *poffset to end of initialized hole; *poffset will be >= offset2.
 */

elem *fillHole(Symbol *stmp, size_t *poffset, size_t offset2, size_t maxoff)
{   elem *e = NULL;
    int basealign = 1;

    while (*poffset < offset2)
    {   tym_t ty;
	elem *e1;

	if (tybasic(stmp->Stype->Tty) == TYnptr)
	    e1 = el_var(stmp);
	else
	    e1 = el_ptr(stmp);
	if (basealign)
	    *poffset &= ~3;
	basealign = 1;
	size_t sz = maxoff - *poffset;
	switch (sz)
	{   case 1: ty = TYchar;	break;
	    case 2: ty = TYshort;	break;
	    case 3:
		ty = TYshort;
		basealign = 0;
		break;
	    default:
		ty = TYlong;
		break;
	}
	e1 = el_bin(OPadd, TYnptr, e1, el_long(TYsize_t, *poffset));
	e1 = el_una(OPind, ty, e1);
	e1 = el_bin(OPeq, ty, e1, el_long(ty, 0));
	e = el_combine(e, e1);
	*poffset += tysize[ty];
    }
    return e;
}

elem *StructLiteralExp::toElem(IRState *irs)
{   elem *e;
    size_t dim;

    //printf("StructLiteralExp::toElem() %s\n", toChars());

    // struct symbol to initialize with the literal
    Symbol *stmp = sym ? sym : symbol_genauto(sd->type->toCtype());

    e = NULL;

    if (fillHoles)
    {
	/* Initialize all alignment 'holes' to zero.
	 * Do before initializing fields, as the hole filling process
	 * can spill over into the fields.
	 */
	size_t offset = 0;
	for (size_t i = 0; i < sd->fields.dim; i++)
	{
	    Dsymbol *s = (Dsymbol *)sd->fields.data[i];
	    VarDeclaration *v = s->isVarDeclaration();
	    assert(v);

	    e = el_combine(e, fillHole(stmp, &offset, v->offset, sd->structsize));
	    size_t vend = v->offset + v->type->size();
	    if (offset < vend)
		offset = vend;
	}
	e = el_combine(e, fillHole(stmp, &offset, sd->structsize, sd->structsize));
    }

    if (elements)
    {
	dim = elements->dim;
	assert(dim <= sd->fields.dim);
	for (size_t i = 0; i < dim; i++)
	{   Expression *el = (Expression *)elements->data[i];
	    if (!el)
		continue;

	    Dsymbol *s = (Dsymbol *)sd->fields.data[i];
	    VarDeclaration *v = s->isVarDeclaration();
	    assert(v);

	    elem *e1;
	    if (tybasic(stmp->Stype->Tty) == TYnptr)
	    {	e1 = el_var(stmp);
		e1->EV.sp.Voffset = soffset;
	    }
	    else
	    {	e1 = el_ptr(stmp);
		if (soffset)
		    e1 = el_bin(OPadd, TYnptr, e1, el_long(TYsize_t, soffset));
	    }
	    e1 = el_bin(OPadd, TYnptr, e1, el_long(TYsize_t, v->offset));
	    elem *ec = e1;			// pointer to destination

	    elem *ep = el->toElem(irs);

	    Type *t1b = v->type->toBasetype();
	    Type *t2b = el->type->toBasetype();
	    if (t1b->ty == Tsarray)
	    {
		if (t2b->implicitConvTo(t1b))
		{
#if DMDV2
		    // Determine if postblit is needed
		    int postblit = 0;
		    Type *t = t1b;
		    do
		    {
			t = t->nextOf()->toBasetype();
		    } while (t->ty == Tsarray);
		    if (t->ty == Tstruct)
		    {	StructDeclaration *sd = ((TypeStruct *)t)->sym;
			if (sd->postblit)
			    postblit = 1;
		    }

		    if (postblit)
		    {
			/* Generate:
			 *	_d_arrayctor(ti, From: ep, To: e1)
			 */
			Expression *ti = t1b->nextOf()->toBasetype()->getTypeInfo(NULL);
			elem *esize = el_long(TYsize_t, ((TypeSArray *)t1b)->dim->toInteger());
			e1 = el_pair(TYdarray, esize, e1);
			ep = el_pair(TYdarray, el_copytree(esize), array_toPtr(el->type, ep));
			ep = el_params(e1, ep, ti->toElem(irs), NULL);
			int rtl = RTLSYM_ARRAYCTOR;
			e1 = el_bin(OPcall, type->totym(), el_var(rtlsym[rtl]), ep);
		    }
		    else
#endif
		    {
			elem *esize = el_long(TYsize_t, t1b->size());
			ep = array_toPtr(el->type, ep);
			e1 = el_bin(OPmemcpy, TYnptr, e1, el_param(ep, esize));
		    }
		}
		else
		{
		    elem *edim = el_long(TYsize_t, t1b->size() / t2b->size());
		    e1 = setArray(e1, edim, t2b, ep, irs, TOKconstruct);
		}
	    }
	    else
	    {
		tym_t ty = v->type->totym();
		e1 = el_una(OPind, ty, e1);
		if (ty == TYstruct)
		    e1->Enumbytes = v->type->size();
		e1 = el_bin(OPeq, ty, e1, ep);
		if (ty == TYstruct)
		{   e1->Eoper = OPstreq;
		    e1->Enumbytes = v->type->size();
		}
#if DMDV2
		/* Call postBlit() on e1
		 */
		Type *tb = v->type->toBasetype();
		if (tb->ty == Tstruct)
		{   StructDeclaration *sd = ((TypeStruct *)tb)->sym;
		    if (sd->postblit)
		    {	FuncDeclaration *fd = sd->postblit;
			ec = el_copytree(ec);
			ec = callfunc(loc, irs, 1, Type::tvoid, ec, tb->pointerTo(), fd, fd->type, NULL, NULL);
			e1 = el_bin(OPcomma, ec->Ety, e1, ec);
		    }
		}
#endif
	    }
	    e = el_combine(e, e1);
	}
    }

    elem *ev = el_var(stmp);
    ev->Enumbytes = sd->structsize;
    e = el_combine(e, ev);
    el_setLoc(e,loc);
    return e;
}