elem *setEthis(Loc loc, IRState *irs, elem *ey, AggregateDeclaration *ad) { elem *ethis; FuncDeclaration *thisfd = irs->getFunc(); int offset = 0; Dsymbol *cdp = ad->toParent2(); // class/func we're nested in //printf("setEthis(ad = %s, cdp = %s, thisfd = %s)\n", ad->toChars(), cdp->toChars(), thisfd->toChars()); if (cdp == thisfd) { /* Class we're new'ing is a local class in this function: * void thisfd() { class ad { } } */ if (irs->sclosure) ethis = el_var(irs->sclosure); else if (irs->sthis) { if (thisfd->hasNestedFrameRefs()) { ethis = el_ptr(irs->sthis); } else ethis = el_var(irs->sthis); } else { ethis = el_long(TYnptr, 0); if (thisfd->hasNestedFrameRefs()) { 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, ad->toParent2()); ethis = el_una(OPaddr, TYnptr, ethis); } ey = el_bin(OPadd, TYnptr, ey, el_long(TYsize_t, ad->vthis->offset)); ey = el_una(OPind, TYnptr, ey); ey = el_bin(OPeq, TYnptr, ey, ethis); return ey; }
/********************************************* * Produce elem which increments the usage count for a particular line. * Used to implement -cov switch (coverage analysis). */ elem *incUsageElem(IRState *irs, Loc loc) { unsigned linnum = loc.linnum; if (!irs->blx->module->cov || !linnum || loc.filename != irs->blx->module->srcfile->toChars()) return NULL; //printf("cov = %p, covb = %p, linnum = %u\n", irs->blx->module->cov, irs->blx->module->covb, p, linnum); linnum--; // from 1-based to 0-based /* Set bit in covb[] indicating this is a valid code line number */ unsigned *p = irs->blx->module->covb; if (p) // covb can be NULL if it has already been written out to its .obj file { assert(linnum < irs->blx->module->numlines); p += linnum / (sizeof(*p) * 8); *p |= 1 << (linnum & (sizeof(*p) * 8 - 1)); } elem *e; e = el_ptr(irs->blx->module->cov); e = el_bin(OPadd, TYnptr, e, el_long(TYuint, linnum * 4)); e = el_una(OPind, TYuint, e); e = el_bin(OPaddass, TYuint, e, el_long(TYuint, 1)); return e; }
/************************************** * Given an expression e that is an array, * determine and set the 'length' variable. * Input: * lengthVar Symbol of 'length' variable * &e expression that is the array * t1 Type of the array * Output: * e is rewritten to avoid side effects * Returns: * expression that initializes 'length' */ elem *resolveLengthVar(VarDeclaration *lengthVar, elem **pe, Type *t1) { //printf("resolveLengthVar()\n"); elem *einit = NULL; if (lengthVar && !(lengthVar->storage_class & STCconst)) { elem *elength; Symbol *slength; if (t1->ty == Tsarray) { TypeSArray *tsa = (TypeSArray *)t1; dinteger_t length = tsa->dim->toInteger(); elength = el_long(TYsize_t, length); goto L3; } else if (t1->ty == Tarray) { elength = *pe; *pe = el_same(&elength); elength = el_una(I64 ? OP128_64 : OP64_32, TYsize_t, elength); L3: slength = toSymbol(lengthVar); //symbol_add(slength); einit = el_bin(OPeq, TYsize_t, el_var(slength), elength); } } return einit; }
elem *Module::toEmodulename() { elem *efilename; // Get filename if (needModuleInfo()) { Symbol *si; /* Class ModuleInfo is defined in std.moduleinfo. * The first member is the name of it, char name[], * which will be at offset 8. */ si = toSymbol(); #if 1 // Use this instead so -fPIC will work efilename = el_ptr(si); efilename = el_bin(OPadd, TYnptr, efilename, el_long(TYuint, 8)); efilename = el_una(OPind, TYdarray, efilename); #else efilename = el_var(si); efilename->Ety = TYdarray; efilename->EV.sp.Voffset += 8; #endif } else // generate our own filename { efilename = toEfilename(); } return efilename; }
symbol *callFuncsAndGates(Module *m, symbols *sctors, StaticDtorDeclarations *ectorgates, const char *id) { symbol *sctor = NULL; if ((sctors && sctors->dim) || (ectorgates && ectorgates->dim)) { static type *t; if (!t) { /* t will be the type of the functions generated: * extern (C) void func(); */ t = type_alloc(TYnfunc); t->Tflags |= TFprototype | TFfixed; t->Tmangle = mTYman_c; t->Tnext = tsvoid; tsvoid->Tcount++; } localgot = NULL; sctor = m->toSymbolX(id, SCglobal, t, "FZv"); cstate.CSpsymtab = &sctor->Sfunc->Flocsym; elem *ector = NULL; if (ectorgates) { for (size_t i = 0; i < ectorgates->dim; i++) { StaticDtorDeclaration *f = (*ectorgates)[i]; Symbol *s = f->vgate->toSymbol(); elem *e = el_var(s); e = el_bin(OPaddass, TYint, e, el_long(TYint, 1)); ector = el_combine(ector, e); } } if (sctors) { for (size_t i = 0; i < sctors->dim; i++) { symbol *s = (*sctors)[i]; elem *e = el_una(OPucall, TYvoid, el_var(s)); ector = el_combine(ector, e); } } block *b = block_calloc(); b->BC = BCret; b->Belem = ector; sctor->Sfunc->Fstartline.Sfilename = m->arg; sctor->Sfunc->Fstartblock = b; writefunc(sctor); } return sctor; }
/************************* * Initialize the hidden aggregate member, vthis, with * the context pointer. * Returns: * *(ey + ad.vthis.offset) = this; */ elem *setEthis(Loc loc, IRState *irs, elem *ey, AggregateDeclaration *ad) { elem *ethis; FuncDeclaration *thisfd = irs->getFunc(); int offset = 0; Dsymbol *adp = ad->toParent2(); // class/func we're nested in //printf("[%s] setEthis(ad = %s, adp = %s, thisfd = %s)\n", loc.toChars(), ad->toChars(), adp->toChars(), thisfd->toChars()); if (adp == thisfd) { ethis = getEthis(loc, irs, ad); } else if (thisfd->vthis && (adp == thisfd->toParent2() || (adp->isClassDeclaration() && adp->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, adp); FuncDeclaration *fdp = adp->isFuncDeclaration(); if (fdp && fdp->hasNestedFrameRefs()) ethis = el_una(OPaddr, TYnptr, ethis); } ey = el_bin(OPadd, TYnptr, ey, el_long(TYsize_t, ad->vthis->offset)); ey = el_una(OPind, TYnptr, ey); ey = el_bin(OPeq, TYnptr, ey, ethis); return ey; }
/****************************************** * Return elem that evaluates to the static frame pointer for function fd. * If fd is a member function, the returned expression will compute the value * of fd's 'this' variable. * This routine is critical for implementing nested functions. */ elem *getEthis(Loc loc, IRState *irs, Dsymbol *fd) { elem *ethis; FuncDeclaration *thisfd = irs->getFunc(); Dsymbol *fdparent = fd->toParent2(); Dsymbol *fdp = fdparent; /* These two are compiler generated functions for the in and out contracts, * and are called from an overriding function, not just the one they're * nested inside, so this hack is so they'll pass */ if (fdparent != thisfd && (fd->ident == Id::require || fd->ident == Id::ensure)) { FuncDeclaration *fdthis = thisfd; for (size_t i = 0; ; ) { if (i == fdthis->foverrides.dim) { if (i == 0) break; fdthis = fdthis->foverrides[0]; i = 0; continue; } if (fdthis->foverrides[i] == fdp) { fdparent = thisfd; break; } i++; } } //printf("[%s] getEthis(thisfd = '%s', fd = '%s', fdparent = '%s')\n", loc.toChars(), thisfd->toPrettyChars(), fd->toPrettyChars(), fdparent->toPrettyChars()); if (fdparent == thisfd) { /* Going down one nesting level, i.e. we're calling * a nested function from its enclosing function. */ if (irs->sclosure && !(fd->ident == Id::require || fd->ident == Id::ensure)) { ethis = el_var(irs->sclosure); } else if (irs->sthis) { // We have a 'this' pointer for the current function /* If no variables in the current function's frame are * referenced by nested functions, then we can 'skip' * adding this frame into the linked list of stack * frames. */ if (thisfd->hasNestedFrameRefs()) { /* Local variables are referenced, can't skip. * Address of 'sthis' gives the 'this' for the nested * function */ ethis = el_ptr(irs->sthis); } else { ethis = el_var(irs->sthis); } } else { /* No 'this' pointer for current function, */ if (thisfd->hasNestedFrameRefs()) { /* OPframeptr is an operator that gets the frame pointer * for the current function, i.e. for the x86 it gets * the value of EBP */ ethis = el_long(TYnptr, 0); ethis->Eoper = OPframeptr; } else { /* Use NULL if no references to the current function's frame */ ethis = el_long(TYnptr, 0); } } } else { if (!irs->sthis) // if no frame pointer for this function { fd->error(loc, "is a nested function and cannot be accessed from %s", irs->getFunc()->toPrettyChars()); return el_long(TYnptr, 0); // error recovery } /* Go up a nesting level, i.e. we need to find the 'this' * of an enclosing function. * Our 'enclosing function' may also be an inner class. */ ethis = el_var(irs->sthis); Dsymbol *s = thisfd; while (fd != s) { FuncDeclaration *fdp = s->toParent2()->isFuncDeclaration(); //printf("\ts = '%s'\n", s->toChars()); thisfd = s->isFuncDeclaration(); if (thisfd) { /* Enclosing function is a function. */ // Error should have been caught by front end assert(thisfd->isNested() || thisfd->vthis); } else { /* Enclosed by an aggregate. That means the current * function must be a member function of that aggregate. */ AggregateDeclaration *ad = s->isAggregateDeclaration(); if (!ad) { Lnoframe: irs->getFunc()->error(loc, "cannot get frame pointer to %s", fd->toPrettyChars()); return el_long(TYnptr, 0); // error recovery } ClassDeclaration *cd = ad->isClassDeclaration(); ClassDeclaration *cdx = fd->isClassDeclaration(); if (cd && cdx && cdx->isBaseOf(cd, NULL)) break; StructDeclaration *sd = ad->isStructDeclaration(); if (fd == sd) break; if (!ad->isNested() || !ad->vthis) goto Lnoframe; ethis = el_bin(OPadd, TYnptr, ethis, el_long(TYsize_t, ad->vthis->offset)); ethis = el_una(OPind, TYnptr, ethis); } if (fdparent == s->toParent2()) break; /* Remember that frames for functions that have no * nested references are skipped in the linked list * of frames. */ if (fdp && fdp->hasNestedFrameRefs()) ethis = el_una(OPind, TYnptr, ethis); s = s->toParent2(); assert(s); } } #if 0 printf("ethis:\n"); elem_print(ethis); printf("\n"); #endif return ethis; }
/************************************* * Closures are implemented by taking the local variables that * need to survive the scope of the function, and copying them * into a gc allocated chuck of memory. That chunk, called the * closure here, is inserted into the linked list of stack * frames instead of the usual stack frame. * * buildClosure() inserts code just after the function prolog * is complete. It allocates memory for the closure, allocates * a local variable (sclosure) to point to it, inserts into it * the link to the enclosing frame, and copies into it the parameters * that are referred to in nested functions. * In VarExp::toElem and SymOffExp::toElem, when referring to a * variable that is in a closure, takes the offset from sclosure rather * than from the frame pointer. * * getEthis() and NewExp::toElem need to use sclosure, if set, rather * than the current frame pointer. */ void buildClosure(FuncDeclaration *fd, IRState *irs) { if (fd->needsClosure()) { // Generate closure on the heap // BUG: doesn't capture variadic arguments passed to this function /* BUG: doesn't handle destructors for the local variables. * The way to do it is to make the closure variables the fields * of a class object: * class Closure { * vtbl[] * monitor * ptr to destructor * sthis * ... closure variables ... * ~this() { call destructor } * } */ //printf("FuncDeclaration::buildClosure() %s\n", toChars()); /* Generate type name for closure struct */ const char *name1 = "CLOSURE."; const char *name2 = fd->toPrettyChars(); size_t namesize = strlen(name1)+strlen(name2)+1; char *closname = (char *) calloc(namesize, sizeof(char)); strcat(strcat(closname, name1), name2); /* Build type for closure */ type *Closstru = type_struct_class(closname, Target::ptrsize, 0, NULL, NULL, false, false, true); symbol_struct_addField(Closstru->Ttag, "__chain", Type_toCtype(Type::tvoidptr), 0); Symbol *sclosure; sclosure = symbol_name("__closptr", SCauto, type_pointer(Closstru)); sclosure->Sflags |= SFLtrue | SFLfree; symbol_add(sclosure); irs->sclosure = sclosure; unsigned offset = Target::ptrsize; // leave room for previous sthis for (size_t i = 0; i < fd->closureVars.dim; i++) { VarDeclaration *v = fd->closureVars[i]; //printf("closure var %s\n", v->toChars()); assert(v->isVarDeclaration()); if (v->needsAutoDtor()) { /* Because the value needs to survive the end of the scope! */ v->error("has scoped destruction, cannot build closure"); } if (v->isargptr) { /* See Bugzilla 2479 * This is actually a bug, but better to produce a nice * message at compile time rather than memory corruption at runtime */ v->error("cannot reference variadic arguments from closure"); } /* Align and allocate space for v in the closure * just like AggregateDeclaration::addField() does. */ unsigned memsize; unsigned memalignsize; structalign_t xalign; if (v->storage_class & STClazy) { /* Lazy variables are really delegates, * so give same answers that TypeDelegate would */ memsize = Target::ptrsize * 2; memalignsize = memsize; xalign = STRUCTALIGN_DEFAULT; } else if (ISWIN64REF(v)) { memsize = v->type->size(); memalignsize = v->type->alignsize(); xalign = v->alignment; } else if (ISREF(v, NULL)) { // reference parameters are just pointers memsize = Target::ptrsize; memalignsize = memsize; xalign = STRUCTALIGN_DEFAULT; } else { memsize = v->type->size(); memalignsize = v->type->alignsize(); xalign = v->alignment; } AggregateDeclaration::alignmember(xalign, memalignsize, &offset); v->offset = offset; offset += memsize; /* Set Sscope to closure */ Symbol *vsym = toSymbol(v); assert(vsym->Sscope == NULL); vsym->Sscope = sclosure; /* Add variable as closure type member */ symbol_struct_addField(Closstru->Ttag, vsym->Sident, vsym->Stype, v->offset); //printf("closure field %s: memalignsize: %i, offset: %i\n", vsym->Sident, memalignsize, v->offset); /* Can't do nrvo if the variable is put in a closure, since * what the shidden points to may no longer exist. */ if (fd->nrvo_can && fd->nrvo_var == v) { fd->nrvo_can = 0; } } // offset is now the size of the closure Closstru->Ttag->Sstruct->Sstructsize = offset; // Allocate memory for the closure elem *e = el_long(TYsize_t, offset); e = el_bin(OPcall, TYnptr, el_var(getRtlsym(RTLSYM_ALLOCMEMORY)), e); toTraceGC(irs, e, &fd->loc); // Assign block of memory to sclosure // sclosure = allocmemory(sz); e = el_bin(OPeq, TYvoid, el_var(sclosure), e); // Set the first element to sthis // *(sclosure + 0) = sthis; elem *ethis; if (irs->sthis) ethis = el_var(irs->sthis); else ethis = el_long(TYnptr, 0); elem *ex = el_una(OPind, TYnptr, el_var(sclosure)); ex = el_bin(OPeq, TYnptr, ex, ethis); e = el_combine(e, ex); // Copy function parameters into closure for (size_t i = 0; i < fd->closureVars.dim; i++) { VarDeclaration *v = fd->closureVars[i]; if (!v->isParameter()) continue; tym_t tym = totym(v->type); bool win64ref = ISWIN64REF(v); if (win64ref) { if (v->storage_class & STClazy) tym = TYdelegate; } else if (ISREF(v, NULL)) tym = TYnptr; // reference parameters are just pointers else if (v->storage_class & STClazy) tym = TYdelegate; ex = el_bin(OPadd, TYnptr, el_var(sclosure), el_long(TYsize_t, v->offset)); ex = el_una(OPind, tym, ex); elem *ev = el_var(toSymbol(v)); if (win64ref) { ev->Ety = TYnptr; ev = el_una(OPind, tym, ev); if (tybasic(ev->Ety) == TYstruct || tybasic(ev->Ety) == TYarray) ev->ET = Type_toCtype(v->type); } if (tybasic(ex->Ety) == TYstruct || tybasic(ex->Ety) == TYarray) { ::type *t = Type_toCtype(v->type); ex->ET = t; ex = el_bin(OPstreq, tym, ex, ev); ex->ET = t; } else ex = el_bin(OPeq, tym, ex, ev); e = el_combine(e, ex); } block_appendexp(irs->blx->curblock, e); } }
void visit(TryCatchStatement *s) { Blockx *blx = irs->blx; #if SEH if (!global.params.is64bit) nteh_declarvars(blx); #endif IRState mystate(irs, s); 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 (s->_body) { Statement_toIR(s->_body, &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); blx->curblock->appendSucc(breakblock); block_next(blx,BCgoto,NULL); assert(s->catches); for (size_t i = 0 ; i < s->catches->dim; i++) { Catch *cs = (*s->catches)[i]; if (cs->var) cs->var->csym = tryblock->jcatchvar; block *bcatch = blx->curblock; if (cs->type) bcatch->Bcatchtype = toSymbol(cs->type->toBasetype()); tryblock->appendSucc(bcatch); block_goto(blx, BCjcatch, NULL); if (cs->handler != NULL) { IRState catchState(irs, s); /* Append to block: * *(sclosure + cs.offset) = cs; */ if (cs->var && cs->var->offset) { tym_t tym = totym(cs->var->type); elem *ex = el_var(irs->sclosure); ex = el_bin(OPadd, TYnptr, ex, el_long(TYsize_t, cs->var->offset)); ex = el_una(OPind, tym, ex); ex = el_bin(OPeq, tym, ex, el_var(toSymbol(cs->var))); block_appendexp(catchState.blx->curblock, ex); } Statement_toIR(cs->handler, &catchState); } blx->curblock->appendSucc(breakblock); block_next(blx, BCgoto, NULL); } block_next(blx,(enum BC)blx->curblock->BC, breakblock); }
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 FuncDeclaration::buildClosure(IRState *irs) { if (needsClosure()) { // Generate closure on the heap // BUG: doesn't capture variadic arguments passed to this function #if DMDV2 /* BUG: doesn't handle destructors for the local variables. * The way to do it is to make the closure variables the fields * of a class object: * class Closure * { vtbl[] * monitor * ptr to destructor * sthis * ... closure variables ... * ~this() { call destructor } * } */ #endif //printf("FuncDeclaration::buildClosure()\n"); Symbol *sclosure; sclosure = symbol_name("__closptr",SCauto,Type::tvoidptr->toCtype()); sclosure->Sflags |= SFLtrue | SFLfree; symbol_add(sclosure); irs->sclosure = sclosure; unsigned offset = PTRSIZE; // leave room for previous sthis for (size_t i = 0; i < closureVars.dim; i++) { VarDeclaration *v = closureVars[i]; assert(v->isVarDeclaration()); #if DMDV2 if (v->needsAutoDtor()) /* Because the value needs to survive the end of the scope! */ v->error("has scoped destruction, cannot build closure"); if (v->isargptr) /* See Bugzilla 2479 * This is actually a bug, but better to produce a nice * message at compile time rather than memory corruption at runtime */ v->error("cannot reference variadic arguments from closure"); #endif /* Align and allocate space for v in the closure * just like AggregateDeclaration::addField() does. */ unsigned memsize; unsigned memalignsize; structalign_t xalign; #if DMDV2 if (v->storage_class & STClazy) { /* Lazy variables are really delegates, * so give same answers that TypeDelegate would */ memsize = PTRSIZE * 2; memalignsize = memsize; xalign = global.structalign; } else if (v->isRef() || v->isOut()) { // reference parameters are just pointers memsize = PTRSIZE; memalignsize = memsize; xalign = global.structalign; } else #endif { memsize = v->type->size(); memalignsize = v->type->alignsize(); xalign = v->alignment; } AggregateDeclaration::alignmember(xalign, memalignsize, &offset); v->offset = offset; offset += memsize; /* Can't do nrvo if the variable is put in a closure, since * what the shidden points to may no longer exist. */ if (nrvo_can && nrvo_var == v) { nrvo_can = 0; } } // offset is now the size of the closure // Allocate memory for the closure elem *e; e = el_long(TYsize_t, offset); e = el_bin(OPcall, TYnptr, el_var(rtlsym[RTLSYM_ALLOCMEMORY]), e); // Assign block of memory to sclosure // sclosure = allocmemory(sz); e = el_bin(OPeq, TYvoid, el_var(sclosure), e); // Set the first element to sthis // *(sclosure + 0) = sthis; elem *ethis; if (irs->sthis) ethis = el_var(irs->sthis); else ethis = el_long(TYnptr, 0); elem *ex = el_una(OPind, TYnptr, el_var(sclosure)); ex = el_bin(OPeq, TYnptr, ex, ethis); e = el_combine(e, ex); // Copy function parameters into closure for (size_t i = 0; i < closureVars.dim; i++) { VarDeclaration *v = closureVars[i]; if (!v->isParameter()) continue; tym_t tym = v->type->totym(); if ( #if !SARRAYVALUE v->type->toBasetype()->ty == Tsarray || #endif v->isOut() || v->isRef()) tym = TYnptr; // reference parameters are just pointers #if DMDV2 else if (v->storage_class & STClazy) tym = TYdelegate; #endif ex = el_bin(OPadd, TYnptr, el_var(sclosure), el_long(TYsize_t, v->offset)); ex = el_una(OPind, tym, ex); if (tybasic(ex->Ety) == TYstruct || tybasic(ex->Ety) == TYarray) { ::type *t = v->type->toCtype(); ex->ET = t; ex = el_bin(OPstreq, tym, ex, el_var(v->toSymbol())); ex->ET = t; } else ex = el_bin(OPeq, tym, ex, el_var(v->toSymbol())); e = el_combine(e, ex); } block_appendexp(irs->blx->curblock, e); } }
void FuncDeclaration_toObjFile(FuncDeclaration *fd, bool multiobj) { ClassDeclaration *cd = fd->parent->isClassDeclaration(); //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", fd, fd->parent->toChars(), fd->toChars()); //if (type) printf("type = %s\n", type->toChars()); #if 0 //printf("line = %d\n", getWhere() / LINEINC); EEcontext *ee = env->getEEcontext(); if (ee->EEcompile == 2) { if (ee->EElinnum < (getWhere() / LINEINC) || ee->EElinnum > (endwhere / LINEINC) ) return; // don't compile this function ee->EEfunc = toSymbol(this); } #endif if (fd->semanticRun >= PASSobj) // if toObjFile() already run return; if (fd->type && fd->type->ty == Tfunction && ((TypeFunction *)fd->type)->next == NULL) return; // If errors occurred compiling it, such as bugzilla 6118 if (fd->type && fd->type->ty == Tfunction && ((TypeFunction *)fd->type)->next->ty == Terror) return; if (fd->semantic3Errors) return; if (global.errors) return; if (!fd->fbody) return; UnitTestDeclaration *ud = fd->isUnitTestDeclaration(); if (ud && !global.params.useUnitTests) return; if (multiobj && !fd->isStaticDtorDeclaration() && !fd->isStaticCtorDeclaration()) { obj_append(fd); return; } if (fd->semanticRun == PASSsemanticdone) { /* What happened is this function failed semantic3() with errors, * but the errors were gagged. * Try to reproduce those errors, and then fail. */ fd->error("errors compiling the function"); return; } assert(fd->semanticRun == PASSsemantic3done); assert(fd->ident != Id::empty); for (FuncDeclaration *fd2 = fd; fd2; ) { if (fd2->inNonRoot()) return; if (fd2->isNested()) fd2 = fd2->toParent2()->isFuncDeclaration(); else break; } if (UnitTestDeclaration *udp = needsDeferredNested(fd)) { /* Can't do unittest's out of order, they are order dependent in that their * execution is done in lexical order. */ udp->deferredNested.push(fd); //printf("%s @[%s]\n\t--> pushed to unittest @[%s]\n", // fd->toPrettyChars(), fd->loc.toChars(), udp->loc.toChars()); return; } if (fd->isArrayOp && isDruntimeArrayOp(fd->ident)) { // Implementation is in druntime return; } // start code generation fd->semanticRun = PASSobj; if (global.params.verbose) fprintf(global.stdmsg, "function %s\n", fd->toPrettyChars()); Symbol *s = toSymbol(fd); func_t *f = s->Sfunc; // tunnel type of "this" to debug info generation if (AggregateDeclaration* ad = fd->parent->isAggregateDeclaration()) { ::type* t = Type_toCtype(ad->getType()); if (cd) t = t->Tnext; // skip reference f->Fclass = (Classsym *)t; } /* This is done so that the 'this' pointer on the stack is the same * distance away from the function parameters, so that an overriding * function can call the nested fdensure or fdrequire of its overridden function * and the stack offsets are the same. */ if (fd->isVirtual() && (fd->fensure || fd->frequire)) f->Fflags3 |= Ffakeeh; #if TARGET_OSX s->Sclass = SCcomdat; #else s->Sclass = SCglobal; #endif for (Dsymbol *p = fd->parent; p; p = p->parent) { if (p->isTemplateInstance()) { s->Sclass = SCcomdat; break; } } /* Vector operations should be comdat's */ if (fd->isArrayOp) s->Sclass = SCcomdat; if (fd->inlinedNestedCallees) { /* Bugzilla 15333: If fd contains inlined expressions that come from * nested function bodies, the enclosing of the functions must be * generated first, in order to calculate correct frame pointer offset. */ for (size_t i = 0; i < fd->inlinedNestedCallees->dim; i++) { FuncDeclaration *f = (*fd->inlinedNestedCallees)[i]; FuncDeclaration *fp = f->toParent2()->isFuncDeclaration();; if (fp && fp->semanticRun < PASSobj) { toObjFile(fp, multiobj); } } } if (fd->isNested()) { //if (!(config.flags3 & CFG3pic)) // s->Sclass = SCstatic; f->Fflags3 |= Fnested; /* The enclosing function must have its code generated first, * in order to calculate correct frame pointer offset. */ FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration(); if (fdp && fdp->semanticRun < PASSobj) { toObjFile(fdp, multiobj); } } else { const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; // Pull in RTL startup code (but only once) if (fd->isMain() && onlyOneMain(fd->loc)) { #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS objmod->external_def("_main"); objmod->ehsections(); // initialize exception handling sections #endif if (global.params.mscoff) { objmod->external_def("main"); objmod->ehsections(); // initialize exception handling sections } else if (config.exe == EX_WIN32) { objmod->external_def("_main"); objmod->external_def("__acrtused_con"); } objmod->includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && fd->linkage == LINKc) { if (global.params.mscoff) { objmod->includelib("LIBCMT"); objmod->includelib("OLDNAMES"); } else if (config.exe == EX_WIN32) { objmod->external_def("__acrtused_con"); // bring in C startup code objmod->includelib("snn.lib"); // bring in C runtime library } s->Sclass = SCglobal; } #if TARGET_WINDOS else if (fd->isWinMain() && onlyOneMain(fd->loc)) { if (global.params.mscoff) { objmod->includelib("uuid"); objmod->includelib("LIBCMT"); objmod->includelib("OLDNAMES"); objmod->ehsections(); // initialize exception handling sections } else { objmod->external_def("__acrtused"); } objmod->includelib(libname); s->Sclass = SCglobal; } // Pull in RTL startup code else if (fd->isDllMain() && onlyOneMain(fd->loc)) { if (global.params.mscoff) { objmod->includelib("uuid"); objmod->includelib("LIBCMT"); objmod->includelib("OLDNAMES"); objmod->ehsections(); // initialize exception handling sections } else { objmod->external_def("__acrtused_dll"); } objmod->includelib(libname); s->Sclass = SCglobal; } #endif } symtab_t *symtabsave = cstate.CSpsymtab; cstate.CSpsymtab = &f->Flocsym; // Find module m for this function Module *m = NULL; for (Dsymbol *p = fd->parent; p; p = p->parent) { m = p->isModule(); if (m) break; } IRState irs(m, fd); Dsymbols deferToObj; // write these to OBJ file later irs.deferToObj = &deferToObj; void *labels = NULL; irs.labels = &labels; symbol *shidden = NULL; Symbol *sthis = NULL; tym_t tyf = tybasic(s->Stype->Tty); //printf("linkage = %d, tyf = x%x\n", linkage, tyf); int reverse = tyrevfunc(s->Stype->Tty); assert(fd->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)fd->type; RET retmethod = retStyle(tf); if (retmethod == RETstack) { // If function returns a struct, put a pointer to that // as the first argument ::type *thidden = Type_toCtype(tf->next->pointerTo()); char hiddenparam[5+4+1]; static int hiddenparami; // how many we've generated so far sprintf(hiddenparam,"__HID%d",++hiddenparami); shidden = symbol_name(hiddenparam,SCparameter,thidden); shidden->Sflags |= SFLtrue | SFLfree; if (fd->nrvo_can && fd->nrvo_var && fd->nrvo_var->nestedrefs.dim) type_setcv(&shidden->Stype, shidden->Stype->Tty | mTYvolatile); irs.shidden = shidden; fd->shidden = shidden; } else { // Register return style cannot make nrvo. // Auto functions keep the nrvo_can flag up to here, // so we should eliminate it before entering backend. fd->nrvo_can = 0; } if (fd->vthis) { assert(!fd->vthis->csym); sthis = toSymbol(fd->vthis); irs.sthis = sthis; if (!(f->Fflags3 & Fnested)) f->Fflags3 |= Fmember; } // Estimate number of parameters, pi size_t pi = (fd->v_arguments != NULL); if (fd->parameters) pi += fd->parameters->dim; // Create a temporary buffer, params[], to hold function parameters Symbol *paramsbuf[10]; Symbol **params = paramsbuf; // allocate on stack if possible if (pi + 2 > 10) // allow extra 2 for sthis and shidden { params = (Symbol **)malloc((pi + 2) * sizeof(Symbol *)); assert(params); } // Get the actual number of parameters, pi, and fill in the params[] pi = 0; if (fd->v_arguments) { params[pi] = toSymbol(fd->v_arguments); pi += 1; } if (fd->parameters) { for (size_t i = 0; i < fd->parameters->dim; i++) { VarDeclaration *v = (*fd->parameters)[i]; //printf("param[%d] = %p, %s\n", i, v, v->toChars()); assert(!v->csym); params[pi + i] = toSymbol(v); } pi += fd->parameters->dim; } if (reverse) { // Reverse params[] entries for (size_t i = 0; i < pi/2; i++) { Symbol *sptmp = params[i]; params[i] = params[pi - 1 - i]; params[pi - 1 - i] = sptmp; } } if (shidden) { #if 0 // shidden becomes last parameter params[pi] = shidden; #else // shidden becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = shidden; #endif pi++; } if (sthis) { #if 0 // sthis becomes last parameter params[pi] = sthis; #else // sthis becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = sthis; #endif pi++; } if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && fd->linkage != LINKd && shidden && sthis) { /* swap shidden and sthis */ Symbol *sp = params[0]; params[0] = params[1]; params[1] = sp; } for (size_t i = 0; i < pi; i++) { Symbol *sp = params[i]; sp->Sclass = SCparameter; sp->Sflags &= ~SFLspill; sp->Sfl = FLpara; symbol_add(sp); } // Determine register assignments if (pi) { FuncParamRegs fpr(tyf); for (size_t i = 0; i < pi; i++) { Symbol *sp = params[i]; if (fpr.alloc(sp->Stype, sp->Stype->Tty, &sp->Spreg, &sp->Spreg2)) { sp->Sclass = (config.exe == EX_WIN64) ? SCshadowreg : SCfastpar; sp->Sfl = (sp->Sclass == SCshadowreg) ? FLpara : FLfast; } } } // Done with params if (params != paramsbuf) free(params); params = NULL; if (fd->fbody) { localgot = NULL; Statement *sbody = fd->fbody; Blockx bx; memset(&bx,0,sizeof(bx)); bx.startblock = block_calloc(); bx.curblock = bx.startblock; bx.funcsym = s; bx.scope_index = -1; bx.classdec = cd; bx.member = fd; bx.module = fd->getModule(); irs.blx = &bx; // Initialize argptr if (fd->v_argptr) { // Declare va_argsave if (global.params.is64bit && !global.params.isWindows) { type *t = type_struct_class("__va_argsave_t", 16, 8 * 6 + 8 * 16 + 8 * 3, NULL, NULL, false, false, true); // The backend will pick this up by name Symbol *s = symbol_name("__va_argsave", SCauto, t); s->Stype->Tty |= mTYvolatile; symbol_add(s); } Symbol *s = toSymbol(fd->v_argptr); symbol_add(s); elem *e = el_una(OPva_start, TYnptr, el_ptr(s)); block_appendexp(irs.blx->curblock, e); } /* Doing this in semantic3() caused all kinds of problems: * 1. couldn't reliably get the final mangling of the function name due to fwd refs * 2. impact on function inlining * 3. what to do when writing out .di files, or other pretty printing */ if (global.params.trace && !fd->isCMain()) { /* The profiler requires TLS, and TLS may not be set up yet when C main() * gets control (i.e. OSX), leading to a crash. */ /* Wrap the entire function body in: * trace_pro("funcname"); * try * body; * finally * _c_trace_epi(); */ StringExp *se = StringExp::create(Loc(), s->Sident); se->type = Type::tstring; se->type = se->type->semantic(Loc(), NULL); Expressions *exps = Expressions_create(); exps->push(se); FuncDeclaration *fdpro = FuncDeclaration::genCfunc(NULL, Type::tvoid, "trace_pro"); Expression *ec = VarExp::create(Loc(), fdpro); Expression *e = CallExp::create(Loc(), ec, exps); e->type = Type::tvoid; Statement *sp = ExpStatement::create(fd->loc, e); FuncDeclaration *fdepi = FuncDeclaration::genCfunc(NULL, Type::tvoid, "_c_trace_epi"); ec = VarExp::create(Loc(), fdepi); e = CallExp::create(Loc(), ec); e->type = Type::tvoid; Statement *sf = ExpStatement::create(fd->loc, e); Statement *stf; if (sbody->blockExit(fd, false) == BEfallthru) stf = CompoundStatement::create(Loc(), sbody, sf); else stf = TryFinallyStatement::create(Loc(), sbody, sf); sbody = CompoundStatement::create(Loc(), sp, stf); } if (fd->interfaceVirtual) { // Adjust the 'this' pointer instead of using a thunk assert(irs.sthis); elem *ethis = el_var(irs.sthis); elem *e = el_bin(OPminass, TYnptr, ethis, el_long(TYsize_t, fd->interfaceVirtual->offset)); block_appendexp(irs.blx->curblock, e); } buildClosure(fd, &irs); if (config.ehmethod == EH_WIN32 && fd->isSynchronized() && cd && !fd->isStatic() && !sbody->usesEH() && !global.params.trace) { /* The "jmonitor" hack uses an optimized exception handling frame * which is a little shorter than the more general EH frame. */ s->Sfunc->Fflags3 |= Fjmonitor; } Statement_toIR(sbody, &irs); bx.curblock->BC = BCret; f->Fstartblock = bx.startblock; // einit = el_combine(einit,bx.init); if (fd->isCtorDeclaration()) { assert(sthis); for (block *b = f->Fstartblock; b; b = b->Bnext) { if (b->BC == BCret) { b->BC = BCretexp; b->Belem = el_combine(b->Belem, el_var(sthis)); } } } insertFinallyBlockCalls(f->Fstartblock); } // If static constructor if (fd->isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration { ssharedctors.push(s); } else if (fd->isStaticCtorDeclaration()) { sctors.push(s); } // If static destructor if (fd->isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration { SharedStaticDtorDeclaration *f = fd->isSharedStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ esharedctorgates.push(f); } sshareddtors.shift(s); } else if (fd->isStaticDtorDeclaration()) { StaticDtorDeclaration *f = fd->isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } sdtors.shift(s); } // If unit test if (ud) { stests.push(s); } if (global.errors) { // Restore symbol table cstate.CSpsymtab = symtabsave; return; } writefunc(s); // Restore symbol table cstate.CSpsymtab = symtabsave; if (fd->isExport()) objmod->export_symbol(s, Para.offset); for (size_t i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (*irs.deferToObj)[i]; toObjFile(s, false); } if (ud) { for (size_t i = 0; i < ud->deferredNested.dim; i++) { FuncDeclaration *fd = ud->deferredNested[i]; toObjFile(fd, false); } } #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS // A hack to get a pointer to this function put in the .dtors segment if (fd->ident && memcmp(fd->ident->toChars(), "_STD", 4) == 0) objmod->staticdtor(s); #endif if (irs.startaddress) { //printf("Setting start address\n"); objmod->startaddress(irs.startaddress); } }
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 }
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 }
void visit(ReturnStatement *s) { Blockx *blx = irs->blx; enum BC bc; incUsage(irs, s->loc); if (s->exp) { elem *e; FuncDeclaration *func = irs->getFunc(); assert(func); assert(func->type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)(func->type); RET retmethod = retStyle(tf); if (retmethod == RETstack) { elem *es; /* If returning struct literal, write result * directly into return value */ if (s->exp->op == TOKstructliteral) { StructLiteralExp *se = (StructLiteralExp *)s->exp; char save[sizeof(StructLiteralExp)]; memcpy(save, (void*)se, sizeof(StructLiteralExp)); se->sym = irs->shidden; se->soffset = 0; se->fillHoles = 1; e = toElemDtor(s->exp, irs); memcpy((void*)se, save, sizeof(StructLiteralExp)); } else e = toElemDtor(s->exp, irs); assert(e); if (s->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 = Type_toCtype(s->exp->type); } e = el_var(irs->shidden); e = el_bin(OPcomma, e->Ety, es, e); } else if (tf->isref) { // Reference return, so convert to a pointer e = toElemDtor(s->exp, irs); e = addressElem(e, s->exp->type->pointerTo()); } else { e = toElemDtor(s->exp, irs); assert(e); } elem_setLoc(e, s->loc); block_appendexp(blx->curblock, e); bc = BCretexp; } else bc = BCret; if (block *finallyBlock = irs->getFinallyBlock()) { assert(finallyBlock->BC == BC_finally); blx->curblock->appendSucc(finallyBlock); } block_next(blx, bc, NULL); }
void FuncDeclaration::toObjFile(int multiobj) { Symbol *s; func_t *f; Symbol *senter; Symbol *sexit; FuncDeclaration *func = this; ClassDeclaration *cd = func->parent->isClassDeclaration(); int reverse; int i; int has_arguments; //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", func, parent->toChars(), func->toChars()); #if 0 //printf("line = %d\n",func->getWhere() / LINEINC); EEcontext *ee = env->getEEcontext(); if (ee->EEcompile == 2) { if (ee->EElinnum < (func->getWhere() / LINEINC) || ee->EElinnum > (func->endwhere / LINEINC) ) return; // don't compile this function ee->EEfunc = func->toSymbol(); } #endif if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration()) { obj_append(this); return; } if (semanticRun >= 5) // if toObjFile() already run return; semanticRun = 5; if (!func->fbody) { return; } if (func->isUnitTestDeclaration() && !global.params.useUnitTests) return; if (global.params.verbose) printf("function %s\n",func->toChars()); s = func->toSymbol(); f = s->Sfunc; #if TARGET_WINDOS /* This is done so that the 'this' pointer on the stack is the same * distance away from the function parameters, so that an overriding * function can call the nested fdensure or fdrequire of its overridden function * and the stack offsets are the same. */ if (isVirtual() && (fensure || frequire)) f->Fflags3 |= Ffakeeh; #endif #if TARGET_OSX s->Sclass = SCcomdat; #else s->Sclass = SCglobal; #endif for (Dsymbol *p = parent; p; p = p->parent) { if (p->isTemplateInstance()) { s->Sclass = SCcomdat; break; } } if (isNested()) { // if (!(config.flags3 & CFG3pic)) // s->Sclass = SCstatic; f->Fflags3 |= Fnested; } else { const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; // Pull in RTL startup code if (func->isMain()) { objextdef("_main"); #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS obj_ehsections(); // initialize exception handling sections #else objextdef("__acrtused_con"); #endif obj_includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && linkage == LINKc) s->Sclass = SCglobal; else if (func->isWinMain()) { objextdef("__acrtused"); obj_includelib(libname); s->Sclass = SCglobal; } // Pull in RTL startup code else if (func->isDllMain()) { objextdef("__acrtused_dll"); obj_includelib(libname); s->Sclass = SCglobal; } } cstate.CSpsymtab = &f->Flocsym; // Find module m for this function Module *m = NULL; for (Dsymbol *p = parent; p; p = p->parent) { m = p->isModule(); if (m) break; } IRState irs(m, func); Array deferToObj; // write these to OBJ file later irs.deferToObj = &deferToObj; TypeFunction *tf; enum RET retmethod; symbol *shidden = NULL; Symbol *sthis = NULL; tym_t tyf; tyf = tybasic(s->Stype->Tty); //printf("linkage = %d, tyf = x%x\n", linkage, tyf); reverse = tyrevfunc(s->Stype->Tty); assert(func->type->ty == Tfunction); tf = (TypeFunction *)(func->type); has_arguments = (tf->linkage == LINKd) && (tf->varargs == 1); retmethod = tf->retStyle(); if (retmethod == RETstack) { // If function returns a struct, put a pointer to that // as the first argument ::type *thidden = tf->next->pointerTo()->toCtype(); char hiddenparam[5+4+1]; static int hiddenparami; // how many we've generated so far sprintf(hiddenparam,"__HID%d",++hiddenparami); shidden = symbol_name(hiddenparam,SCparameter,thidden); shidden->Sflags |= SFLtrue | SFLfree; #if DMDV1 if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedref) #else if (func->nrvo_can && func->nrvo_var && func->nrvo_var->nestedrefs.dim) #endif type_setcv(&shidden->Stype, shidden->Stype->Tty | mTYvolatile); irs.shidden = shidden; this->shidden = shidden; } if (vthis) { assert(!vthis->csym); sthis = vthis->toSymbol(); irs.sthis = sthis; if (!(f->Fflags3 & Fnested)) f->Fflags3 |= Fmember; } Symbol **params; unsigned pi; // Estimate number of parameters, pi pi = (v_arguments != NULL); if (parameters) pi += parameters->dim; // Allow extra 2 for sthis and shidden params = (Symbol **)alloca((pi + 2) * sizeof(Symbol *)); // Get the actual number of parameters, pi, and fill in the params[] pi = 0; if (v_arguments) { params[pi] = v_arguments->toSymbol(); pi += 1; } if (parameters) { for (i = 0; i < parameters->dim; i++) { VarDeclaration *v = (VarDeclaration *)parameters->data[i]; if (v->csym) { error("compiler error, parameter '%s', bugzilla 2962?", v->toChars()); assert(0); } params[pi + i] = v->toSymbol(); } pi += i; } if (reverse) { // Reverse params[] entries for (i = 0; i < pi/2; i++) { Symbol *sptmp; sptmp = params[i]; params[i] = params[pi - 1 - i]; params[pi - 1 - i] = sptmp; } } if (shidden) { #if 0 // shidden becomes last parameter params[pi] = shidden; #else // shidden becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = shidden; #endif pi++; } if (sthis) { #if 0 // sthis becomes last parameter params[pi] = sthis; #else // sthis becomes first parameter memmove(params + 1, params, pi * sizeof(params[0])); params[0] = sthis; #endif pi++; } if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && linkage != LINKd && shidden && sthis) { /* swap shidden and sthis */ Symbol *sp = params[0]; params[0] = params[1]; params[1] = sp; } for (i = 0; i < pi; i++) { Symbol *sp = params[i]; sp->Sclass = SCparameter; sp->Sflags &= ~SFLspill; sp->Sfl = FLpara; symbol_add(sp); } // First parameter goes in register if (pi) { Symbol *sp = params[0]; if ((tyf == TYjfunc || tyf == TYmfunc) && type_jparam(sp->Stype)) { sp->Sclass = SCfastpar; sp->Spreg = (tyf == TYjfunc) ? AX : CX; sp->Sfl = FLauto; //printf("'%s' is SCfastpar\n",sp->Sident); } } if (func->fbody) { block *b; Blockx bx; Statement *sbody; localgot = NULL; sbody = func->fbody; memset(&bx,0,sizeof(bx)); bx.startblock = block_calloc(); bx.curblock = bx.startblock; bx.funcsym = s; bx.scope_index = -1; bx.classdec = cd; bx.member = func; bx.module = getModule(); irs.blx = &bx; buildClosure(&irs); #if 0 if (func->isSynchronized()) { if (cd) { elem *esync; if (func->isStatic()) { // monitor is in ClassInfo esync = el_ptr(cd->toSymbol()); } else { // 'this' is the monitor esync = el_var(sthis); } if (func->isStatic() || sbody->usesEH() || !(config.flags2 & CFG2seh)) { // BUG: what if frequire or fensure uses EH? sbody = new SynchronizedStatement(func->loc, esync, sbody); } else { #if TARGET_WINDOS if (config.flags2 & CFG2seh) { /* The "jmonitor" uses an optimized exception handling frame * which is a little shorter than the more general EH frame. * It isn't strictly necessary. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif el_free(esync); } } else { error("synchronized function %s must be a member of a class", func->toChars()); } } #elif TARGET_WINDOS if (func->isSynchronized() && cd && config.flags2 & CFG2seh && !func->isStatic() && !sbody->usesEH()) { /* The "jmonitor" hack uses an optimized exception handling frame * which is a little shorter than the more general EH frame. */ s->Sfunc->Fflags3 |= Fjmonitor; } #endif sbody->toIR(&irs); bx.curblock->BC = BCret; f->Fstartblock = bx.startblock; // einit = el_combine(einit,bx.init); if (isCtorDeclaration()) { assert(sthis); for (b = f->Fstartblock; b; b = b->Bnext) { if (b->BC == BCret) { b->BC = BCretexp; b->Belem = el_combine(b->Belem, el_var(sthis)); } } } } // If static constructor if (isStaticConstructor()) { elem *e = el_una(OPucall, TYvoid, el_var(s)); ector = el_combine(ector, e); } // If static destructor if (isStaticDestructor()) { elem *e; #if STATICCTOR e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_FATEXIT]), el_ptr(s)); ector = el_combine(ector, e); dtorcount++; #else StaticDtorDeclaration *f = isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } e = el_una(OPucall, TYvoid, el_var(s)); edtor = el_combine(e, edtor); #endif } // If unit test if (isUnitTestDeclaration()) { elem *e = el_una(OPucall, TYvoid, el_var(s)); etest = el_combine(etest, e); } if (global.errors) return; writefunc(s); if (isExport()) obj_export(s, Poffset); for (i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (Dsymbol *)irs.deferToObj->data[i]; s->toObjFile(0); } #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS // A hack to get a pointer to this function put in the .dtors segment if (ident && memcmp(ident->toChars(), "_STD", 4) == 0) obj_staticdtor(s); #endif #if DMDV2 if (irs.startaddress) { printf("Setting start address\n"); obj_startaddress(irs.startaddress); } #endif }
void ReturnStatement::toIR(IRState *irs) { Blockx *blx = irs->blx; enum BC bc; 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 0//DMDV2 /* Call postBlit() on *shidden */ Type *tb = exp->type->toBasetype(); //if (tb->ty == Tstruct) exp->dump(0); if (exp->isLvalue() && 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); } } #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); bc = BCretexp; } else bc = BCret; block *btry = blx->curblock->Btry; if (btry) { // A finally block is a successor to a return block inside a try-finally if (btry->numSucc() == 2) // try-finally { block *bfinally = btry->nthSucc(1); assert(bfinally->BC == BC_finally); blx->curblock->appendSucc(bfinally); } } block_next(blx, bc, NULL); }