Files
ldc/s2ir.c
Alexey Prokhin caad8cde58 Squashed 'dmd2/' content from commit 10017d5
git-subtree-dir: dmd2
git-subtree-split: 10017d50eaaff4ecdc37a0153b6c37ea0b004c81
2012-04-05 11:10:48 +04:00

1723 lines
45 KiB
C

// Compiler implementation of the D programming language
// Copyright (c) 2000-2011 by Digital Mars
// All Rights Reserved
// Written by Walter Bright
// http://www.digitalmars.com
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "mars.h"
#include "lexer.h"
#include "statement.h"
#include "expression.h"
#include "mtype.h"
#include "dsymbol.h"
#include "declaration.h"
#include "irstate.h"
#include "init.h"
#include "module.h"
#include "enum.h"
#include "aggregate.h"
#include "template.h"
#include "id.h"
// Back end
#include "cc.h"
#include "type.h"
#include "code.h"
#include "oper.h"
#include "global.h"
#include "dt.h"
#include "rmem.h"
static char __file__[] = __FILE__; // for tassert.h
#include "tassert.h"
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
Expressions *arguments);
elem *exp2_copytotemp(elem *e);
elem *incUsageElem(IRState *irs, Loc loc);
StructDeclaration *needsPostblit(Type *t);
#define elem_setLoc(e,loc) ((e)->Esrcpos.Sfilename = (char *)(loc).filename, \
(e)->Esrcpos.Slinnum = (loc).linnum)
#define SEH (TARGET_WINDOS)
/***********************************************
* Generate code to set index into scope table.
*/
#if SEH
void setScopeIndex(Blockx *blx, block *b, int scope_index)
{
block_appendexp(b, nteh_setScopeTableIndex(blx, scope_index));
}
#else
#define setScopeIndex(blx, b, scope_index) ;
#endif
/****************************************
* Allocate a new block, and set the tryblock.
*/
block *block_calloc(Blockx *blx)
{
block *b = block_calloc();
b->Btry = blx->tryblock;
return b;
}
/**************************************
* Convert label to block.
*/
block *labelToBlock(Loc loc, Blockx *blx, LabelDsymbol *label, int flag = 0)
{
if (!label->statement)
{
error(loc, "undefined label %s", label->toChars());
return NULL;
}
LabelStatement *s = label->statement;
if (!s->lblock)
{ s->lblock = block_calloc(blx);
s->lblock->Btry = NULL; // fill this in later
if (flag)
{
// Keep track of the forward reference to this block, so we can check it later
if (!s->fwdrefs)
s->fwdrefs = new Blocks();
s->fwdrefs->push(blx->curblock);
}
}
return s->lblock;
}
/**************************************
* Add in code to increment usage count for linnum.
*/
void incUsage(IRState *irs, Loc loc)
{
if (global.params.cov && loc.linnum)
{
block_appendexp(irs->blx->curblock, incUsageElem(irs, loc));
}
}
/****************************************
* This should be overridden by each statement class.
*/
void Statement::toIR(IRState *irs)
{
print();
assert(0);
}
/*************************************
*/
void OnScopeStatement::toIR(IRState *irs)
{
}
/****************************************
*/
void IfStatement::toIR(IRState *irs)
{
elem *e;
Blockx *blx = irs->blx;
//printf("IfStatement::toIR('%s')\n", condition->toChars());
IRState mystate(irs, this);
// bexit is the block that gets control after this IfStatement is done
block *bexit = mystate.breakBlock ? mystate.breakBlock : block_calloc();
incUsage(irs, loc);
#if 0
if (match)
{ /* Generate:
* if (match = RTLSYM_IFMATCH(string, pattern)) ...
*/
assert(condition->op == TOKmatch);
e = matchexp_toelem((MatchExp *)condition, &mystate, RTLSYM_IFMATCH);
Symbol *s = match->toSymbol();
symbol_add(s);
e = el_bin(OPeq, TYnptr, el_var(s), e);
}
else
#endif
e = condition->toElemDtor(&mystate);
block_appendexp(blx->curblock, e);
block *bcond = blx->curblock;
block_next(blx, BCiftrue, NULL);
list_append(&bcond->Bsucc, blx->curblock);
if (ifbody)
ifbody->toIR(&mystate);
list_append(&blx->curblock->Bsucc, bexit);
if (elsebody)
{
block_next(blx, BCgoto, NULL);
list_append(&bcond->Bsucc, blx->curblock);
elsebody->toIR(&mystate);
list_append(&blx->curblock->Bsucc, bexit);
}
else
list_append(&bcond->Bsucc, bexit);
block_next(blx, BCgoto, bexit);
}
/**************************************
*/
#if DMDV2
void PragmaStatement::toIR(IRState *irs)
{
//printf("PragmaStatement::toIR()\n");
if (ident == Id::startaddress)
{
assert(args && args->dim == 1);
Expression *e = args->tdata()[0];
Dsymbol *sa = getDsymbol(e);
FuncDeclaration *f = sa->isFuncDeclaration();
assert(f);
Symbol *s = f->toSymbol();
while (irs->prev)
irs = irs->prev;
irs->startaddress = s;
}
}
#endif
/***********************
*/
void WhileStatement::toIR(IRState *irs)
{
assert(0); // was "lowered"
#if 0
Blockx *blx = irs->blx;
/* Create a new state, because we need a new continue and break target
*/
IRState mystate(irs,this);
mystate.breakBlock = block_calloc(blx);
mystate.contBlock = block_calloc(blx);
list_append(&blx->curblock->Bsucc, mystate.contBlock);
block_next(blx, BCgoto, mystate.contBlock);
incUsage(irs, loc);
block_appendexp(mystate.contBlock, condition->toElem(&mystate));
block_next(blx, BCiftrue, NULL);
/* curblock is the start of the while loop body
*/
list_append(&mystate.contBlock->Bsucc, blx->curblock);
if (body)
body->toIR(&mystate);
list_append(&blx->curblock->Bsucc, mystate.contBlock);
block_next(blx, BCgoto, mystate.breakBlock);
list_append(&mystate.contBlock->Bsucc, mystate.breakBlock);
#endif
}
/******************************************
*/
void DoStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
IRState mystate(irs,this);
mystate.breakBlock = block_calloc(blx);
mystate.contBlock = block_calloc(blx);
block *bpre = blx->curblock;
block_next(blx, BCgoto, NULL);
list_append(&bpre->Bsucc, blx->curblock);
list_append(&mystate.contBlock->Bsucc, blx->curblock);
list_append(&mystate.contBlock->Bsucc, mystate.breakBlock);
if (body)
body->toIR(&mystate);
list_append(&blx->curblock->Bsucc, mystate.contBlock);
block_next(blx, BCgoto, mystate.contBlock);
incUsage(irs, condition->loc);
block_appendexp(mystate.contBlock, condition->toElemDtor(&mystate));
block_next(blx, BCiftrue, mystate.breakBlock);
}
/*****************************************
*/
void ForStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
IRState mystate(irs,this);
mystate.breakBlock = block_calloc(blx);
mystate.contBlock = block_calloc(blx);
if (init)
init->toIR(&mystate);
block *bpre = blx->curblock;
block_next(blx,BCgoto,NULL);
block *bcond = blx->curblock;
list_append(&bpre->Bsucc, bcond);
list_append(&mystate.contBlock->Bsucc, bcond);
if (condition)
{
incUsage(irs, condition->loc);
block_appendexp(bcond, condition->toElemDtor(&mystate));
block_next(blx,BCiftrue,NULL);
list_append(&bcond->Bsucc, blx->curblock);
list_append(&bcond->Bsucc, mystate.breakBlock);
}
else
{ /* No conditional, it's a straight goto
*/
block_next(blx,BCgoto,NULL);
list_append(&bcond->Bsucc, blx->curblock);
}
if (body)
body->toIR(&mystate);
/* End of the body goes to the continue block
*/
list_append(&blx->curblock->Bsucc, mystate.contBlock);
block_next(blx, BCgoto, mystate.contBlock);
if (increment)
{
incUsage(irs, increment->loc);
block_appendexp(mystate.contBlock, increment->toElemDtor(&mystate));
}
/* The 'break' block follows the for statement.
*/
block_next(blx,BCgoto, mystate.breakBlock);
}
/**************************************
*/
void ForeachStatement::toIR(IRState *irs)
{
printf("ForeachStatement::toIR() %s\n", toChars());
assert(0); // done by "lowering" in the front end
#if 0
Type *tab;
elem *eaggr;
elem *e;
elem *elength;
tym_t keytym;
//printf("ForeachStatement::toIR()\n");
block *bpre;
block *bcond;
block *bbody;
block *bbodyx;
Blockx *blx = irs->blx;
IRState mystate(irs,this);
mystate.breakBlock = block_calloc(blx);
mystate.contBlock = block_calloc(blx);
tab = aggr->type->toBasetype();
assert(tab->ty == Tarray || tab->ty == Tsarray);
incUsage(irs, aggr->loc);
eaggr = aggr->toElem(irs);
/* Create sp: pointer to start of array data
*/
Symbol *sp = symbol_genauto(TYnptr);
if (tab->ty == Tarray)
{
// stmp is copy of eaggr (the array), so eaggr is evaluated only once
Symbol *stmp;
// Initialize stmp
stmp = symbol_genauto(eaggr);
e = el_bin(OPeq, eaggr->Ety, el_var(stmp), eaggr);
block_appendexp(blx->curblock, e);
// Initialize sp
e = el_una(OPmsw, TYnptr, el_var(stmp));
e = el_bin(OPeq, TYnptr, el_var(sp), e);
block_appendexp(blx->curblock, e);
// Get array.length
elength = el_var(stmp);
elength->Ety = TYsize_t;
}
else // Tsarray
{
// Initialize sp
e = el_una(OPaddr, TYnptr, eaggr);
e = el_bin(OPeq, TYnptr, el_var(sp), e);
block_appendexp(blx->curblock, e);
// Get array.length
elength = el_long(TYsize_t, ((TypeSArray *)tab)->dim->toInteger());
}
Symbol *spmax;
Symbol *skey;
if (key)
{
/* Create skey, the index to the array.
* Initialize skey to 0 (foreach) or .length (foreach_reverse).
*/
skey = key->toSymbol();
symbol_add(skey);
keytym = key->type->totym();
elem *einit = (op == TOKforeach_reverse) ? elength : el_long(keytym, 0);
e = el_bin(OPeq, keytym, el_var(skey), einit);
}
else
{
/* Create spmax, pointer past end of data.
* Initialize spmax = sp + array.length * size
*/
spmax = symbol_genauto(TYnptr);
e = el_bin(OPmul, TYsize_t, elength, el_long(TYsize_t, tab->nextOf()->size()));
e = el_bin(OPadd, TYnptr, el_var(sp), e);
e = el_bin(OPeq, TYnptr, el_var(spmax), e);
/* For foreach_reverse, swap sp and spmax
*/
if (op == TOKforeach_reverse)
{ Symbol *s = sp;
sp = spmax;
spmax = s;
}
}
block_appendexp(blx->curblock, e);
bpre = blx->curblock;
block_next(blx,BCgoto,NULL);
bcond = blx->curblock;
if (key)
{
if (op == TOKforeach_reverse)
{
// Construct (key != 0)
e = el_bin(OPne, TYint, el_var(skey), el_long(keytym, 0));
}
else
{
// Construct (key < elength)
e = el_bin(OPlt, TYint, el_var(skey), elength);
}
}
else
{
if (op == TOKforeach_reverse)
{
// Construct (sp > spmax)
e = el_bin(OPgt, TYint, el_var(sp), el_var(spmax));
}
else
{
// Construct (sp < spmax)
e = el_bin(OPlt, TYint, el_var(sp), el_var(spmax));
}
}
bcond->Belem = e;
block_next(blx, BCiftrue, NULL);
if (op == TOKforeach_reverse)
{
if (key)
{ // Construct (skey -= 1)
e = el_bin(OPminass, keytym, el_var(skey), el_long(keytym, 1));
}
else
{ // Construct (sp--)
e = el_bin(OPminass, TYnptr, el_var(sp), el_long(TYsize_t, tab->nextOf()->size()));
}
block_appendexp(blx->curblock, e);
}
Symbol *s;
FuncDeclaration *fd = NULL;
if (value->toParent2())
fd = value->toParent2()->isFuncDeclaration();
int nrvo = 0;
if (fd && fd->nrvo_can && fd->nrvo_var == value)
{
s = fd->shidden;
nrvo = 1;
}
else
{ s = value->toSymbol();
symbol_add(s);
}
// Construct (value = *sp) or (value = sp[skey * elemsize])
tym_t tym = value->type->totym();
if (key)
{ // sp + skey * elemsize
e = el_bin(OPmul, keytym, el_var(skey), el_long(keytym, tab->nextOf()->size()));
e = el_bin(OPadd, TYnptr, el_var(sp), e);
}
else
e = el_var(sp);
elem *evalue;
#if DMDV2
if (value->offset) // if value is a member of a closure
{
assert(irs->sclosure);
evalue = el_var(irs->sclosure);
evalue = el_bin(OPadd, TYnptr, evalue, el_long(TYint, value->offset));
evalue = el_una(OPind, value->type->totym(), evalue);
}
else
#endif
evalue = el_var(s);
if (value->isOut() || value->isRef())
{
assert(value->storage_class & (STCout | STCref));
e = el_bin(OPeq, TYnptr, evalue, e);
}
else
{
if (nrvo)
evalue = el_una(OPind, tym, evalue);
StructDeclaration *sd = needsPostblit(value->type);
if (tybasic(tym) == TYstruct)
{
e = el_bin(OPeq, tym, evalue, el_una(OPind, tym, e));
e->Eoper = OPstreq;
e->ET = value->type->toCtype();
#if DMDV2
// Call postblit on e
if (sd)
{ FuncDeclaration *fd = sd->postblit;
elem *ec = el_copytree(evalue);
ec = el_una(OPaddr, TYnptr, ec);
ec = callfunc(loc, irs, 1, Type::tvoid, ec, sd->type->pointerTo(), fd, fd->type, NULL, NULL);
e = el_combine(e, ec);
}
#endif
}
else if (tybasic(tym) == TYarray)
{
if (sd)
{
/* Generate:
* _d_arrayctor(ti, efrom, eto)
*/
Expression *ti = value->type->toBasetype()->nextOf()->toBasetype()->getTypeInfo(NULL);
elem *esize = el_long(TYsize_t, ((TypeSArray *)value->type->toBasetype())->dim->toInteger());
elem *eto = el_pair(TYdarray, esize, el_una(OPaddr, TYnptr, evalue));
elem *efrom = el_pair(TYdarray, el_copytree(esize), e);
elem *ep = el_params(eto, efrom, ti->toElem(irs), NULL);
int rtl = RTLSYM_ARRAYCTOR;
e = el_bin(OPcall, TYvoid, el_var(rtlsym[rtl]), ep);
}
else
{
e = el_bin(OPeq, tym, evalue, el_una(OPind, tym, e));
e->Eoper = OPstreq;
e->Ejty = e->Ety = TYstruct;
e->ET = value->type->toCtype();
}
}
else
e = el_bin(OPeq, tym, evalue, el_una(OPind, tym, e));
}
incUsage(irs, loc);
block_appendexp(blx->curblock, e);
bbody = blx->curblock;
if (body)
body->toIR(&mystate);
bbodyx = blx->curblock;
block_next(blx,BCgoto,mystate.contBlock);
if (op == TOKforeach)
{
if (key)
{ // Construct (skey += 1)
e = el_bin(OPaddass, keytym, el_var(skey), el_long(keytym, 1));
}
else
{ // Construct (sp++)
e = el_bin(OPaddass, TYnptr, el_var(sp), el_long(TYsize_t, tab->nextOf()->size()));
}
mystate.contBlock->Belem = e;
}
block_next(blx,BCgoto,mystate.breakBlock);
list_append(&bpre->Bsucc,bcond);
list_append(&bcond->Bsucc,bbody);
list_append(&bcond->Bsucc,mystate.breakBlock);
list_append(&bbodyx->Bsucc,mystate.contBlock);
list_append(&mystate.contBlock->Bsucc,bcond);
#endif
}
/**************************************
*/
#if DMDV2
void ForeachRangeStatement::toIR(IRState *irs)
{
assert(0);
#if 0
Type *tab;
elem *eaggr;
elem *elwr;
elem *eupr;
elem *e;
elem *elength;
tym_t keytym;
//printf("ForeachStatement::toIR()\n");
block *bpre;
block *bcond;
block *bbody;
block *bbodyx;
Blockx *blx = irs->blx;
IRState mystate(irs,this);
mystate.breakBlock = block_calloc(blx);
mystate.contBlock = block_calloc(blx);
incUsage(irs, lwr->loc);
elwr = lwr->toElem(irs);
incUsage(irs, upr->loc);
eupr = upr->toElem(irs);
/* Create skey, the index to the array.
* Initialize skey to elwr (foreach) or eupr (foreach_reverse).
*/
Symbol *skey = key->toSymbol();
symbol_add(skey);
keytym = key->type->totym();
elem *ekey;
if (key->offset) // if key is member of a closure
{
assert(irs->sclosure);
ekey = el_var(irs->sclosure);
ekey = el_bin(OPadd, TYnptr, ekey, el_long(TYint, key->offset));
ekey = el_una(OPind, keytym, ekey);
}
else
ekey = el_var(skey);
elem *einit = (op == TOKforeach_reverse) ? eupr : elwr;
e = el_bin(OPeq, keytym, ekey, einit); // skey = einit;
block_appendexp(blx->curblock, e);
/* Make a copy of the end condition, so it only
* gets evaluated once.
*/
elem *eend = (op == TOKforeach_reverse) ? elwr : eupr;
Symbol *send = symbol_genauto(eend);
e = el_bin(OPeq, eend->Ety, el_var(send), eend);
assert(tybasic(e->Ety) != TYstruct);
block_appendexp(blx->curblock, e);
bpre = blx->curblock;
block_next(blx,BCgoto,NULL);
bcond = blx->curblock;
if (op == TOKforeach_reverse)
{
// Construct (key > elwr)
e = el_bin(OPgt, TYint, el_copytree(ekey), el_var(send));
}
else
{
// Construct (key < eupr)
e = el_bin(OPlt, TYint, el_copytree(ekey), el_var(send));
}
// The size of the increment
size_t sz = 1;
Type *tkeyb = key->type->toBasetype();
if (tkeyb->ty == Tpointer)
sz = tkeyb->nextOf()->size();
bcond->Belem = e;
block_next(blx, BCiftrue, NULL);
if (op == TOKforeach_reverse)
{
// Construct (skey -= 1)
e = el_bin(OPminass, keytym, el_copytree(ekey), el_long(keytym, sz));
block_appendexp(blx->curblock, e);
}
bbody = blx->curblock;
if (body)
body->toIR(&mystate);
bbodyx = blx->curblock;
block_next(blx,BCgoto,mystate.contBlock);
if (op == TOKforeach)
{
// Construct (skey += 1)
e = el_bin(OPaddass, keytym, el_copytree(ekey), el_long(keytym, sz));
mystate.contBlock->Belem = e;
}
block_next(blx,BCgoto,mystate.breakBlock);
list_append(&bpre->Bsucc,bcond);
list_append(&bcond->Bsucc,bbody);
list_append(&bcond->Bsucc,mystate.breakBlock);
list_append(&bbodyx->Bsucc,mystate.contBlock);
list_append(&mystate.contBlock->Bsucc,bcond);
#endif
}
#endif
/****************************************
*/
void BreakStatement::toIR(IRState *irs)
{
block *bbreak;
block *b;
Blockx *blx = irs->blx;
bbreak = irs->getBreakBlock(ident);
assert(bbreak);
b = blx->curblock;
incUsage(irs, loc);
// Adjust exception handler scope index if in different try blocks
if (b->Btry != bbreak->Btry)
{
//setScopeIndex(blx, b, bbreak->Btry ? bbreak->Btry->Bscope_index : -1);
}
/* Nothing more than a 'goto' to the current break destination
*/
list_append(&b->Bsucc, bbreak);
block_next(blx, BCgoto, NULL);
}
/************************************
*/
void ContinueStatement::toIR(IRState *irs)
{
block *bcont;
block *b;
Blockx *blx = irs->blx;
//printf("ContinueStatement::toIR() %p\n", this);
bcont = irs->getContBlock(ident);
assert(bcont);
b = blx->curblock;
incUsage(irs, loc);
// Adjust exception handler scope index if in different try blocks
if (b->Btry != bcont->Btry)
{
//setScopeIndex(blx, b, bcont->Btry ? bcont->Btry->Bscope_index : -1);
}
/* Nothing more than a 'goto' to the current continue destination
*/
list_append(&b->Bsucc, bcont);
block_next(blx, BCgoto, NULL);
}
/**************************************
*/
void el_setVolatile(elem *e)
{
elem_debug(e);
while (1)
{
e->Ety |= mTYvolatile;
if (OTunary(e->Eoper))
e = e->E1;
else if (OTbinary(e->Eoper))
{ el_setVolatile(e->E2);
e = e->E1;
}
else
break;
}
}
void VolatileStatement::toIR(IRState *irs)
{
block *b;
if (statement)
{
Blockx *blx = irs->blx;
block_goto(blx, BCgoto, NULL);
b = blx->curblock;
statement->toIR(irs);
block_goto(blx, BCgoto, NULL);
// Mark the blocks generated as volatile
for (; b != blx->curblock; b = b->Bnext)
{ b->Bflags |= BFLvolatile;
if (b->Belem)
el_setVolatile(b->Belem);
}
}
}
/**************************************
*/
void GotoStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
if (!label->statement)
{ error("label %s is undefined", label->toChars());
return;
}
if (tf != label->statement->tf)
error("cannot goto forward out of or into finally block");
block *bdest = labelToBlock(loc, blx, label, 1);
if (!bdest)
return;
block *b = blx->curblock;
incUsage(irs, loc);
if (b->Btry != bdest->Btry)
{
// Check that bdest is in an enclosing try block
for (block *bt = b->Btry; bt != bdest->Btry; bt = bt->Btry)
{
if (!bt)
{
//printf("b->Btry = %p, bdest->Btry = %p\n", b->Btry, bdest->Btry);
error("cannot goto into try block");
break;
}
}
}
list_append(&b->Bsucc,bdest);
block_next(blx,BCgoto,NULL);
}
void LabelStatement::toIR(IRState *irs)
{
//printf("LabelStatement::toIR() %p, statement = %p\n", this, statement);
Blockx *blx = irs->blx;
block *bc = blx->curblock;
IRState mystate(irs,this);
mystate.ident = ident;
if (lblock)
{
// At last, we know which try block this label is inside
lblock->Btry = blx->tryblock;
/* Go through the forward references and check.
*/
if (fwdrefs)
{
for (size_t i = 0; i < fwdrefs->dim; i++)
{ block *b = fwdrefs->tdata()[i];
if (b->Btry != lblock->Btry)
{
// Check that lblock is in an enclosing try block
for (block *bt = b->Btry; bt != lblock->Btry; bt = bt->Btry)
{
if (!bt)
{
//printf("b->Btry = %p, lblock->Btry = %p\n", b->Btry, lblock->Btry);
error("cannot goto into try block");
break;
}
}
}
}
delete fwdrefs;
fwdrefs = NULL;
}
}
else
lblock = block_calloc(blx);
block_next(blx,BCgoto,lblock);
list_append(&bc->Bsucc,blx->curblock);
if (statement)
statement->toIR(&mystate);
}
/**************************************
*/
void SwitchStatement::toIR(IRState *irs)
{
int string;
Blockx *blx = irs->blx;
//printf("SwitchStatement::toIR()\n");
IRState mystate(irs,this);
mystate.switchBlock = blx->curblock;
/* Block for where "break" goes to
*/
mystate.breakBlock = block_calloc(blx);
/* Block for where "default" goes to.
* If there is a default statement, then that is where default goes.
* If not, then do:
* default: break;
* by making the default block the same as the break block.
*/
mystate.defaultBlock = sdefault ? block_calloc(blx) : mystate.breakBlock;
int numcases = 0;
if (cases)
numcases = cases->dim;
incUsage(irs, loc);
elem *econd = condition->toElemDtor(&mystate);
#if DMDV2
if (hasVars)
{ /* Generate a sequence of if-then-else blocks for the cases.
*/
if (econd->Eoper != OPvar)
{
elem *e = exp2_copytotemp(econd);
block_appendexp(mystate.switchBlock, e);
econd = e->E2;
}
for (int i = 0; i < numcases; i++)
{ CaseStatement *cs = cases->tdata()[i];
elem *ecase = cs->exp->toElemDtor(&mystate);
elem *e = el_bin(OPeqeq, TYbool, el_copytree(econd), ecase);
block *b = blx->curblock;
block_appendexp(b, e);
block *bcase = block_calloc(blx);
cs->cblock = bcase;
block_next(blx, BCiftrue, NULL);
list_append(&b->Bsucc, bcase);
list_append(&b->Bsucc, blx->curblock);
}
/* The final 'else' clause goes to the default
*/
block *b = blx->curblock;
block_next(blx, BCgoto, NULL);
list_append(&b->Bsucc, mystate.defaultBlock);
body->toIR(&mystate);
/* Have the end of the switch body fall through to the block
* following the switch statement.
*/
block_goto(blx, BCgoto, mystate.breakBlock);
return;
}
#endif
if (condition->type->isString())
{
// Number the cases so we can unscramble things after the sort()
for (int i = 0; i < numcases; i++)
{ CaseStatement *cs = cases->tdata()[i];
cs->index = i;
}
cases->sort();
/* Create a sorted array of the case strings, and si
* will be the symbol for it.
*/
dt_t *dt = NULL;
Symbol *si = symbol_generate(SCstatic,type_fake(TYdarray));
#if MACHOBJ
si->Sseg = DATA;
#endif
dtsize_t(&dt, numcases);
dtxoff(&dt, si, PTRSIZE * 2, TYnptr);
for (int i = 0; i < numcases; i++)
{ CaseStatement *cs = cases->tdata()[i];
if (cs->exp->op != TOKstring)
{ error("case '%s' is not a string", cs->exp->toChars()); // BUG: this should be an assert
}
else
{
StringExp *se = (StringExp *)(cs->exp);
unsigned len = se->len;
dtsize_t(&dt, len);
dtabytes(&dt, TYnptr, 0, se->len * se->sz, (char *)se->string);
}
}
si->Sdt = dt;
si->Sfl = FLdata;
outdata(si);
/* Call:
* _d_switch_string(string[] si, string econd)
*/
elem *eparam = el_param(econd, el_var(si));
switch (condition->type->nextOf()->ty)
{
case Tchar:
econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_STRING]), eparam);
break;
case Twchar:
econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_USTRING]), eparam);
break;
case Tdchar: // BUG: implement
econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_DSTRING]), eparam);
break;
default:
assert(0);
}
elem_setLoc(econd, loc);
string = 1;
}
else
string = 0;
block_appendexp(mystate.switchBlock, econd);
block_next(blx,BCswitch,NULL);
// Corresponding free is in block_free
targ_llong *pu = (targ_llong *) ::malloc(sizeof(*pu) * (numcases + 1));
mystate.switchBlock->BS.Bswitch = pu;
/* First pair is the number of cases, and the default block
*/
*pu++ = numcases;
list_append(&mystate.switchBlock->Bsucc, mystate.defaultBlock);
/* Fill in the first entry in each pair, which is the case value.
* CaseStatement::toIR() will fill in
* the second entry for each pair with the block.
*/
for (int i = 0; i < numcases; i++)
{
CaseStatement *cs = cases->tdata()[i];
if (string)
{
pu[cs->index] = i;
}
else
{
pu[i] = cs->exp->toInteger();
}
}
body->toIR(&mystate);
/* Have the end of the switch body fall through to the block
* following the switch statement.
*/
block_goto(blx, BCgoto, mystate.breakBlock);
}
void CaseStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
block *bcase = blx->curblock;
if (!cblock)
cblock = block_calloc(blx);
block_next(blx,BCgoto,cblock);
block *bsw = irs->getSwitchBlock();
if (bsw->BC == BCswitch)
list_append(&bsw->Bsucc,cblock); // second entry in pair
list_append(&bcase->Bsucc,cblock);
if (blx->tryblock != bsw->Btry)
error("case cannot be in different try block level from switch");
incUsage(irs, loc);
if (statement)
statement->toIR(irs);
}
void DefaultStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
block *bcase = blx->curblock;
block *bdefault = irs->getDefaultBlock();
block_next(blx,BCgoto,bdefault);
list_append(&bcase->Bsucc,blx->curblock);
if (blx->tryblock != irs->getSwitchBlock()->Btry)
error("default cannot be in different try block level from switch");
incUsage(irs, loc);
if (statement)
statement->toIR(irs);
}
void GotoDefaultStatement::toIR(IRState *irs)
{
block *b;
Blockx *blx = irs->blx;
block *bdest = irs->getDefaultBlock();
b = blx->curblock;
// The rest is equivalent to GotoStatement
// Adjust exception handler scope index if in different try blocks
if (b->Btry != bdest->Btry)
{
// Check that bdest is in an enclosing try block
for (block *bt = b->Btry; bt != bdest->Btry; bt = bt->Btry)
{
if (!bt)
{
//printf("b->Btry = %p, bdest->Btry = %p\n", b->Btry, bdest->Btry);
error("cannot goto into try block");
break;
}
}
//setScopeIndex(blx, b, bdest->Btry ? bdest->Btry->Bscope_index : -1);
}
list_append(&b->Bsucc,bdest);
incUsage(irs, loc);
block_next(blx,BCgoto,NULL);
}
void GotoCaseStatement::toIR(IRState *irs)
{
block *b;
Blockx *blx = irs->blx;
block *bdest = cs->cblock;
if (!bdest)
{
bdest = block_calloc(blx);
cs->cblock = bdest;
}
b = blx->curblock;
// The rest is equivalent to GotoStatement
// Adjust exception handler scope index if in different try blocks
if (b->Btry != bdest->Btry)
{
// Check that bdest is in an enclosing try block
for (block *bt = b->Btry; bt != bdest->Btry; bt = bt->Btry)
{
if (!bt)
{
//printf("b->Btry = %p, bdest->Btry = %p\n", b->Btry, bdest->Btry);
error("cannot goto into try block");
break;
}
}
//setScopeIndex(blx, b, bdest->Btry ? bdest->Btry->Bscope_index : -1);
}
list_append(&b->Bsucc,bdest);
incUsage(irs, loc);
block_next(blx,BCgoto,NULL);
}
void SwitchErrorStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
//printf("SwitchErrorStatement::toIR()\n");
elem *efilename = el_ptr(blx->module->toSymbol());
elem *elinnum = el_long(TYint, loc.linnum);
elem *e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DSWITCHERR]), el_param(elinnum, efilename));
block_appendexp(blx->curblock, e);
}
/**************************************
*/
void ReturnStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
incUsage(irs, loc);
if (exp)
{ elem *e;
FuncDeclaration *func = irs->getFunc();
assert(func);
assert(func->type->ty == Tfunction);
TypeFunction *tf = (TypeFunction *)(func->type);
enum RET retmethod = tf->retStyle();
if (retmethod == RETstack)
{
elem *es;
/* If returning struct literal, write result
* directly into return value
*/
if (exp->op == TOKstructliteral)
{ StructLiteralExp *se = (StructLiteralExp *)exp;
char save[sizeof(StructLiteralExp)];
memcpy(save, se, sizeof(StructLiteralExp));
se->sym = irs->shidden;
se->soffset = 0;
se->fillHoles = 1;
e = exp->toElemDtor(irs);
memcpy(se, save, sizeof(StructLiteralExp));
}
else
e = exp->toElemDtor(irs);
assert(e);
if (exp->op == TOKstructliteral ||
(func->nrvo_can && func->nrvo_var))
{
// Return value via hidden pointer passed as parameter
// Write exp; return shidden;
es = e;
}
else
{
// Return value via hidden pointer passed as parameter
// Write *shidden=exp; return shidden;
int op;
tym_t ety;
ety = e->Ety;
es = el_una(OPind,ety,el_var(irs->shidden));
op = (tybasic(ety) == TYstruct) ? OPstreq : OPeq;
es = el_bin(op, ety, es, e);
if (op == OPstreq)
es->ET = exp->type->toCtype();
#if DMDV2
/* Call postBlit() on *shidden
*/
Type *tb = exp->type->toBasetype();
//if (tb->ty == Tstruct) exp->dump(0);
if ((exp->op == TOKvar || exp->op == TOKdotvar || exp->op == TOKstar || exp->op == TOKthis) &&
tb->ty == Tstruct)
{ StructDeclaration *sd = ((TypeStruct *)tb)->sym;
if (sd->postblit)
{ FuncDeclaration *fd = sd->postblit;
if (fd->storage_class & STCdisable)
{
fd->toParent()->error(loc, "is not copyable because it is annotated with @disable");
}
elem *ec = el_var(irs->shidden);
ec = callfunc(loc, irs, 1, Type::tvoid, ec, tb->pointerTo(), fd, fd->type, NULL, NULL);
es = el_bin(OPcomma, ec->Ety, es, ec);
}
#if 0
/* It has been moved, so disable destructor
*/
if (exp->op == TOKvar)
{ VarExp *ve = (VarExp *)exp;
VarDeclaration *v = ve->var->isVarDeclaration();
if (v && v->rundtor)
{
elem *er = el_var(v->rundtor->toSymbol());
er = el_bin(OPeq, TYint, er, el_long(TYint, 0));
es = el_bin(OPcomma, TYint, es, er);
}
}
#endif
}
#endif
}
e = el_var(irs->shidden);
e = el_bin(OPcomma, e->Ety, es, e);
}
#if DMDV2
else if (tf->isref)
{ // Reference return, so convert to a pointer
Expression *ae = exp->addressOf(NULL);
e = ae->toElemDtor(irs);
}
#endif
else
{
e = exp->toElemDtor(irs);
assert(e);
}
elem_setLoc(e, loc);
block_appendexp(blx->curblock, e);
block_next(blx, BCretexp, NULL);
}
else
block_next(blx, BCret, NULL);
}
/**************************************
*/
void ExpStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
//printf("ExpStatement::toIR(), exp = %s\n", exp ? exp->toChars() : "");
incUsage(irs, loc);
if (exp)
block_appendexp(blx->curblock,exp->toElemDtor(irs));
}
/**************************************
*/
void DtorExpStatement::toIR(IRState *irs)
{
//printf("DtorExpStatement::toIR(), exp = %s\n", exp ? exp->toChars() : "");
FuncDeclaration *fd = irs->getFunc();
assert(fd);
if (fd->nrvo_can && fd->nrvo_var == var)
/* Do not call destructor, because var is returned as the nrvo variable.
* This is done at this stage because nrvo can be turned off at a
* very late stage in semantic analysis.
*/
;
else
{
ExpStatement::toIR(irs);
}
}
/**************************************
*/
void CompoundStatement::toIR(IRState *irs)
{
if (statements)
{
size_t dim = statements->dim;
for (size_t i = 0 ; i < dim ; i++)
{
Statement *s = statements->tdata()[i];
if (s != NULL)
{
s->toIR(irs);
}
}
}
}
/**************************************
*/
void UnrolledLoopStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
IRState mystate(irs, this);
mystate.breakBlock = block_calloc(blx);
block *bpre = blx->curblock;
block_next(blx, BCgoto, NULL);
block *bdo = blx->curblock;
list_append(&bpre->Bsucc, bdo);
block *bdox;
size_t dim = statements->dim;
for (size_t i = 0 ; i < dim ; i++)
{
Statement *s = statements->tdata()[i];
if (s != NULL)
{
mystate.contBlock = block_calloc(blx);
s->toIR(&mystate);
bdox = blx->curblock;
block_next(blx, BCgoto, mystate.contBlock);
list_append(&bdox->Bsucc, mystate.contBlock);
}
}
bdox = blx->curblock;
block_next(blx, BCgoto, mystate.breakBlock);
list_append(&bdox->Bsucc, mystate.breakBlock);
}
/**************************************
*/
void ScopeStatement::toIR(IRState *irs)
{
if (statement)
{
Blockx *blx = irs->blx;
IRState mystate(irs,this);
if (mystate.prev->ident)
mystate.ident = mystate.prev->ident;
statement->toIR(&mystate);
if (mystate.breakBlock)
block_goto(blx,BCgoto,mystate.breakBlock);
}
}
/***************************************
*/
void WithStatement::toIR(IRState *irs)
{
Symbol *sp;
elem *e;
elem *ei;
ExpInitializer *ie;
Blockx *blx = irs->blx;
//printf("WithStatement::toIR()\n");
if (exp->op == TOKimport || exp->op == TOKtype)
{
}
else
{
// Declare with handle
sp = wthis->toSymbol();
symbol_add(sp);
// Perform initialization of with handle
ie = wthis->init->isExpInitializer();
assert(ie);
ei = ie->exp->toElemDtor(irs);
e = el_var(sp);
e = el_bin(OPeq,e->Ety, e, ei);
elem_setLoc(e, loc);
incUsage(irs, loc);
block_appendexp(blx->curblock,e);
}
// Execute with block
if (body)
body->toIR(irs);
}
/***************************************
*/
void ThrowStatement::toIR(IRState *irs)
{
// throw(exp)
Blockx *blx = irs->blx;
incUsage(irs, loc);
elem *e = exp->toElemDtor(irs);
e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_THROWC]),e);
block_appendexp(blx->curblock, e);
}
/***************************************
* Builds the following:
* _try
* block
* jcatch
* handler
* A try-catch statement.
*/
void TryCatchStatement::toIR(IRState *irs)
{
Blockx *blx = irs->blx;
#if SEH
nteh_declarvars(blx);
#endif
IRState mystate(irs, this);
block *tryblock = block_goto(blx,BCgoto,NULL);
int previndex = blx->scope_index;
tryblock->Blast_index = previndex;
blx->scope_index = tryblock->Bscope_index = blx->next_index++;
// Set the current scope index
setScopeIndex(blx,tryblock,tryblock->Bscope_index);
// This is the catch variable
tryblock->jcatchvar = symbol_genauto(type_fake(mTYvolatile | TYnptr));
blx->tryblock = tryblock;
block *breakblock = block_calloc(blx);
block_goto(blx,BC_try,NULL);
if (body)
{
body->toIR(&mystate);
}
blx->tryblock = tryblock->Btry;
// break block goes here
block_goto(blx, BCgoto, breakblock);
setScopeIndex(blx,blx->curblock, previndex);
blx->scope_index = previndex;
// create new break block that follows all the catches
breakblock = block_calloc(blx);
list_append(&blx->curblock->Bsucc, breakblock);
block_next(blx,BCgoto,NULL);
assert(catches);
for (size_t i = 0 ; i < catches->dim; i++)
{
Catch *cs = catches->tdata()[i];
if (cs->var)
cs->var->csym = tryblock->jcatchvar;
block *bcatch = blx->curblock;
if (cs->type)
bcatch->Bcatchtype = cs->type->toBasetype()->toSymbol();
list_append(&tryblock->Bsucc,bcatch);
block_goto(blx,BCjcatch,NULL);
if (cs->handler != NULL)
{
IRState catchState(irs, this);
cs->handler->toIR(&catchState);
}
list_append(&blx->curblock->Bsucc, breakblock);
block_next(blx, BCgoto, NULL);
}
block_next(blx,(enum BC)blx->curblock->BC, breakblock);
}
/****************************************
* A try-finally statement.
* Builds the following:
* _try
* block
* _finally
* finalbody
* _ret
*/
void TryFinallyStatement::toIR(IRState *irs)
{
//printf("TryFinallyStatement::toIR()\n");
Blockx *blx = irs->blx;
#if SEH
nteh_declarvars(blx);
#endif
block *tryblock = block_goto(blx, BCgoto, NULL);
int previndex = blx->scope_index;
tryblock->Blast_index = previndex;
tryblock->Bscope_index = blx->next_index++;
blx->scope_index = tryblock->Bscope_index;
// Current scope index
setScopeIndex(blx,tryblock,tryblock->Bscope_index);
blx->tryblock = tryblock;
block_goto(blx,BC_try,NULL);
IRState bodyirs(irs, this);
block *breakblock = block_calloc(blx);
block *contblock = block_calloc(blx);
if (body)
body->toIR(&bodyirs);
blx->tryblock = tryblock->Btry; // back to previous tryblock
setScopeIndex(blx,blx->curblock,previndex);
blx->scope_index = previndex;
block_goto(blx,BCgoto, breakblock);
block *finallyblock = block_goto(blx,BCgoto,contblock);
list_append(&tryblock->Bsucc,finallyblock);
block_goto(blx,BC_finally,NULL);
IRState finallyState(irs, this);
breakblock = block_calloc(blx);
contblock = block_calloc(blx);
setScopeIndex(blx, blx->curblock, previndex);
if (finalbody)
finalbody->toIR(&finallyState);
block_goto(blx, BCgoto, contblock);
block_goto(blx, BCgoto, breakblock);
block *retblock = blx->curblock;
block_next(blx,BC_ret,NULL);
list_append(&finallyblock->Bsucc, blx->curblock);
list_append(&retblock->Bsucc, blx->curblock);
}
/****************************************
*/
void SynchronizedStatement::toIR(IRState *irs)
{
assert(0);
}
/****************************************
*/
void AsmStatement::toIR(IRState *irs)
{
block *bpre;
block *basm;
Declaration *d;
Symbol *s;
Blockx *blx = irs->blx;
//printf("AsmStatement::toIR(asmcode = %x)\n", asmcode);
bpre = blx->curblock;
block_next(blx,BCgoto,NULL);
basm = blx->curblock;
list_append(&bpre->Bsucc, basm);
basm->Bcode = asmcode;
basm->Balign = asmalign;
#if 0
if (label)
{ block *b;
b = labelToBlock(loc, blx, label);
printf("AsmStatement::toIR() %p\n", b);
if (b)
list_append(&basm->Bsucc, b);
}
#endif
// Loop through each instruction, fixing Dsymbols into Symbol's
for (code *c = asmcode; c; c = c->next)
{ LabelDsymbol *label;
block *b;
switch (c->IFL1)
{
case FLblockoff:
case FLblock:
// FLblock and FLblockoff have LabelDsymbol's - convert to blocks
label = c->IEVlsym1;
b = labelToBlock(loc, blx, label);
list_append(&basm->Bsucc, b);
c->IEV1.Vblock = b;
break;
case FLdsymbol:
case FLfunc:
s = c->IEVdsym1->toSymbol();
if (s->Sclass == SCauto && s->Ssymnum == -1)
symbol_add(s);
c->IEVsym1 = s;
c->IFL1 = s->Sfl ? s->Sfl : FLauto;
break;
}
// Repeat for second operand
switch (c->IFL2)
{
case FLblockoff:
case FLblock:
label = c->IEVlsym2;
b = labelToBlock(loc, blx, label);
list_append(&basm->Bsucc, b);
c->IEV2.Vblock = b;
break;
case FLdsymbol:
case FLfunc:
d = c->IEVdsym2;
s = d->toSymbol();
if (s->Sclass == SCauto && s->Ssymnum == -1)
symbol_add(s);
c->IEVsym2 = s;
c->IFL2 = s->Sfl ? s->Sfl : FLauto;
if (d->isDataseg())
s->Sflags |= SFLlivexit;
break;
}
//c->print();
}
basm->bIasmrefparam = refparam; // are parameters reference?
basm->usIasmregs = regs; // registers modified
block_next(blx,BCasm, NULL);
list_prepend(&basm->Bsucc, blx->curblock);
if (naked)
{
blx->funcsym->Stype->Tty |= mTYnaked;
}
}
/****************************************
*/
void ImportStatement::toIR(IRState *irs)
{
}