/********************************************* * 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; }
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; }
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; }
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 visit(SwitchErrorStatement *s) { Blockx *blx = irs->blx; //printf("SwitchErrorStatement::toIR()\n"); elem *efilename = el_ptr(toSymbol(blx->module)); elem *elinnum = el_long(TYint, s->loc.linnum); elem *e = el_bin(OPcall, TYvoid, el_var(getRtlsym(RTLSYM_DSWITCHERR)), el_param(elinnum, efilename)); block_appendexp(blx->curblock, e); }
elem *toEfilename(Module *m) { //printf("toEfilename(%s)\n", m->toChars()); const char *id = m->srcfile->toChars(); size_t len = strlen(id); if (!m->sfilename) { // Put out as a static array m->sfilename = toStringSymbol(id, len, 1); } // Turn static array into dynamic array return el_pair(TYdarray, el_long(TYsize_t, len), el_ptr(m->sfilename)); }
elem *toEfilename(Module *m) { elem *efilename; if (!m->sfilename) { dt_t *dt = NULL; char *id = m->srcfile->toChars(); size_t len = strlen(id); dtsize_t(&dt, len); dtabytes(&dt,TYnptr, 0, len + 1, id); m->sfilename = symbol_generate(SCstatic,type_fake(TYdarray)); m->sfilename->Sdt = dt; m->sfilename->Sfl = FLdata; out_readonly(m->sfilename); outdata(m->sfilename); } efilename = (config.exe == EX_WIN64) ? el_ptr(m->sfilename) : el_var(m->sfilename); return efilename; }
void genObjFile(Module *m, bool multiobj) { //EEcontext *ee = env->getEEcontext(); //printf("Module::genobjfile(multiobj = %d) %s\n", multiobj, m->toChars()); if (m->ident == Id::entrypoint) { bool v = global.params.verbose; global.params.verbose = false; for (size_t i = 0; i < m->members->dim; i++) { Dsymbol *member = (*m->members)[i]; //printf("toObjFile %s %s\n", member->kind(), member->toChars()); toObjFile(member, global.params.multiobj); } global.params.verbose = v; return; } lastmname = m->srcfile->toChars(); objmod->initfile(lastmname, NULL, m->toPrettyChars()); eictor = NULL; ictorlocalgot = NULL; sctors.setDim(0); ectorgates.setDim(0); sdtors.setDim(0); ssharedctors.setDim(0); esharedctorgates.setDim(0); sshareddtors.setDim(0); stests.setDim(0); if (m->doppelganger) { /* Generate a reference to the moduleinfo, so the module constructors * and destructors get linked in. */ Module *mod = m->aimports[0]; assert(mod); if (mod->sictor || mod->sctor || mod->sdtor || mod->ssharedctor || mod->sshareddtor) { Symbol *s = toSymbol(mod); //objextern(s); //if (!s->Sxtrnnum) objextdef(s->Sident); if (!s->Sxtrnnum) { //printf("%s\n", s->Sident); #if 0 /* This should work, but causes optlink to fail in common/newlib.asm */ objextdef(s->Sident); #else Symbol *sref = symbol_generate(SCstatic, type_fake(TYnptr)); sref->Sfl = FLdata; dtxoff(&sref->Sdt, s, 0, TYnptr); outdata(sref); #endif } } } if (global.params.cov) { /* Create coverage identifier: * private uint[numlines] __coverage; */ m->cov = symbol_calloc("__coverage"); m->cov->Stype = type_fake(TYint); m->cov->Stype->Tmangle = mTYman_c; m->cov->Stype->Tcount++; m->cov->Sclass = SCstatic; m->cov->Sfl = FLdata; dtnzeros(&m->cov->Sdt, 4 * m->numlines); outdata(m->cov); slist_add(m->cov); m->covb = (unsigned *)calloc((m->numlines + 32) / 32, sizeof(*m->covb)); } for (size_t i = 0; i < m->members->dim; i++) { Dsymbol *member = (*m->members)[i]; //printf("toObjFile %s %s\n", member->kind(), member->toChars()); toObjFile(member, multiobj); } if (global.params.cov) { /* Generate * bit[numlines] __bcoverage; */ Symbol *bcov = symbol_calloc("__bcoverage"); bcov->Stype = type_fake(TYuint); bcov->Stype->Tcount++; bcov->Sclass = SCstatic; bcov->Sfl = FLdata; dtnbytes(&bcov->Sdt, (m->numlines + 32) / 32 * sizeof(*m->covb), (char *)m->covb); outdata(bcov); free(m->covb); m->covb = NULL; /* Generate: * _d_cover_register(uint[] __coverage, BitArray __bcoverage, string filename); * and prepend it to the static constructor. */ /* t will be the type of the functions generated: * extern (C) void func(); */ type *t = type_function(TYnfunc, NULL, 0, false, tsvoid); t->Tmangle = mTYman_c; m->sictor = toSymbolX(m, "__modictor", SCglobal, t, "FZv"); cstate.CSpsymtab = &m->sictor->Sfunc->Flocsym; localgot = ictorlocalgot; elem *ecov = el_pair(TYdarray, el_long(TYsize_t, m->numlines), el_ptr(m->cov)); elem *ebcov = el_pair(TYdarray, el_long(TYsize_t, m->numlines), el_ptr(bcov)); if (config.exe == EX_WIN64) { ecov = addressElem(ecov, Type::tvoid->arrayOf(), false); ebcov = addressElem(ebcov, Type::tvoid->arrayOf(), false); } elem *efilename = toEfilename(m); if (config.exe == EX_WIN64) efilename = addressElem(efilename, Type::tstring, true); elem *e = el_params( el_long(TYuchar, global.params.covPercent), ecov, ebcov, efilename, NULL); e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DCOVER2]), e); eictor = el_combine(e, eictor); ictorlocalgot = localgot; } // If coverage / static constructor / destructor / unittest calls if (eictor || sctors.dim || ectorgates.dim || sdtors.dim || ssharedctors.dim || esharedctorgates.dim || sshareddtors.dim || stests.dim) { if (eictor) { localgot = ictorlocalgot; block *b = block_calloc(); b->BC = BCret; b->Belem = eictor; m->sictor->Sfunc->Fstartline.Sfilename = m->arg; m->sictor->Sfunc->Fstartblock = b; writefunc(m->sictor); } m->sctor = callFuncsAndGates(m, &sctors, &ectorgates, "__modctor"); m->sdtor = callFuncsAndGates(m, &sdtors, NULL, "__moddtor"); m->ssharedctor = callFuncsAndGates(m, &ssharedctors, (StaticDtorDeclarations *)&esharedctorgates, "__modsharedctor"); m->sshareddtor = callFuncsAndGates(m, &sshareddtors, NULL, "__modshareddtor"); m->stest = callFuncsAndGates(m, &stests, NULL, "__modtest"); if (m->doppelganger) genModuleInfo(m); } if (m->doppelganger) { objmod->termfile(); return; } if (global.params.multiobj) { /* This is necessary because the main .obj for this module is written * first, but determining whether marray or massert or munittest are needed is done * possibly later in the doppelganger modules. * Another way to fix it is do the main one last. */ toModuleAssert(m); toModuleUnittest(m); toModuleArray(m); } /* Always generate module info, because of templates and -cov. * But module info needs the runtime library, so disable it for betterC. */ if (!global.params.betterC /*|| needModuleInfo()*/) genModuleInfo(m); genhelpers(m, false); objmod->termfile(); }
/****************************************** * 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; }
void visit(SwitchStatement *s) { int string; Blockx *blx = irs->blx; //printf("SwitchStatement::toIR()\n"); IRState mystate(irs,s); 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 = s->sdefault ? block_calloc(blx) : mystate.breakBlock; size_t numcases = 0; if (s->cases) numcases = s->cases->dim; incUsage(irs, s->loc); elem *econd = toElemDtor(s->condition, &mystate); if (s->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 (size_t i = 0; i < numcases; i++) { CaseStatement *cs = (*s->cases)[i]; elem *ecase = toElemDtor(cs->exp, &mystate); elem *e = el_bin(OPeqeq, TYbool, el_copytree(econd), ecase); block *b = blx->curblock; block_appendexp(b, e); Label *clabel = getLabel(irs, blx, cs); block_next(blx, BCiftrue, NULL); b->appendSucc(clabel->lblock); b->appendSucc(blx->curblock); } /* The final 'else' clause goes to the default */ block *b = blx->curblock; block_next(blx, BCgoto, NULL); b->appendSucc(mystate.defaultBlock); Statement_toIR(s->_body, &mystate); /* Have the end of the switch body fall through to the block * following the switch statement. */ block_goto(blx, BCgoto, mystate.breakBlock); return; } if (s->condition->type->isString()) { // Number the cases so we can unscramble things after the sort() for (size_t i = 0; i < numcases; i++) { CaseStatement *cs = (*s->cases)[i]; cs->index = i; } s->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)); dtsize_t(&dt, numcases); dtxoff(&dt, si, Target::ptrsize * 2, TYnptr); for (size_t i = 0; i < numcases; i++) { CaseStatement *cs = (*s->cases)[i]; if (cs->exp->op != TOKstring) { s->error("case '%s' is not a string", cs->exp->toChars()); // BUG: this should be an assert } else { StringExp *se = (StringExp *)(cs->exp); Symbol *si = toStringSymbol((char *)se->string, se->len, se->sz); dtsize_t(&dt, se->len); dtxoff(&dt, si, 0); } } si->Sdt = dt; si->Sfl = FLdata; outdata(si); /* Call: * _d_switch_string(string[] si, string econd) */ if (config.exe == EX_WIN64) econd = addressElem(econd, s->condition->type, true); elem *eparam = el_param(econd, (config.exe == EX_WIN64) ? el_ptr(si) : el_var(si)); switch (s->condition->type->nextOf()->ty) { case Tchar: econd = el_bin(OPcall, TYint, el_var(getRtlsym(RTLSYM_SWITCH_STRING)), eparam); break; case Twchar: econd = el_bin(OPcall, TYint, el_var(getRtlsym(RTLSYM_SWITCH_USTRING)), eparam); break; case Tdchar: // BUG: implement econd = el_bin(OPcall, TYint, el_var(getRtlsym(RTLSYM_SWITCH_DSTRING)), eparam); break; default: assert(0); } elem_setLoc(econd, s->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; mystate.switchBlock->appendSucc(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 (size_t i = 0; i < numcases; i++) { CaseStatement *cs = (*s->cases)[i]; if (string) { pu[cs->index] = i; } else { pu[i] = cs->exp->toInteger(); } } Statement_toIR(s->_body, &mystate); /* Have the end of the switch body fall through to the block * following the switch statement. */ block_goto(blx, BCgoto, mystate.breakBlock); }
void FuncDeclaration::toObjFile(int multiobj) { FuncDeclaration *func = this; ClassDeclaration *cd = func->parent->isClassDeclaration(); int reverse; int has_arguments; //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", func, parent->toChars(), func->toChars()); //if (type) printf("type = %s\n", func->type->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 (semanticRun >= PASSobj) // if toObjFile() already run return; // If errors occurred compiling it, such as bugzilla 6118 if (type && type->ty == Tfunction && ((TypeFunction *)type)->next->ty == Terror) return; if (!func->fbody) { return; } if (func->isUnitTestDeclaration() && !global.params.useUnitTests) return; if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration()) { obj_append(this); return; } assert(semanticRun == PASSsemantic3done); semanticRun = PASSobj; if (global.params.verbose) printf("function %s\n",func->toChars()); Symbol *s = func->toSymbol(); func_t *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; } } /* Vector operations should be comdat's */ if (isArrayOp) s->Sclass = SCcomdat; 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_OPENBSD || TARGET_SOLARIS obj_ehsections(); // initialize exception handling sections #endif #if TARGET_WINDOS objextdef("__acrtused_con"); #endif obj_includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && linkage == LINKc) { #if TARGET_WINDOS objextdef("__acrtused_con"); // bring in C startup code obj_includelib("snn.lib"); // bring in C runtime library #endif 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); Dsymbols 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; } 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. nrvo_can = 0; } 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 (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (*parameters)[i]; if (v->csym) { error("compiler error, parameter '%s', bugzilla 2962?", v->toChars()); assert(0); } params[pi + i] = v->toSymbol(); } pi += 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) && 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) { size_t numintegerregs = 0, numfloatregs = 0; const unsigned char* argregs = getintegerparamsreglist(tyf, &numintegerregs); const unsigned char* floatregs = getfloatparamsreglist(tyf, &numfloatregs); // Order of assignment of pointer or integer parameters int r = 0; int xmmcnt = 0; for (size_t i = 0; i < pi; i++) { Symbol *sp = params[i]; tym_t ty = tybasic(sp->Stype->Tty); // BUG: doesn't work for structs if (r < numintegerregs) { if ((I64 || (i == 0 && (tyf == TYjfunc || tyf == TYmfunc))) && type_jparam(sp->Stype)) { sp->Sclass = SCfastpar; sp->Spreg = argregs[r]; sp->Sfl = FLauto; ++r; } } if (xmmcnt < numfloatregs) { if (tyxmmreg(ty)) { sp->Sclass = SCfastpar; sp->Spreg = floatregs[xmmcnt]; sp->Sfl = FLauto; ++xmmcnt; } } } } 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; #if DMDV2 buildClosure(&irs); #endif #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 DMDV2 if (isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration { ssharedctors.push(s); } else #endif if (isStaticCtorDeclaration()) { sctors.push(s); } // If static destructor #if DMDV2 if (isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration { SharedStaticDtorDeclaration *f = isSharedStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ esharedctorgates.push(f); } sshareddtors.shift(s); } else #endif if (isStaticDtorDeclaration()) { StaticDtorDeclaration *f = isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } sdtors.shift(s); } // If unit test if (isUnitTestDeclaration()) { stests.push(s); } if (global.errors) return; writefunc(s); if (isExport()) obj_export(s, Poffset); for (size_t i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (*irs.deferToObj)[i]; FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration(); if (fdp && fdp->semanticRun < PASSobj) { /* Bugzilla 7595 * FuncDeclaration::buildClosure() relies on nested functions * being toObjFile'd after the outer function. Otherwise, the * v->offset's for the closure variables are wrong. * So, defer fd until after fdp is done. */ fdp->deferred.push(fd); continue; } } s->toObjFile(0); } for (size_t i = 0; i < deferred.dim; i++) { FuncDeclaration *fd = deferred[i]; fd->toObjFile(0); } #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 (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 Module::genobjfile(int multiobj) { //EEcontext *ee = env->getEEcontext(); //printf("Module::genobjfile(multiobj = %d) %s\n", multiobj, toChars()); lastmname = srcfile->toChars(); obj_initfile(lastmname, NULL, toPrettyChars()); eictor = NULL; ictorlocalgot = NULL; sctors.setDim(0); ectorgates.setDim(0); sdtors.setDim(0); ssharedctors.setDim(0); esharedctorgates.setDim(0); sshareddtors.setDim(0); stests.setDim(0); dtorcount = 0; shareddtorcount = 0; if (doppelganger) { /* Generate a reference to the moduleinfo, so the module constructors * and destructors get linked in. */ Module *m = aimports[0]; assert(m); if (m->sictor || m->sctor || m->sdtor || m->ssharedctor || m->sshareddtor) { Symbol *s = m->toSymbol(); //objextern(s); //if (!s->Sxtrnnum) objextdef(s->Sident); if (!s->Sxtrnnum) { //printf("%s\n", s->Sident); #if 0 /* This should work, but causes optlink to fail in common/newlib.asm */ objextdef(s->Sident); #else #if ELFOBJ || MACHOBJ int nbytes = reftoident(DATA, Offset(DATA), s, 0, I64 ? (CFoff | CFoffset64) : CFoff); #else int nbytes = reftoident(DATA, Doffset, s, 0, CFoff); Doffset += nbytes; #endif #endif } } } if (global.params.cov) { /* Create coverage identifier: * private uint[numlines] __coverage; */ cov = symbol_calloc("__coverage"); cov->Stype = type_fake(TYint); cov->Stype->Tmangle = mTYman_c; cov->Stype->Tcount++; cov->Sclass = SCstatic; cov->Sfl = FLdata; #if ELFOBJ || MACHOBJ cov->Sseg = UDATA; #endif dtnzeros(&cov->Sdt, 4 * numlines); outdata(cov); slist_add(cov); covb = (unsigned *)calloc((numlines + 32) / 32, sizeof(*covb)); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *member = (*members)[i]; member->toObjFile(multiobj); } if (global.params.cov) { /* Generate * bit[numlines] __bcoverage; */ Symbol *bcov = symbol_calloc("__bcoverage"); bcov->Stype = type_fake(TYuint); bcov->Stype->Tcount++; bcov->Sclass = SCstatic; bcov->Sfl = FLdata; #if ELFOBJ || MACHOBJ bcov->Sseg = DATA; #endif dtnbytes(&bcov->Sdt, (numlines + 32) / 32 * sizeof(*covb), (char *)covb); outdata(bcov); free(covb); covb = NULL; /* Generate: * _d_cover_register(uint[] __coverage, BitArray __bcoverage, string filename); * and prepend it to the static constructor. */ /* t will be the type of the functions generated: * extern (C) void func(); */ type *t = type_alloc(TYnfunc); t->Tflags |= TFprototype | TFfixed; t->Tmangle = mTYman_c; t->Tnext = tsvoid; tsvoid->Tcount++; sictor = toSymbolX("__modictor", SCglobal, t, "FZv"); cstate.CSpsymtab = &sictor->Sfunc->Flocsym; localgot = ictorlocalgot; elem *e; e = el_params(el_pair(TYdarray, el_long(TYsize_t, numlines), el_ptr(cov)), el_pair(TYdarray, el_long(TYsize_t, numlines), el_ptr(bcov)), toEfilename(), NULL); e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DCOVER]), e); eictor = el_combine(e, eictor); ictorlocalgot = localgot; } // If coverage / static constructor / destructor / unittest calls if (eictor || sctors.dim || ectorgates.dim || sdtors.dim || ssharedctors.dim || esharedctorgates.dim || sshareddtors.dim || stests.dim) { if (eictor) { localgot = ictorlocalgot; block *b = block_calloc(); b->BC = BCret; b->Belem = eictor; sictor->Sfunc->Fstartline.Sfilename = arg; sictor->Sfunc->Fstartblock = b; writefunc(sictor); } sctor = callFuncsAndGates(this, &sctors, &ectorgates, "__modctor"); sdtor = callFuncsAndGates(this, &sdtors, NULL, "__moddtor"); #if DMDV2 ssharedctor = callFuncsAndGates(this, &ssharedctors, (StaticDtorDeclarations *)&esharedctorgates, "__modsharedctor"); sshareddtor = callFuncsAndGates(this, &sshareddtors, NULL, "__modshareddtor"); #endif stest = callFuncsAndGates(this, &stests, NULL, "__modtest"); if (doppelganger) genmoduleinfo(); } if (doppelganger) { obj_termfile(); return; } if (global.params.multiobj) { /* This is necessary because the main .obj for this module is written * first, but determining whether marray or massert or munittest are needed is done * possibly later in the doppelganger modules. * Another way to fix it is do the main one last. */ toModuleAssert(); toModuleUnittest(); toModuleArray(); } #if 1 // Always generate module info, because of templates and -cov if (1 || needModuleInfo()) genmoduleinfo(); #endif // If module assert for (int i = 0; i < 3; i++) { Symbol *ma; unsigned rt; unsigned bc; switch (i) { case 0: ma = marray; rt = RTLSYM_DARRAY; bc = BCexit; break; case 1: ma = massert; rt = RTLSYM_DASSERTM; bc = BCexit; break; case 2: ma = munittest; rt = RTLSYM_DUNITTESTM; bc = BCret; break; default: assert(0); } if (ma) { elem *elinnum; localgot = NULL; // Call dassert(filename, line) // Get sole parameter, linnum { Symbol *sp = symbol_calloc("linnum"); sp->Stype = type_fake(TYint); sp->Stype->Tcount++; sp->Sclass = SCfastpar; size_t num; sp->Spreg = getintegerparamsreglist(TYjfunc, &num)[0]; sp->Sflags &= ~SFLspill; sp->Sfl = FLpara; // FLauto? cstate.CSpsymtab = &ma->Sfunc->Flocsym; symbol_add(sp); elinnum = el_var(sp); } elem *efilename = el_ptr(toSymbol()); elem *e = el_var(rtlsym[rt]); e = el_bin(OPcall, TYvoid, e, el_param(elinnum, efilename)); block *b = block_calloc(); b->BC = bc; b->Belem = e; ma->Sfunc->Fstartline.Sfilename = arg; ma->Sfunc->Fstartblock = b; ma->Sclass = SCglobal; ma->Sfl = 0; ma->Sflags |= rtlsym[rt]->Sflags & SFLexit; writefunc(ma); } } obj_termfile(); }
void Module::genobjfile(int multiobj) { //EEcontext *ee = env->getEEcontext(); //printf("Module::genobjfile(multiobj = %d) %s\n", multiobj, toChars()); lastmname = srcfile->toChars(); objmod->initfile(lastmname, NULL, toPrettyChars()); eictor = NULL; ictorlocalgot = NULL; sctors.setDim(0); ectorgates.setDim(0); sdtors.setDim(0); ssharedctors.setDim(0); esharedctorgates.setDim(0); sshareddtors.setDim(0); stests.setDim(0); dtorcount = 0; shareddtorcount = 0; if (doppelganger) { /* Generate a reference to the moduleinfo, so the module constructors * and destructors get linked in. */ Module *m = aimports[0]; assert(m); if (m->sictor || m->sctor || m->sdtor || m->ssharedctor || m->sshareddtor) { Symbol *s = m->toSymbol(); //objextern(s); //if (!s->Sxtrnnum) objextdef(s->Sident); if (!s->Sxtrnnum) { //printf("%s\n", s->Sident); #if 0 /* This should work, but causes optlink to fail in common/newlib.asm */ objextdef(s->Sident); #else Symbol *sref = symbol_generate(SCstatic, type_fake(TYnptr)); sref->Sfl = FLdata; dtxoff(&sref->Sdt, s, 0, TYnptr); outdata(sref); #endif } } } if (global.params.cov) { /* Create coverage identifier: * private uint[numlines] __coverage; */ cov = symbol_calloc("__coverage"); cov->Stype = type_fake(TYint); cov->Stype->Tmangle = mTYman_c; cov->Stype->Tcount++; cov->Sclass = SCstatic; cov->Sfl = FLdata; dtnzeros(&cov->Sdt, 4 * numlines); outdata(cov); slist_add(cov); covb = (unsigned *)calloc((numlines + 32) / 32, sizeof(*covb)); } for (size_t i = 0; i < members->dim; i++) { Dsymbol *member = (*members)[i]; //printf("toObjFile %s %s\n", member->kind(), member->toChars()); member->toObjFile(multiobj); } if (global.params.cov) { /* Generate * bit[numlines] __bcoverage; */ Symbol *bcov = symbol_calloc("__bcoverage"); bcov->Stype = type_fake(TYuint); bcov->Stype->Tcount++; bcov->Sclass = SCstatic; bcov->Sfl = FLdata; dtnbytes(&bcov->Sdt, (numlines + 32) / 32 * sizeof(*covb), (char *)covb); outdata(bcov); free(covb); covb = NULL; /* Generate: * _d_cover_register(uint[] __coverage, BitArray __bcoverage, string filename); * and prepend it to the static constructor. */ /* t will be the type of the functions generated: * extern (C) void func(); */ type *t = type_function(TYnfunc, NULL, 0, false, tsvoid); t->Tmangle = mTYman_c; sictor = toSymbolX("__modictor", SCglobal, t, "FZv"); cstate.CSpsymtab = &sictor->Sfunc->Flocsym; localgot = ictorlocalgot; elem *ecov = el_pair(TYdarray, el_long(TYsize_t, numlines), el_ptr(cov)); elem *ebcov = el_pair(TYdarray, el_long(TYsize_t, numlines), el_ptr(bcov)); if (config.exe == EX_WIN64) { ecov = addressElem(ecov, Type::tvoid->arrayOf(), false); ebcov = addressElem(ebcov, Type::tvoid->arrayOf(), false); } elem *e = el_params( el_long(TYuchar, global.params.covPercent), ecov, ebcov, toEfilename(), NULL); e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DCOVER2]), e); eictor = el_combine(e, eictor); ictorlocalgot = localgot; } // If coverage / static constructor / destructor / unittest calls if (eictor || sctors.dim || ectorgates.dim || sdtors.dim || ssharedctors.dim || esharedctorgates.dim || sshareddtors.dim || stests.dim) { if (eictor) { localgot = ictorlocalgot; block *b = block_calloc(); b->BC = BCret; b->Belem = eictor; sictor->Sfunc->Fstartline.Sfilename = arg; sictor->Sfunc->Fstartblock = b; writefunc(sictor); } sctor = callFuncsAndGates(this, &sctors, &ectorgates, "__modctor"); sdtor = callFuncsAndGates(this, &sdtors, NULL, "__moddtor"); #if DMDV2 ssharedctor = callFuncsAndGates(this, &ssharedctors, (StaticDtorDeclarations *)&esharedctorgates, "__modsharedctor"); sshareddtor = callFuncsAndGates(this, &sshareddtors, NULL, "__modshareddtor"); #endif stest = callFuncsAndGates(this, &stests, NULL, "__modtest"); if (doppelganger) genmoduleinfo(); } if (doppelganger) { objmod->termfile(); return; } if (global.params.multiobj) { /* This is necessary because the main .obj for this module is written * first, but determining whether marray or massert or munittest are needed is done * possibly later in the doppelganger modules. * Another way to fix it is do the main one last. */ toModuleAssert(); toModuleUnittest(); toModuleArray(); } /* Always generate module info, because of templates and -cov. * But module info needs the runtime library, so disable it for betterC. */ if (!global.params.betterC /*|| needModuleInfo()*/) genmoduleinfo(); // If module assert for (int i = 0; i < 3; i++) { Symbol *ma; unsigned rt; unsigned bc; switch (i) { case 0: ma = marray; rt = RTLSYM_DARRAY; bc = BCexit; break; case 1: ma = massert; rt = RTLSYM_DASSERTM; bc = BCexit; break; case 2: ma = munittest; rt = RTLSYM_DUNITTESTM; bc = BCret; break; default: assert(0); } if (ma) { elem *elinnum; localgot = NULL; // Call dassert(filename, line) // Get sole parameter, linnum { Symbol *sp = symbol_calloc("linnum"); sp->Stype = type_fake(TYint); sp->Stype->Tcount++; sp->Sclass = (config.exe == EX_WIN64) ? SCshadowreg : SCfastpar; FuncParamRegs fpr(TYjfunc); fpr.alloc(sp->Stype, sp->Stype->Tty, &sp->Spreg, &sp->Spreg2); sp->Sflags &= ~SFLspill; sp->Sfl = (sp->Sclass == SCshadowreg) ? FLpara : FLfast; cstate.CSpsymtab = &ma->Sfunc->Flocsym; symbol_add(sp); elinnum = el_var(sp); } elem *efilename = el_ptr(toSymbol()); elem *e = el_var(rtlsym[rt]); e = el_bin(OPcall, TYvoid, e, el_param(elinnum, efilename)); block *b = block_calloc(); b->BC = bc; b->Belem = e; ma->Sfunc->Fstartline.Sfilename = arg; ma->Sfunc->Fstartblock = b; ma->Sclass = SCglobal; ma->Sfl = 0; ma->Sflags |= rtlsym[rt]->Sflags & SFLexit; writefunc(ma); } } objmod->termfile(); }
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 Module::genobjfile(int multiobj) { //EEcontext *ee = env->getEEcontext(); //printf("Module::genobjfile(multiobj = %d) %s\n", multiobj, toChars()); lastmname = srcfile->toChars(); obj_initfile(lastmname, NULL, toPrettyChars()); eictor = NULL; ictorlocalgot = NULL; ector = NULL; ectorgates.setDim(0); edtor = NULL; etest = NULL; dtorcount = 0; if (doppelganger) { /* Generate a reference to the moduleinfo, so the module constructors * and destructors get linked in. */ Module *m = (Module *)aimports.data[0]; assert(m); if (m->sictor || m->sctor || m->sdtor) { Symbol *s = m->toSymbol(); //objextern(s); //if (!s->Sxtrnnum) objextdef(s->Sident); if (!s->Sxtrnnum) { //printf("%s\n", s->Sident); #if 0 /* This should work, but causes optlink to fail in common/newlib.asm */ objextdef(s->Sident); #else #if ELFOBJ || MACHOBJ int nbytes = reftoident(DATA, Offset(DATA), s, 0, CFoff); Offset(DATA) += nbytes; #else int nbytes = reftoident(DATA, Doffset, s, 0, CFoff); Doffset += nbytes; #endif #endif } } } if (global.params.cov) { /* Create coverage identifier: * private uint[numlines] __coverage; */ cov = symbol_calloc("__coverage"); cov->Stype = type_fake(TYint); cov->Stype->Tmangle = mTYman_c; cov->Stype->Tcount++; cov->Sclass = SCstatic; cov->Sfl = FLdata; #if ELFOBJ || MACHOBJ cov->Sseg = UDATA; #endif dtnzeros(&cov->Sdt, 4 * numlines); outdata(cov); slist_add(cov); covb = (unsigned *)calloc((numlines + 32) / 32, sizeof(*covb)); } for (int i = 0; i < members->dim; i++) { Dsymbol *member = (Dsymbol *)members->data[i]; member->toObjFile(multiobj); } if (global.params.cov) { /* Generate * bit[numlines] __bcoverage; */ Symbol *bcov = symbol_calloc("__bcoverage"); bcov->Stype = type_fake(TYuint); bcov->Stype->Tcount++; bcov->Sclass = SCstatic; bcov->Sfl = FLdata; #if ELFOBJ || MACHOBJ bcov->Sseg = DATA; #endif dtnbytes(&bcov->Sdt, (numlines + 32) / 32 * sizeof(*covb), (char *)covb); outdata(bcov); free(covb); covb = NULL; /* Generate: * _d_cover_register(uint[] __coverage, BitArray __bcoverage, string filename); * and prepend it to the static constructor. */ /* t will be the type of the functions generated: * extern (C) void func(); */ type *t = type_alloc(TYnfunc); t->Tflags |= TFprototype | TFfixed; t->Tmangle = mTYman_c; t->Tnext = tsvoid; tsvoid->Tcount++; sictor = toSymbolX("__modictor", SCglobal, t, "FZv"); cstate.CSpsymtab = &sictor->Sfunc->Flocsym; localgot = ictorlocalgot; elem *e; e = el_params(el_ptr(cov), el_long(TYuint, numlines), el_ptr(bcov), el_long(TYuint, numlines), toEfilename(), NULL); e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_DCOVER]), e); eictor = el_combine(e, eictor); ictorlocalgot = localgot; } // If coverage / static constructor / destructor / unittest calls if (eictor || ector || ectorgates.dim || edtor || etest) { /* t will be the type of the functions generated: * extern (C) void func(); */ type *t = type_alloc(TYnfunc); t->Tflags |= TFprototype | TFfixed; t->Tmangle = mTYman_c; t->Tnext = tsvoid; tsvoid->Tcount++; static char moddeco[] = "FZv"; if (eictor) { localgot = ictorlocalgot; block *b = block_calloc(); b->BC = BCret; b->Belem = eictor; sictor->Sfunc->Fstartblock = b; writefunc(sictor); } if (ector || ectorgates.dim) { localgot = NULL; sctor = toSymbolX("__modctor", SCglobal, t, moddeco); cstate.CSpsymtab = &sctor->Sfunc->Flocsym; for (int i = 0; i < ectorgates.dim; i++) { StaticDtorDeclaration *f = (StaticDtorDeclaration *)ectorgates.data[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); } block *b = block_calloc(); b->BC = BCret; b->Belem = ector; sctor->Sfunc->Fstartblock = b; writefunc(sctor); #if STATICCTOR obj_staticctor(sctor, dtorcount, 1); #endif } if (edtor) { localgot = NULL; sdtor = toSymbolX("__moddtor", SCglobal, t, moddeco); block *b = block_calloc(); b->BC = BCret; b->Belem = edtor; sdtor->Sfunc->Fstartblock = b; writefunc(sdtor); } if (etest) { localgot = NULL; stest = toSymbolX("__modtest", SCglobal, t, moddeco); block *b = block_calloc(); b->BC = BCret; b->Belem = etest; stest->Sfunc->Fstartblock = b; writefunc(stest); } if (doppelganger) genmoduleinfo(); } if (doppelganger) { obj_termfile(); return; } if (global.params.multiobj) { /* This is necessary because the main .obj for this module is written * first, but determining whether marray or massert are needed is done * possibly later in the doppelganger modules. * Another way to fix it is do the main one last. */ toModuleAssert(); toModuleArray(); } // If module assert for (int i = 0; i < 2; i++) { Symbol *ma = i ? marray : massert; if (ma) { elem *elinnum; elem *efilename; localgot = NULL; // Call dassert(filename, line) // Get sole parameter, linnum { Symbol *sp; sp = symbol_calloc("linnum"); sp->Stype = type_fake(TYint); sp->Stype->Tcount++; sp->Sclass = SCfastpar; sp->Spreg = AX; sp->Sflags &= ~SFLspill; sp->Sfl = FLpara; // FLauto? cstate.CSpsymtab = &ma->Sfunc->Flocsym; symbol_add(sp); elinnum = el_var(sp); } efilename = toEmodulename(); elem *e = el_var(rtlsym[i ? RTLSYM_DARRAY : RTLSYM_DASSERT]); e = el_bin(OPcall, TYvoid, e, el_param(elinnum, efilename)); block *b = block_calloc(); b->BC = BCret; b->Belem = e; ma->Sfunc->Fstartblock = b; ma->Sclass = SCglobal; ma->Sfl = 0; writefunc(ma); } } #if 1 // Always generate module info, because of templates and -cov if (1 || needModuleInfo()) genmoduleinfo(); #endif obj_termfile(); }
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 FuncDeclaration::toObjFile(int multiobj) { FuncDeclaration *func = this; ClassDeclaration *cd = func->parent->isClassDeclaration(); int reverse; int has_arguments; //printf("FuncDeclaration::toObjFile(%p, %s.%s)\n", func, parent->toChars(), func->toChars()); //if (type) printf("type = %s\n", func->type->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 (semanticRun >= PASSobj) // if toObjFile() already run return; // If errors occurred compiling it, such as bugzilla 6118 if (type && type->ty == Tfunction && ((TypeFunction *)type)->next->ty == Terror) return; if (!func->fbody) { return; } if (func->isUnitTestDeclaration() && !global.params.useUnitTests) return; if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration()) { obj_append(this); return; } if (semanticRun == PASSsemanticdone) { /* What happened is this function failed semantic3() with errors, * but the errors were gagged. * Try to reproduce those errors, and then fail. */ error("errors compiling the function"); return; } assert(semanticRun == PASSsemantic3done); semanticRun = PASSobj; if (global.params.verbose) printf("function %s\n",func->toPrettyChars()); Symbol *s = func->toSymbol(); func_t *f = s->Sfunc; // tunnel type of "this" to debug info generation if (AggregateDeclaration* ad = func->parent->isAggregateDeclaration()) { ::type* t = ad->getType()->toCtype(); if(cd) t = t->Tnext; // skip reference f->Fclass = (Classsym *)t; } #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; } } /* Vector operations should be comdat's */ if (isArrayOp) s->Sclass = SCcomdat; if (isNested()) { // if (!(config.flags3 & CFG3pic)) // s->Sclass = SCstatic; f->Fflags3 |= Fnested; /* The enclosing function must have its code generated first, * so we know things like where its local symbols are stored. */ FuncDeclaration *fdp = toAliasFunc()->toParent2()->isFuncDeclaration(); // Bug 8016 - only include the function if it is a template instance Dsymbol * owner = NULL; if (fdp) { owner = fdp->toParent(); while (owner && !owner->isTemplateInstance()) owner = owner->toParent(); } if (owner && fdp && fdp->semanticRun == PASSsemantic3done && !fdp->isUnitTestDeclaration()) { /* Can't do unittest's out of order, they are order dependent in that their * execution is done in lexical order, and some modules (std.datetime *cough* * *cough*) rely on this. */ fdp->toObjFile(multiobj); } } else { const char *libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; // Pull in RTL startup code (but only once) if (func->isMain() && onlyOneMain(loc)) { #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS objmod->external_def("_main"); objmod->ehsections(); // initialize exception handling sections #endif #if TARGET_WINDOS if (I64) { objmod->external_def("main"); objmod->ehsections(); // initialize exception handling sections } else { objmod->external_def("_main"); objmod->external_def("__acrtused_con"); } #endif objmod->includelib(libname); s->Sclass = SCglobal; } else if (strcmp(s->Sident, "main") == 0 && linkage == LINKc) { #if TARGET_WINDOS if (I64) { objmod->includelib("LIBCMT"); objmod->includelib("OLDNAMES"); } else { objmod->external_def("__acrtused_con"); // bring in C startup code objmod->includelib("snn.lib"); // bring in C runtime library } #endif s->Sclass = SCglobal; } #if TARGET_WINDOS else if (func->isWinMain() && onlyOneMain(loc)) { if (I64) { 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 (func->isDllMain() && onlyOneMain(loc)) { if (I64) { 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 } 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); Dsymbols 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; } 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. nrvo_can = 0; } if (vthis) { assert(!vthis->csym); sthis = vthis->toSymbol(); irs.sthis = sthis; if (!(f->Fflags3 & Fnested)) f->Fflags3 |= Fmember; } // Estimate number of parameters, pi size_t pi = (v_arguments != NULL); if (parameters) pi += 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 (v_arguments) { params[pi] = v_arguments->toSymbol(); pi += 1; } if (parameters) { for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (*parameters)[i]; if (v->csym) { error("compiler error, parameter '%s', bugzilla 2962?", v->toChars()); assert(0); } params[pi + i] = v->toSymbol(); } pi += 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) && 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 (func->fbody) { localgot = NULL; Statement *sbody = func->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 = func; bx.module = getModule(); irs.blx = &bx; /* If profiling, insert call to the profiler here. * _c_trace_pro(char* funcname); */ if (global.params.trace) { dt_t *dt = NULL; char *id = s->Sident; size_t len = strlen(id); dtnbytes(&dt, len + 1, id); Symbol *sfuncname = symbol_generate(SCstatic,type_fake(TYchar)); sfuncname->Sdt = dt; sfuncname->Sfl = FLdata; out_readonly(sfuncname); outdata(sfuncname); elem *efuncname = el_ptr(sfuncname); elem *eparam = el_params(efuncname, el_long(TYsize_t, len), NULL); elem *e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_TRACE_CPRO]), eparam); block_appendexp(bx.curblock, e); } #if DMDV2 buildClosure(&irs); #endif #if 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 (block *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 DMDV2 if (isSharedStaticCtorDeclaration()) // must come first because it derives from StaticCtorDeclaration { ssharedctors.push(s); } else #endif if (isStaticCtorDeclaration()) { sctors.push(s); } // If static destructor #if DMDV2 if (isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration { SharedStaticDtorDeclaration *f = isSharedStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ esharedctorgates.push(f); } sshareddtors.shift(s); } else #endif if (isStaticDtorDeclaration()) { StaticDtorDeclaration *f = isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } sdtors.shift(s); } // If unit test if (isUnitTestDeclaration()) { stests.push(s); } if (global.errors) return; writefunc(s); if (isExport()) objmod->export_symbol(s, Para.offset); for (size_t i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = (*irs.deferToObj)[i]; FuncDeclaration *fd = s->isFuncDeclaration(); if (fd) { FuncDeclaration *fdp = fd->toParent2()->isFuncDeclaration(); if (fdp && fdp->semanticRun < PASSobj) { /* Bugzilla 7595 * FuncDeclaration::buildClosure() relies on nested functions * being toObjFile'd after the outer function. Otherwise, the * v->offset's for the closure variables are wrong. * So, defer fd until after fdp is done. */ fdp->deferred.push(fd); continue; } } s->toObjFile(0); } for (size_t i = 0; i < deferred.dim; i++) { FuncDeclaration *fd = deferred[i]; fd->toObjFile(0); } #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 (ident && memcmp(ident->toChars(), "_STD", 4) == 0) objmod->staticdtor(s); #endif #if DMDV2 if (irs.startaddress) { //printf("Setting start address\n"); objmod->startaddress(irs.startaddress); } #endif }