code *xmmopass(elem *e,regm_t *pretregs) { elem *e1 = e->E1; elem *e2 = e->E2; tym_t ty1 = tybasic(e1->Ety); unsigned sz1 = tysize[ty1]; regm_t rretregs = XMMREGS & ~*pretregs; if (!rretregs) rretregs = XMMREGS; code *cr = codelem(e2,&rretregs,FALSE); // eval right leaf unsigned rreg = findreg(rretregs); code cs; code *cl,*cg; regm_t retregs; unsigned reg; bool regvar = FALSE; if (config.flags4 & CFG4optimized) { // Be careful of cases like (x = x+x+x). We cannot evaluate in // x if x is in a register. unsigned varreg; regm_t varregm; if (isregvar(e1,&varregm,&varreg) && // if lvalue is register variable doinreg(e1->EV.sp.Vsym,e2) // and we can compute directly into it ) { regvar = TRUE; retregs = varregm; reg = varreg; // evaluate directly in target register cl = NULL; cg = getregs(retregs); // destroy these regs } } if (!regvar) { cl = getlvalue(&cs,e1,rretregs); // get EA retregs = *pretregs & XMMREGS & ~rretregs; if (!retregs) retregs = XMMREGS & ~rretregs; cg = allocreg(&retregs,®,ty1); cs.Iop = xmmload(ty1); // MOVSD xmm,xmm_m64 code_newreg(&cs,reg - XMM0); cg = gen(cg,&cs); } unsigned op = xmmoperator(e1->Ety, e->Eoper); code *co = gen2(CNIL,op,modregxrmx(3,reg-XMM0,rreg-XMM0)); if (!regvar) { cs.Iop = xmmstore(ty1); // reverse operand order of MOVS[SD] gen(co,&cs); } if (e1->Ecount || // if lvalue is a CSE or regvar) // rvalue can't be a CSE { cl = cat(cl,getregs_imm(retregs)); // necessary if both lvalue and // rvalue are CSEs (since a reg // can hold only one e at a time) cssave(e1,retregs,EOP(e1)); // if lvalue is a CSE } co = cat(co,fixresult(e,retregs,pretregs)); freenode(e1); return cat4(cr,cl,cg,co); }
STATIC void ecom(elem **pe) { int i,op; HCSArray hcsarraySave; unsigned hash; elem *e,*ehash; tym_t tym; e = *pe; assert(e); elem_debug(e); #ifdef DEBUG assert(e->Ecount == 0); //assert(e->Ecomsub == 0); #endif tym = tybasic(e->Ety); op = e->Eoper; switch (op) { case OPconst: case OPvar: case OPrelconst: break; case OPstreq: case OPpostinc: case OPpostdec: case OPeq: case OPaddass: case OPminass: case OPmulass: case OPdivass: case OPmodass: case OPshrass: case OPashrass: case OPshlass: case OPandass: case OPxorass: case OPorass: case OPvecsto: #if TX86 /* Reverse order of evaluation for double op=. This is so that */ /* the pushing of the address of the second operand is easier. */ /* However, with the 8087 we don't need the kludge. */ if (op != OPeq && tym == TYdouble && !config.inline8087) { if (EOP(e->E1)) ecom(&e->E1->E1); ecom(&e->E2); } else #endif { /* Don't mark the increment of an i++ or i-- as a CSE, if it */ /* can be done with an INC or DEC instruction. */ if (!(OTpost(op) && elemisone(e->E2))) ecom(&e->E2); /* evaluate 2nd operand first */ case OPnegass: if (EOP(e->E1)) /* if lvalue is an operator */ { if (e->E1->Eoper != OPind) elem_print(e); assert(e->E1->Eoper == OPind); ecom(&(e->E1->E1)); } } touchlvalue(e->E1); if (!OTpost(op)) /* lvalue of i++ or i-- is not a cse*/ { hash = cs_comphash(e->E1); vec_setbit(hash % CSVECDIM,csvec); addhcstab(e->E1,hash); // add lvalue to hcstab[] } return; case OPbtc: case OPbts: case OPbtr: case OPcmpxchg: ecom(&e->E1); ecom(&e->E2); touchfunc(0); // indirect assignment return; case OPandand: case OPoror: ecom(&e->E1); hcsarraySave = hcsarray; ecom(&e->E2); hcsarray = hcsarraySave; // no common subs by E2 return; /* if comsub then logexp() will */ /* break */ case OPcond: ecom(&e->E1); hcsarraySave = hcsarray; ecom(&e->E2->E1); // left condition hcsarray = hcsarraySave; // no common subs by E2 ecom(&e->E2->E2); // right condition hcsarray = hcsarraySave; // no common subs by E2 return; // can't be a common sub case OPcall: case OPcallns: ecom(&e->E2); /* eval right first */ /* FALL-THROUGH */ case OPucall: case OPucallns: ecom(&e->E1); touchfunc(1); return; case OPstrpar: /* so we don't break logexp() */ #if TX86 case OPinp: /* never CSE the I/O instruction itself */ #endif ecom(&e->E1); /* FALL-THROUGH */ case OPasm: case OPstrthis: // don't CSE these case OPframeptr: case OPgot: case OPctor: case OPdtor: case OPdctor: case OPmark: return; case OPddtor: touchall(); ecom(&e->E1); touchall(); return; case OPparam: #if TX86 case OPoutp: #endif ecom(&e->E1); case OPinfo: ecom(&e->E2); return; case OPcomma: case OPremquo: ecom(&e->E1); ecom(&e->E2); break; #if TARGET_SEGMENTED case OPvp_fp: case OPcvp_fp: ecom(&e->E1); touchaccess(e); break; #endif case OPind: ecom(&e->E1); /* Generally, CSEing a *(double *) results in worse code */ if (tyfloating(tym)) return; break; case OPstrcpy: case OPstrcat: case OPmemcpy: case OPmemset: ecom(&e->E2); case OPsetjmp: ecom(&e->E1); touchfunc(0); return; default: /* other operators */ if (!EBIN(e)) WROP(e->Eoper); assert(EBIN(e)); case OPadd: case OPmin: case OPmul: case OPdiv: case OPor: case OPxor: case OPand: case OPeqeq: case OPne: #if TX86 case OPscale: case OPyl2x: case OPyl2xp1: #endif ecom(&e->E1); ecom(&e->E2); break; case OPstring: case OPaddr: case OPbit: WROP(e->Eoper); elem_print(e); assert(0); /* optelem() should have removed these */ /* NOTREACHED */ // Explicitly list all the unary ops for speed case OPnot: case OPcom: case OPneg: case OPuadd: case OPabs: case OPrndtol: case OPrint: case OPpreinc: case OPpredec: case OPbool: case OPstrlen: case OPs16_32: case OPu16_32: case OPd_s32: case OPd_u32: case OPs32_d: case OPu32_d: case OPd_s16: case OPs16_d: case OP32_16: case OPd_f: case OPf_d: case OPd_ld: case OPld_d: case OPc_r: case OPc_i: case OPu8_16: case OPs8_16: case OP16_8: case OPu32_64: case OPs32_64: case OP64_32: case OPmsw: case OPu64_128: case OPs64_128: case OP128_64: case OPd_s64: case OPs64_d: case OPd_u64: case OPu64_d: case OPstrctor: case OPu16_d: case OPd_u16: case OParrow: case OPvoid: case OPnullcheck: case OPbsf: case OPbsr: case OPbswap: case OPpopcnt: case OPvector: case OPld_u64: #if TX86 case OPsqrt: case OPsin: case OPcos: #endif #if TARGET_SEGMENTED case OPoffset: case OPnp_fp: case OPnp_f16p: case OPf16p_np: #endif ecom(&e->E1); break; case OPhalt: return; } /* don't CSE structures or unions or volatile stuff */ if (tym == TYstruct || tym == TYvoid || e->Ety & mTYvolatile #if TX86 || tyxmmreg(tym) // don't CSE doubles if inline 8087 code (code generator can't handle it) || (tyfloating(tym) && config.inline8087) #endif ) return; hash = cs_comphash(e); /* must be AFTER leaves are done */ /* Search for a match in hcstab[]. * Search backwards, as most likely matches will be towards the end * of the list. */ #ifdef DEBUG if (debugx) dbg_printf("elem: %p hash: %6d\n",e,hash); #endif int csveci = hash % CSVECDIM; if (vec_testbit(csveci,csvec)) { for (i = hcsarray.top; i--;) { #ifdef DEBUG if (debugx) dbg_printf("i: %2d Hhash: %6d Helem: %p\n", i,hcstab[i].Hhash,hcstab[i].Helem); #endif if (hash == hcstab[i].Hhash && (ehash = hcstab[i].Helem) != NULL) { /* if elems are the same and we still have room for more */ if (el_match(e,ehash) && ehash->Ecount < 0xFF) { /* Make sure leaves are also common subexpressions * to avoid false matches. */ if (!OTleaf(op)) { if (!e->E1->Ecount) continue; if (OTbinary(op) && !e->E2->Ecount) continue; } ehash->Ecount++; *pe = ehash; #ifdef DEBUG if (debugx) dbg_printf("**MATCH** %p with %p\n",e,*pe); #endif el_free(e); return; } } } } else vec_setbit(csveci,csvec); addhcstab(e,hash); // add this elem to hcstab[] }
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); } }
STATIC void cpp_basic_data_type(type *t) { char c; int i; //printf("cpp_basic_data_type(t)\n"); //type_print(t); switch (tybasic(t->Tty)) { case TYschar: c = 'C'; goto dochar; case TYchar: c = 'D'; goto dochar; case TYuchar: c = 'E'; goto dochar; case TYshort: c = 'F'; goto dochar; case TYushort: c = 'G'; goto dochar; case TYint: c = 'H'; goto dochar; case TYuint: c = 'I'; goto dochar; case TYlong: c = 'J'; goto dochar; case TYulong: c = 'K'; goto dochar; case TYfloat: c = 'M'; goto dochar; case TYdouble: c = 'N'; goto dochar; case TYdouble_alias: if (intsize == 4) { c = 'O'; goto dochar; } c = 'Z'; goto dochar2; case TYldouble: if (intsize == 2) { c = 'O'; goto dochar; } c = 'Z'; goto dochar2; dochar: CHAR(c); break; case TYllong: c = 'J'; goto dochar2; case TYullong: c = 'K'; goto dochar2; case TYbool: c = 'N'; goto dochar2; // was 'X' prior to 8.1b8 case TYwchar_t: if (config.flags4 & CFG4nowchar_t) { c = 'G'; goto dochar; // same as TYushort } else { pstate.STflags |= PFLmfc; c = 'Y'; goto dochar2; } // Digital Mars extensions case TYifloat: c = 'R'; goto dochar2; case TYidouble: c = 'S'; goto dochar2; case TYildouble: c = 'T'; goto dochar2; case TYcfloat: c = 'U'; goto dochar2; case TYcdouble: c = 'V'; goto dochar2; case TYcldouble: c = 'W'; goto dochar2; case TYchar16: c = 'X'; goto dochar2; case TYdchar: c = 'Y'; goto dochar2; case TYnullptr: c = 'Z'; goto dochar2; dochar2: CHAR('_'); goto dochar; #if TARGET_SEGMENTED case TYsptr: case TYcptr: case TYf16ptr: case TYfptr: case TYhptr: case TYvptr: #endif #if !MARS case TYmemptr: #endif case TYnptr: c = 'P' + cpp_cvidx(t->Tty); CHAR(c); if(I64) CHAR('E'); // __ptr64 modifier cpp_pointer_type(t); break; case TYstruct: case TYenum: cpp_ecsu_data_type(t); break; case TYarray: i = cpp_cvidx(t->Tty); i |= 1; // always const CHAR('P' + i); cpp_pointer_type(t); break; case TYvoid: c = 'X'; goto dochar; #if !MARS case TYident: if (pstate.STintemplate) { CHAR('V'); // pretend to be a class name cpp_zname(t->Tident); } else { #if SCPP cpperr(EM_no_type,t->Tident); // no type for argument #endif c = 'X'; goto dochar; } break; case TYtemplate: if (pstate.STintemplate) { CHAR('V'); // pretend to be a class name cpp_zname(((typetemp_t *)t)->Tsym->Sident); } else goto Ldefault; break; #endif default: Ldefault: if (tyfunc(t->Tty)) cpp_function_type(t); else { #if SCPP #ifdef DEBUG if (!errcnt) type_print(t); #endif assert(errcnt); #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; } 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 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 (global.errors) 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; 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; /* 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) { /* Wrap the entire function body in: * trace_pro("funcname"); * try * body; * finally * _c_trace_epi(); */ StringExp *se = new StringExp(Loc(), s->Sident); se->type = new TypeDArray(Type::tchar->immutableOf()); se->type = se->type->semantic(Loc(), NULL); Expressions *exps = new Expressions(); exps->push(se); FuncDeclaration *fdpro = FuncDeclaration::genCfunc(Type::tvoid, "trace_pro"); Expression *ec = new VarExp(Loc(), fdpro); Expression *e = new CallExp(Loc(), ec, exps); e->type = Type::tvoid; Statement *sp = new ExpStatement(loc, e); FuncDeclaration *fdepi = FuncDeclaration::genCfunc(Type::tvoid, "_c_trace_epi"); ec = new VarExp(Loc(), fdepi); e = new CallExp(Loc(), ec); e->type = Type::tvoid; Statement *sf = new ExpStatement(loc, e); Statement *stf; if (sbody->blockExit(tf->isnothrow) == BEfallthru) stf = new CompoundStatement(Loc(), sbody, sf); else stf = new TryFinallyStatement(Loc(), sbody, sf); sbody = new CompoundStatement(Loc(), sp, stf); } #if DMDV2 buildClosure(&irs); #endif #if TARGET_WINDOS if (func->isSynchronized() && cd && config.flags2 & CFG2seh && !func->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; } #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 }
type *type_copy(type *t) { type *tn; param_t *p; type_debug(t); #if SCPP if (tybasic(t->Tty) == TYtemplate) { tn = type_alloc_template(((typetemp_t *)t)->Tsym); } else #endif tn = type_alloc(t->Tty); *tn = *t; switch (tybasic(tn->Tty)) { #if SCPP case TYtemplate: ((typetemp_t *)tn)->Tsym = ((typetemp_t *)t)->Tsym; goto L1; case TYident: tn->Tident = (char *)MEM_PH_STRDUP(t->Tident); break; #endif case TYarray: if (tn->Tflags & TFvla) tn->Tel = el_copytree(tn->Tel); break; default: if (tyfunc(tn->Tty)) { L1: tn->Tparamtypes = NULL; for (p = t->Tparamtypes; p; p = p->Pnext) { param_t *pn; pn = param_append_type(&tn->Tparamtypes,p->Ptype); if (p->Pident) { pn->Pident = (char *)MEM_PH_STRDUP(p->Pident); } assert(!p->Pelem); } } #if SCPP else if (tn->Talternate && typtr(tn->Tty)) tn->Talternate->Tcount++; #endif #if MARS else if (tn->Tkey && typtr(tn->Tty)) tn->Tkey->Tcount++; #endif break; } if (tn->Tnext) { type_debug(tn->Tnext); tn->Tnext->Tcount++; } tn->Tcount = 0; return tn; }
/*************** * Generate code for OPvecfill (broadcast). * OPvecfill takes the single value in e1 and * fills the vector type with it. */ code *cdvecfill(elem *e, regm_t *pretregs) { //printf("cdvecfill(e = %p, *pretregs = %s)\n",e,regm_str(*pretregs)); regm_t retregs = *pretregs & XMMREGS; if (!retregs) retregs = XMMREGS; CodeBuilder cdb; code *c; code cs; elem *e1 = e->E1; #if 0 if ((e1->Eoper == OPind && !e1->Ecount) || e1->Eoper == OPvar) { cr = getlvalue(&cs, e1, RMload | retregs); // get addressing mode } else { unsigned rretregs = XMMREGS & ~retregs; cr = scodelem(op2, &rretregs, retregs, TRUE); unsigned rreg = findreg(rretregs) - XMM0; cs.Irm = modregrm(3,0,rreg & 7); cs.Iflags = 0; cs.Irex = 0; if (rreg & 8) cs.Irex |= REX_B; } #endif unsigned reg; unsigned rreg; unsigned varreg; regm_t varregm; tym_t ty = tybasic(e->Ety); switch (ty) { case TYfloat4: case TYfloat8: if (config.avx && ((e1->Eoper == OPind && !e1->Ecount) || e1->Eoper == OPvar && !isregvar(e1,&varregm,&varreg)) || tysize(ty) == 32 && !isregvar(e1,&varregm,&varreg) ) { Lint: if (e1->Eoper == OPvar) e1->EV.sp.Vsym->Sflags &= ~GTregcand; // VBROADCASTSS XMM,MEM cdb.append(getlvalue(&cs, e1, 0)); // get addressing mode assert((cs.Irm & 0xC0) != 0xC0); // AVX1 doesn't have register source operands cdb.append(allocreg(&retregs,®,ty)); cs.Iop = VBROADCASTSS; cs.Irex &= ~REX_W; code_newreg(&cs,reg - XMM0); checkSetVex(&cs,ty); cdb.gen(&cs); } else { // SHUFPS XMM0,XMM0,0 0F C6 /r ib c = codelem(e1,&retregs,FALSE); // eval left leaf cdb.append(c); reg = findreg(retregs) - XMM0; cdb.append(getregs(retregs)); cs.Iop = SHUFPS; cs.Irm = modregxrmx(3,reg,reg); cs.Iflags = 0; cs.IFL2 = FLconst; cs.IEV2.Vsize_t = 0; if (config.avx >= 2 || tysize(ty) == 32) { // VBROADCASTSS XMM,XMM cs.Iop = VBROADCASTSS; checkSetVex(&cs, ty); } cdb.gen(&cs); } break; case TYdouble2: case TYdouble4: if (config.avx && ((e1->Eoper == OPind && !e1->Ecount) || e1->Eoper == OPvar && !isregvar(e1,&varregm,&varreg)) || tysize(ty) == 32 && !isregvar(e1,&varregm,&varreg) ) { if (e1->Eoper == OPvar) e1->EV.sp.Vsym->Sflags &= ~GTregcand; // VBROADCASTSD XMM,MEM cdb.append(getlvalue(&cs, e1, 0)); // get addressing mode assert((cs.Irm & 0xC0) != 0xC0); // AVX1 doesn't have register source operands cdb.append(allocreg(&retregs,®,ty)); cs.Iop = VBROADCASTSD; cs.Irex &= ~REX_W; code_newreg(&cs,reg - XMM0); checkSetVex(&cs,ty); cdb.gen(&cs); } else { // UNPCKLPD XMM0,XMM0 66 0F 14 /r c = codelem(e1,&retregs,FALSE); // eval left leaf cdb.append(c); reg = findreg(retregs) - XMM0; cdb.append(getregs(retregs)); cs.Iop = UNPCKLPD; cs.Irm = modregxrmx(3,reg,reg); cs.Iflags = 0; if (config.avx >= 2 || tysize(ty) == 32) { // VBROADCASTSD XMM,XMM cs.Iop = VBROADCASTSD; checkSetVex(&cs, ty); } cdb.gen(&cs); } break; case TYschar16: case TYuchar16: case TYschar32: case TYuchar32: { /* MOVD XMM0,r * PUNPCKLBW XMM0,XMM0 * PUNPCKLWD XMM0,XMM0 * PSHUFD XMM0,XMM0,0 */ regm_t regm = ALLREGS; c = codelem(e1,®m,FALSE); // eval left leaf cdb.append(c); unsigned r = findreg(regm); c = allocreg(&retregs,®, e->Ety); cdb.append(c); reg -= XMM0; cdb.gen2(LODD,modregxrmx(3,reg,r)); // MOVD reg,r checkSetVex(cdb.last(),TYschar16); cs.Iop = PUNPCKLBW; cs.Irm = modregxrmx(3,reg,reg); cs.Iflags = 0; cdb.gen(&cs); cs.Iop = PUNPCKLWD; cdb.gen(&cs); cs.Iop = PSHUFD; cs.IFL2 = FLconst; cs.IEV2.Vsize_t = 0; checkSetVex(&cs,TYschar16); cdb.gen(&cs); if (tysize(ty) == 32) { // VINSERTF128 YMM0,YMM0,XMM0,1 cs.Iop = VINSERTF128; cs.Irm = modregxrmx(3,reg,reg); cs.Iflags = 0; cs.IFL2 = FLconst; cs.IEV2.Vsize_t = 1; checkSetVex(&cs,ty); cdb.gen(&cs); } break; } case TYshort8: case TYushort8: case TYshort16: case TYushort16: { regm_t regm = ALLREGS; c = codelem(e1,®m,FALSE); // eval left leaf cdb.append(c); unsigned r = findreg(regm); if (config.avx || tysize(ty) == 32) { /* * VPXOR XMM0,XMM0,XMM0 * VPINSRW XMM0,XMM0,r,0 * VPINSRW XMM0,XMM0,r,1 * VPINSRW XMM0,XMM0,r,2 * VPINSRW XMM0,XMM0,r,3 */ cdb.append(allocreg(&retregs,®, ty)); cdb.gen2(PXOR,modregxrmx(3,reg-XMM0,reg-XMM0)); checkSetVex(cdb.last(), TYshort8); for (int i = 0; i < tysize(ty) / 4; ++i) { cdb.genc2(PINSRW,modregxrmx(3,reg-XMM0,r),i); checkSetVex(cdb.last(), TYshort8); } if (tysize(ty) == 32) { // VINSERTF128 YMM0,YMM0,XMM0,1 cs.Iop = VINSERTF128; cs.Irm = modregxrmx(3,reg-XMM0,reg-XMM0); cs.Iflags = 0; cs.IFL2 = FLconst; cs.IEV2.Vsize_t = 1; checkSetVex(&cs,ty); cdb.gen(&cs); } else { // VPSHUFD XMM0,XMM0,0 cs.Iop = PSHUFD; cs.Irm = modregxrmx(3,reg-XMM0,reg-XMM0); cs.Iflags = 0; cs.IFL2 = FLconst; cs.IEV2.Vsize_t = 0; checkSetVex(&cs,ty); cdb.gen(&cs); } } else { /* MOVD XMM0,r * PUNPCKLWD XMM0,XMM0 * PSHUFD XMM0,XMM0,0 */ c = allocreg(&retregs,®, e->Ety); cdb.append(c); reg -= XMM0; cdb.gen2(LODD,modregxrmx(3,reg,r)); // MOVD reg,r checkSetVex(cdb.last(),e->Ety); cs.Iop = PUNPCKLWD; cs.Irm = modregxrmx(3,reg,reg); cs.Iflags = 0; cdb.gen(&cs); cs.Iop = PSHUFD; cs.IFL2 = FLconst; cs.IEV2.Vsize_t = 0; cdb.gen(&cs); } break; } case TYlong8: case TYulong8: case TYlong4: case TYulong4: { if (config.avx && ((e1->Eoper == OPind && !e1->Ecount) || e1->Eoper == OPvar && !isregvar(e1,&varregm,&varreg)) || tysize(ty) == 32 && !isregvar(e1,&varregm,&varreg)) { goto Lint; } /* MOVD XMM1,r * PSHUFD XMM0,XMM1,0 */ regm_t regm = ALLREGS; c = codelem(e1,®m,FALSE); // eval left leaf cdb.append(c); unsigned r = findreg(regm); c = allocreg(&retregs,®, e->Ety); cdb.append(c); reg -= XMM0; cdb.gen2(LODD,modregxrmx(3,reg,r)); // MOVD reg,r cs.Iop = PSHUFD; cs.Irm = modregxrmx(3,reg,reg); cs.Iflags = 0; cs.IFL2 = FLconst; cs.IEV2.Vsize_t = 0; if (config.avx >= 2 || tysize(ty) == 32) { // VBROADCASTSS XMM,XMM cs.Iop = VBROADCASTSS; checkSetVex(&cs, ty); } cdb.gen(&cs); break; } case TYllong2: case TYullong2: case TYllong4: case TYullong4: if (config.avx || tysize(ty) >= 32) { if (e1->Eoper == OPvar) e1->EV.sp.Vsym->Sflags &= ~GTregcand; // VMOVDDUP XMM,MEM cdb.append(getlvalue(&cs, e1, 0)); // get addressing mode if ((cs.Irm & 0xC0) == 0xC0) { unsigned sreg = ((cs.Irm & 7) | (cs.Irex & REX_B ? 8 : 0)); regm_t sregm = XMMREGS; cdb.append(fixresult(e1, mask[sreg], &sregm)); unsigned rmreg = findreg(sregm); cs.Irm = (cs.Irm & ~7) | ((rmreg - XMM0) & 7); if ((rmreg - XMM0) & 8) cs.Irex |= REX_B; else cs.Irex &= ~REX_B; } cdb.append(allocreg(&retregs,®,ty)); if (config.avx >= 2 || tysize(ty) >= 32) { cs.Iop = VBROADCASTSD; cs.Irex &= ~REX_W; } else cs.Iop = MOVDDUP; code_newreg(&cs,reg - XMM0); checkSetVex(&cs,ty); cdb.gen(&cs); } else { /* MOVQ XMM0,mem128 * PUNPCKLQDQ XMM0,XMM0 */ c = codelem(e1,&retregs,FALSE); // eval left leaf cdb.append(c); unsigned reg = findreg(retregs); reg -= XMM0; //cdb.gen2(LODD,modregxrmx(3,reg,r)); // MOVQ reg,r cs.Iop = PUNPCKLQDQ; cs.Irm = modregxrmx(3,reg,reg); cs.Iflags = 0; cdb.gen(&cs); } break; default: assert(0); } c = fixresult(e,retregs,pretregs); cdb.append(c); return cdb.finish(); }
code *xmmeq(elem *e, unsigned op, elem *e1, elem *e2,regm_t *pretregs) { tym_t tymll; unsigned reg; int i; code cs; elem *e11; bool regvar; /* TRUE means evaluate into register variable */ regm_t varregm; unsigned varreg; targ_int postinc; //printf("xmmeq(e1 = %p, e2 = %p, *pretregs = %s)\n", e1, e2, regm_str(*pretregs)); int e2oper = e2->Eoper; tym_t tyml = tybasic(e1->Ety); /* type of lvalue */ regm_t retregs = *pretregs; if (!(retregs & XMMREGS)) retregs = XMMREGS; // pick any XMM reg bool aligned = xmmIsAligned(e1); cs.Iop = (op == OPeq) ? xmmstore(tyml, aligned) : op; regvar = FALSE; varregm = 0; if (config.flags4 & CFG4optimized) { // Be careful of cases like (x = x+x+x). We cannot evaluate in // x if x is in a register. if (isregvar(e1,&varregm,&varreg) && // if lvalue is register variable doinreg(e1->EV.sp.Vsym,e2) && // and we can compute directly into it varregm & XMMREGS ) { regvar = TRUE; retregs = varregm; reg = varreg; /* evaluate directly in target register */ } } if (*pretregs & mPSW && !EOP(e1)) // if evaluating e1 couldn't change flags { // Be careful that this lines up with jmpopcode() retregs |= mPSW; *pretregs &= ~mPSW; } CodeBuilder cdb; cdb.append(scodelem(e2,&retregs,0,TRUE)); // get rvalue // Look for special case of (*p++ = ...), where p is a register variable if (e1->Eoper == OPind && ((e11 = e1->E1)->Eoper == OPpostinc || e11->Eoper == OPpostdec) && e11->E1->Eoper == OPvar && e11->E1->EV.sp.Vsym->Sfl == FLreg ) { postinc = e11->E2->EV.Vint; if (e11->Eoper == OPpostdec) postinc = -postinc; cdb.append(getlvalue(&cs,e11,RMstore | retregs)); freenode(e11->E2); } else { postinc = 0; cdb.append(getlvalue(&cs,e1,RMstore | retregs)); // get lvalue (cl == CNIL if regvar) } cdb.append(getregs_imm(regvar ? varregm : 0)); reg = findreg(retregs & XMMREGS); cs.Irm |= modregrm(0,(reg - XMM0) & 7,0); if ((reg - XMM0) & 8) cs.Irex |= REX_R; // Do not generate mov from register onto itself if (!(regvar && reg == XMM0 + ((cs.Irm & 7) | (cs.Irex & REX_B ? 8 : 0)))) { cdb.gen(&cs); // MOV EA+offset,reg if (op == OPeq) checkSetVex(cdb.last(), tyml); } if (e1->Ecount || // if lvalue is a CSE or regvar) // rvalue can't be a CSE { cdb.append(getregs_imm(retregs)); // necessary if both lvalue and // rvalue are CSEs (since a reg // can hold only one e at a time) cssave(e1,retregs,EOP(e1)); // if lvalue is a CSE } cdb.append(fixresult(e,retregs,pretregs)); Lp: if (postinc) { int reg = findreg(idxregm(&cs)); if (*pretregs & mPSW) { // Use LEA to avoid touching the flags unsigned rm = cs.Irm & 7; if (cs.Irex & REX_B) rm |= 8; cdb.genc1(0x8D,buildModregrm(2,reg,rm),FLconst,postinc); if (tysize(e11->E1->Ety) == 8) code_orrex(cdb.last(), REX_W); } else if (I64) { cdb.genc2(0x81,modregrmx(3,0,reg),postinc); if (tysize(e11->E1->Ety) == 8) code_orrex(cdb.last(), REX_W); } else { if (postinc == 1) cdb.gen1(0x40 + reg); // INC reg else if (postinc == -(targ_int)1) cdb.gen1(0x48 + reg); // DEC reg else { cdb.genc2(0x81,modregrm(3,0,reg),postinc); } } } freenode(e1); return cdb.finish(); }
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 }
code *cdvector(elem *e, regm_t *pretregs) { /* e should look like one of: * vector * | * param * / \ * param op2 * / \ * op op1 */ if (!config.fpxmmregs) { printf("SIMD operations not supported on this platform\n"); exit(1); } unsigned n = el_nparams(e->E1); elem **params = (elem **)malloc(n * sizeof(elem *)); assert(params); elem **tmp = params; el_paramArray(&tmp, e->E1); #if 0 printf("cdvector()\n"); for (int i = 0; i < n; i++) { printf("[%d]: ", i); elem_print(params[i]); } #endif if (*pretregs == 0) { /* Evaluate for side effects only */ CodeBuilder cdb; for (int i = 0; i < n; i++) { cdb.append(codelem(params[i], pretregs, FALSE)); *pretregs = 0; // in case they got set } return cdb.finish(); } assert(n >= 2 && n <= 4); elem *eop = params[0]; elem *op1 = params[1]; elem *op2 = NULL; tym_t ty2 = 0; if (n >= 3) { op2 = params[2]; ty2 = tybasic(op2->Ety); } unsigned op = el_tolong(eop); #ifdef DEBUG assert(!isXMMstore(op)); #endif tym_t ty1 = tybasic(op1->Ety); unsigned sz1 = _tysize[ty1]; // assert(sz1 == 16); // float or double regm_t retregs; CodeBuilder cdb; if (n == 3 && ty2 == TYuchar && op2->Eoper == OPconst) { // Handle: op xmm,imm8 retregs = *pretregs & XMMREGS; if (!retregs) retregs = XMMREGS; cdb.append(codelem(op1,&retregs,FALSE)); // eval left leaf unsigned reg = findreg(retregs); int r; switch (op) { case PSLLD: r = 6; op = 0x660F72; break; case PSLLQ: r = 6; op = 0x660F73; break; case PSLLW: r = 6; op = 0x660F71; break; case PSRAD: r = 4; op = 0x660F72; break; case PSRAW: r = 4; op = 0x660F71; break; case PSRLD: r = 2; op = 0x660F72; break; case PSRLQ: r = 2; op = 0x660F73; break; case PSRLW: r = 2; op = 0x660F71; break; case PSRLDQ: r = 3; op = 0x660F73; break; case PSLLDQ: r = 7; op = 0x660F73; break; default: printf("op = x%x\n", op); assert(0); break; } cdb.append(getregs(retregs)); cdb.genc2(op,modregrmx(3,r,reg-XMM0), el_tolong(op2)); } else if (n == 2) { /* Handle: op xmm,mem * where xmm is written only, not read */ code cs; if ((op1->Eoper == OPind && !op1->Ecount) || op1->Eoper == OPvar) { cdb.append(getlvalue(&cs, op1, RMload)); // get addressing mode } else { regm_t rretregs = XMMREGS; cdb.append(codelem(op1, &rretregs, FALSE)); unsigned rreg = findreg(rretregs) - XMM0; cs.Irm = modregrm(3,0,rreg & 7); cs.Iflags = 0; cs.Irex = 0; if (rreg & 8) cs.Irex |= REX_B; } retregs = *pretregs & XMMREGS; if (!retregs) retregs = XMMREGS; unsigned reg; cdb.append(allocreg(&retregs, ®, e->Ety)); code_newreg(&cs, reg - XMM0); cs.Iop = op; cdb.gen(&cs); } else if (n == 3 || n == 4) { /* Handle: * op xmm,mem // n = 3 * op xmm,mem,imm8 // n = 4 * Both xmm and mem are operands, evaluate xmm first. */ code cs; retregs = *pretregs & XMMREGS; if (!retregs) retregs = XMMREGS; cdb.append(codelem(op1,&retregs,FALSE)); // eval left leaf unsigned reg = findreg(retregs); if ((op2->Eoper == OPind && !op2->Ecount) || op2->Eoper == OPvar) { cdb.append(getlvalue(&cs, op2, RMload | retregs)); // get addressing mode } else { unsigned rretregs = XMMREGS & ~retregs; cdb.append(scodelem(op2, &rretregs, retregs, TRUE)); unsigned rreg = findreg(rretregs) - XMM0; cs.Irm = modregrm(3,0,rreg & 7); cs.Iflags = 0; cs.Irex = 0; if (rreg & 8) cs.Irex |= REX_B; } cdb.append(getregs(retregs)); if (n == 4) { switch (op) { case CMPPD: case CMPSS: case CMPSD: case CMPPS: case PSHUFD: case PSHUFHW: case PSHUFLW: case BLENDPD: case BLENDPS: case DPPD: case DPPS: case MPSADBW: case PBLENDW: case ROUNDPD: case ROUNDPS: case ROUNDSD: case ROUNDSS: case SHUFPD: case SHUFPS: break; default: printf("op = x%x\n", op); assert(0); break; } elem *imm8 = params[3]; cs.IFL2 = FLconst; cs.IEV2.Vsize_t = el_tolong(imm8); } code_newreg(&cs, reg - XMM0); cs.Iop = op; cdb.gen(&cs); } else assert(0); cdb.append(fixresult(e,retregs,pretregs)); free(params); freenode(e); return cdb.finish(); }
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 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 cod5_prol_epi() { tym_t tym; tym_t tyf; block *b; block *bp; list_t bl; int nepis; tyf = funcsym_p->ty(); tym = tybasic(tyf); goto L1; if (!(config.flags4 & CFG4optimized) || anyiasm || usedalloca || usednteh || tyf & (mTYnaked | mTYloadds) || tym == TYifunc || tym == TYmfunc || // can't yet handle ECX passed as parameter tym == TYjfunc || // can't yet handle EAX passed as parameter config.flags & (CFGalwaysframe | CFGtrace) || // config.fulltypes || (config.wflags & WFwindows && tyfarfunc(tym)) || need_prolog(startblock) ) { // First block gets the prolog, all return blocks // get the epilog. //printf("not even a candidate\n"); L1: cod5_noprol(); return; } // Turn on BFLoutsideprolog for all blocks outside the ones needing the prolog. for (b = startblock; b; b = b->Bnext) b->Bflags &= ~BFLoutsideprolog; // start with them all off pe_add(startblock); // Look for only one block (bp) that will hold the prolog bp = NULL; nepis = 0; for (b = startblock; b; b = b->Bnext) { int mark; if (b->Bflags & BFLoutsideprolog) continue; // If all predecessors are marked mark = 0; assert(b->Bpred); for (bl = b->Bpred; bl; bl = list_next(bl)) { if (list_block(bl)->Bflags & BFLoutsideprolog) { if (mark == 2) goto L1; mark = 1; } else { if (mark == 1) goto L1; mark = 2; } } if (mark == 1) { if (bp) // if already have one goto L1; bp = b; } // See if b is an epilog mark = 0; for (bl = b->Bsucc; bl; bl = list_next(bl)) { if (list_block(bl)->Bflags & BFLoutsideprolog) { if (mark == 2) goto L1; mark = 1; } else { if (mark == 1) goto L1; mark = 2; } } if (mark == 1 || b->BC == BCret || b->BC == BCretexp) { b->Bflags |= BFLepilog; nepis++; if (nepis > 1 && config.flags4 & CFG4space) goto L1; } } if (bp) { bp->Bflags |= BFLprolog; //printf("=============== prolog opt\n"); } }
void visit(TypeStruct *t) { //printf("TypeStruct::toCtype() '%s'\n", t->sym->toChars()); Type *tm = t->mutableOf(); if (tm->ctype) { t->ctype = type_alloc(tybasic(tm->ctype->Tty)); t->ctype->Tcount++; if (t->ctype->Tty == TYstruct) { Symbol *s = tm->ctype->Ttag; t->ctype->Ttag = (Classsym *)s; // structure tag name } // Add modifiers switch (t->mod) { case 0: assert(0); break; case MODconst: case MODwild: case MODwildconst: t->ctype->Tty |= mTYconst; break; case MODshared: t->ctype->Tty |= mTYshared; break; case MODshared | MODconst: case MODshared | MODwild: case MODshared | MODwildconst: t->ctype->Tty |= mTYshared | mTYconst; break; case MODimmutable: t->ctype->Tty |= mTYimmutable; break; default: assert(0); } } else { StructDeclaration *sym = t->sym; if (sym->ident == Id::__c_long_double) { t->ctype = type_fake(TYdouble); t->ctype->Tcount++; return; } t->ctype = type_struct_class(sym->toPrettyChars(true), sym->alignsize, sym->structsize, sym->arg1type ? Type_toCtype(sym->arg1type) : NULL, sym->arg2type ? Type_toCtype(sym->arg2type) : NULL, sym->isUnionDeclaration() != 0, false, sym->isPOD() != 0); tm->ctype = t->ctype; /* Add in fields of the struct * (after setting ctype to avoid infinite recursion) */ if (global.params.symdebug) { for (size_t i = 0; i < sym->fields.dim; i++) { VarDeclaration *v = sym->fields[i]; symbol_struct_addField(t->ctype->Ttag, v->ident->toChars(), Type_toCtype(v->type), v->offset); } } } //printf("t = %p, Tflags = x%x\n", ctype, ctype->Tflags); }
code *xmmpost(elem *e,regm_t *pretregs) { elem *e1 = e->E1; elem *e2 = e->E2; tym_t ty1 = tybasic(e1->Ety); CodeBuilder cdb; regm_t retregs; unsigned reg; bool regvar = FALSE; if (config.flags4 & CFG4optimized) { // Be careful of cases like (x = x+x+x). We cannot evaluate in // x if x is in a register. unsigned varreg; regm_t varregm; if (isregvar(e1,&varregm,&varreg) && // if lvalue is register variable doinreg(e1->EV.sp.Vsym,e2) // and we can compute directly into it ) { regvar = TRUE; retregs = varregm; reg = varreg; // evaluate directly in target register cdb.append(getregs(retregs)); // destroy these regs } } code cs; if (!regvar) { code *c = getlvalue(&cs,e1,0); // get EA cdb.append(c); retregs = XMMREGS & ~*pretregs; if (!retregs) retregs = XMMREGS; c = allocreg(&retregs,®,ty1); cdb.append(c); cs.Iop = xmmload(ty1, true); // MOVSD xmm,xmm_m64 code_newreg(&cs,reg - XMM0); cdb.gen(&cs); checkSetVex(cdb.last(), ty1); } // Result register regm_t resultregs = XMMREGS & *pretregs & ~retregs; if (!resultregs) resultregs = XMMREGS & ~retregs; unsigned resultreg; code *c = allocreg(&resultregs, &resultreg, ty1); cdb.append(c); cdb.gen2(xmmload(ty1,true),modregxrmx(3,resultreg-XMM0,reg-XMM0)); // MOVSS/D resultreg,reg checkSetVex(cdb.last(), ty1); regm_t rretregs = XMMREGS & ~(*pretregs | retregs | resultregs); if (!rretregs) rretregs = XMMREGS & ~(retregs | resultregs); c = codelem(e2,&rretregs,FALSE); // eval right leaf cdb.append(c); unsigned rreg = findreg(rretregs); unsigned op = xmmoperator(e1->Ety, e->Eoper); cdb.gen2(op,modregxrmx(3,reg-XMM0,rreg-XMM0)); // ADD reg,rreg checkSetVex(cdb.last(), e1->Ety); if (!regvar) { cs.Iop = xmmstore(ty1,true); // reverse operand order of MOVS[SD] cdb.gen(&cs); checkSetVex(cdb.last(), ty1); } if (e1->Ecount || // if lvalue is a CSE or regvar) // rvalue can't be a CSE { cdb.append(getregs_imm(retregs)); // necessary if both lvalue and // rvalue are CSEs (since a reg // can hold only one e at a time) cssave(e1,retregs,EOP(e1)); // if lvalue is a CSE } cdb.append(fixresult(e,resultregs,pretregs)); freenode(e1); return cdb.finish(); }
void WReqn(elem *e) { static int nest; if (!e) return; if (OTunary(e->Eoper)) { WROP(e->Eoper); if (OTbinary(e->E1->Eoper)) { nest++; ferr("("); WReqn(e->E1); ferr(")"); nest--; } else WReqn(e->E1); } else if (e->Eoper == OPcomma && !nest) { WReqn(e->E1); dbg_printf(";\n\t"); WReqn(e->E2); } else if (OTbinary(e->Eoper)) { if (OTbinary(e->E1->Eoper)) { nest++; ferr("("); WReqn(e->E1); ferr(")"); nest--; } else WReqn(e->E1); ferr(" "); WROP(e->Eoper); if (e->Eoper == OPstreq) dbg_printf("%ld",(long)type_size(e->ET)); ferr(" "); if (OTbinary(e->E2->Eoper)) { nest++; ferr("("); WReqn(e->E2); ferr(")"); nest--; } else WReqn(e->E2); } else { switch (e->Eoper) { case OPconst: switch (tybasic(e->Ety)) { case TYfloat: dbg_printf("%g <float> ",e->EV.Vfloat); break; case TYdouble: dbg_printf("%g ",e->EV.Vdouble); break; case TYldouble: dbg_printf("%Lg ",e->EV.Vldouble); break; case TYcent: case TYucent: dbg_printf("%lld+%lld ", e->EV.Vcent.msw, e->EV.Vcent.lsw); break; default: dbg_printf("%lld ",el_tolong(e)); break; } break; case OPrelconst: ferr("#"); /* FALL-THROUGH */ case OPvar: dbg_printf("%s",e->EV.sp.Vsym->Sident); if (e->EV.sp.Vsym->Ssymnum != -1) dbg_printf("(%d)",e->EV.sp.Vsym->Ssymnum); if (e->Eoffset != 0) { if (sizeof(e->Eoffset) == 8) dbg_printf(".x%llx", e->Eoffset); else dbg_printf(".%ld",(long)e->Eoffset); } break; case OPasm: case OPstring: dbg_printf("\"%s\"",e->EV.ss.Vstring); if (e->EV.ss.Voffset) dbg_printf("+%ld",(long)e->EV.ss.Voffset); break; case OPmark: case OPgot: case OPframeptr: case OPhalt: WROP(e->Eoper); break; case OPstrthis: break; default: WROP(e->Eoper); assert(0); } } }
static unsigned xmmoperator(tym_t tym, unsigned oper) { tym = tybasic(tym); unsigned op; switch (oper) { case OPadd: case OPaddass: case OPpostinc: switch (tym) { case TYfloat: case TYifloat: op = ADDSS; break; case TYdouble: case TYidouble: op = ADDSD; break; // SIMD vector types case TYfloat8: case TYfloat4: op = ADDPS; break; case TYdouble4: case TYdouble2: op = ADDPD; break; case TYschar32: case TYuchar32: case TYschar16: case TYuchar16: op = PADDB; break; case TYshort16: case TYushort16: case TYshort8: case TYushort8: op = PADDW; break; case TYlong8: case TYulong8: case TYlong4: case TYulong4: op = PADDD; break; case TYllong4: case TYullong4: case TYllong2: case TYullong2: op = PADDQ; break; default: printf("tym = x%x\n", tym); assert(0); } break; case OPmin: case OPminass: case OPpostdec: switch (tym) { case TYfloat: case TYifloat: op = SUBSS; break; case TYdouble: case TYidouble: op = SUBSD; break; // SIMD vector types case TYfloat8: case TYfloat4: op = SUBPS; break; case TYdouble4: case TYdouble2: op = SUBPD; break; case TYschar32: case TYuchar32: case TYschar16: case TYuchar16: op = PSUBB; break; case TYshort16: case TYushort16: case TYshort8: case TYushort8: op = PSUBW; break; case TYlong8: case TYulong8: case TYlong4: case TYulong4: op = PSUBD; break; case TYllong4: case TYullong4: case TYllong2: case TYullong2: op = PSUBQ; break; default: assert(0); } break; case OPmul: case OPmulass: switch (tym) { case TYfloat: case TYifloat: op = MULSS; break; case TYdouble: case TYidouble: op = MULSD; break; // SIMD vector types case TYfloat8: case TYfloat4: op = MULPS; break; case TYdouble4: case TYdouble2: op = MULPD; break; case TYshort16: case TYushort16: case TYshort8: case TYushort8: op = PMULLW; break; default: assert(0); } break; case OPdiv: case OPdivass: switch (tym) { case TYfloat: case TYifloat: op = DIVSS; break; case TYdouble: case TYidouble: op = DIVSD; break; // SIMD vector types case TYfloat8: case TYfloat4: op = DIVPS; break; case TYdouble4: case TYdouble2: op = DIVPD; break; default: assert(0); } break; case OPor: case OPorass: switch (tym) { // SIMD vector types case TYschar16: case TYuchar16: case TYshort8: case TYushort8: case TYlong4: case TYulong4: case TYllong2: case TYullong2: case TYschar32: case TYuchar32: case TYshort16: case TYushort16: case TYlong8: case TYulong8: case TYllong4: case TYullong4: op = POR; break; default: assert(0); } break; case OPand: case OPandass: switch (tym) { // SIMD vector types case TYschar16: case TYuchar16: case TYshort8: case TYushort8: case TYlong4: case TYulong4: case TYllong2: case TYullong2: case TYschar32: case TYuchar32: case TYshort16: case TYushort16: case TYlong8: case TYulong8: case TYllong4: case TYullong4: op = PAND; break; default: assert(0); } break; case OPxor: case OPxorass: switch (tym) { // SIMD vector types case TYschar16: case TYuchar16: case TYshort8: case TYushort8: case TYlong4: case TYulong4: case TYllong2: case TYullong2: case TYschar32: case TYuchar32: case TYshort16: case TYushort16: case TYlong8: case TYulong8: case TYllong4: case TYullong4: op = PXOR; break; default: assert(0); } break; case OPlt: case OPle: case OPgt: case OPge: case OPne: case OPeqeq: case OPunord: /* !<>= */ case OPlg: /* <> */ case OPleg: /* <>= */ case OPule: /* !> */ case OPul: /* !>= */ case OPuge: /* !< */ case OPug: /* !<= */ case OPue: /* !<> */ case OPngt: case OPnge: case OPnlt: case OPnle: case OPord: case OPnlg: case OPnleg: case OPnule: case OPnul: case OPnuge: case OPnug: case OPnue: switch (tym) { case TYfloat: case TYifloat: op = UCOMISS; break; case TYdouble: case TYidouble: op = UCOMISD; break; default: assert(0); } break; default: assert(0); } return op; }
void type_print(type *t) { type_debug(t); dbg_printf("Tty="); WRTYxx(t->Tty); dbg_printf(" Tmangle=%d",t->Tmangle); dbg_printf(" Tflags=x%x",t->Tflags); dbg_printf(" Tcount=%d",t->Tcount); if (!(t->Tflags & TFsizeunknown) && tybasic(t->Tty) != TYvoid && tybasic(t->Tty) != TYident && tybasic(t->Tty) != TYtemplate && tybasic(t->Tty) != TYmfunc && tybasic(t->Tty) != TYarray) dbg_printf(" Tsize=%lld",(long long)type_size(t)); dbg_printf(" Tnext=%p",t->Tnext); switch (tybasic(t->Tty)) { case TYstruct: case TYmemptr: dbg_printf(" Ttag=%p,'%s'",t->Ttag,t->Ttag->Sident); //dbg_printf(" Sfldlst=%p",t->Ttag->Sstruct->Sfldlst); break; case TYarray: dbg_printf(" Tdim=%ld",(long)t->Tdim); break; case TYident: dbg_printf(" Tident='%s'",t->Tident); break; case TYtemplate: dbg_printf(" Tsym='%s'",((typetemp_t *)t)->Tsym->Sident); { param_t *p; int i; i = 1; for (p = t->Tparamtypes; p; p = p->Pnext) { dbg_printf("\nTP%d (%p): ",i++,p); fflush(stdout); dbg_printf("Pident=%p,Ptype=%p,Pelem=%p,Pnext=%p ",p->Pident,p->Ptype,p->Pelem,p->Pnext); param_debug(p); if (p->Pident) printf("'%s' ", p->Pident); if (p->Ptype) type_print(p->Ptype); if (p->Pelem) elem_print(p->Pelem); } } break; default: if (tyfunc(t->Tty)) { param_t *p; int i; i = 1; for (p = t->Tparamtypes; p; p = p->Pnext) { dbg_printf("\nP%d (%p): ",i++,p); fflush(stdout); dbg_printf("Pident=%p,Ptype=%p,Pelem=%p,Pnext=%p ",p->Pident,p->Ptype,p->Pelem,p->Pnext); param_debug(p); if (p->Pident) printf("'%s' ", p->Pident); type_print(p->Ptype); } } break; } dbg_printf("\n"); if (t->Tnext) type_print(t->Tnext); }
void sliceStructs() { if (debugc) printf("sliceStructs()\n"); size_t sia_length = globsym.top; /* 3 is because it is used for two arrays, sia[] and sia2[]. * sia2[] can grow to twice the size of sia[], as symbols can get split into two. */ SymInfo *sia = (SymInfo *)malloc(3 * sia_length * sizeof(SymInfo)); assert(sia); SymInfo *sia2 = sia + sia_length; bool anySlice = false; for (int si = 0; si < globsym.top; si++) { Symbol *s = globsym.tab[si]; //printf("slice1: %s\n", s->Sident); if ((s->Sflags & (GTregcand | SFLunambig)) != (GTregcand | SFLunambig)) { sia[si].canSlice = false; continue; } targ_size_t sz = type_size(s->Stype); if (sz != 2 * REGSIZE || tyfv(s->Stype->Tty) || tybasic(s->Stype->Tty) == TYhptr) // because there is no TYseg { sia[si].canSlice = false; continue; } switch (s->Sclass) { case SCfastpar: case SCregister: case SCauto: case SCshadowreg: case SCparameter: anySlice = true; sia[si].canSlice = true; sia[si].accessSlice = false; // We can't slice whole XMM registers if (tyxmmreg(s->Stype->Tty) && s->Spreg >= XMM0 && s->Spreg <= XMM15 && s->Spreg2 == NOREG) { sia[si].canSlice = false; } break; case SCstack: case SCpseudo: case SCstatic: case SCbprel: sia[si].canSlice = false; break; default: symbol_print(s); assert(0); } } if (!anySlice) goto Ldone; for (block *b = startblock; b; b = b->Bnext) { if (b->BC == BCasm) goto Ldone; if (b->Belem) sliceStructs_Gather(sia, b->Belem); } { // scope needed because of goto skipping declarations bool any = false; int n = 0; // the number of symbols added for (int si = 0; si < sia_length; si++) { sia2[si + n].canSlice = false; if (sia[si].canSlice) { // If never did access it as a slice, don't slice if (!sia[si].accessSlice) { sia[si].canSlice = false; continue; } /* Split slice-able symbol sold into two symbols, * (sold,snew) in adjacent slots in the symbol table. */ Symbol *sold = globsym.tab[si + n]; size_t idlen = 2 + strlen(sold->Sident) + 2; char *id = (char *)malloc(idlen + 1); assert(id); sprintf(id, "__%s_%d", sold->Sident, REGSIZE); if (debugc) printf("creating slice symbol %s\n", id); Symbol *snew = symbol_calloc(id, idlen); free(id); snew->Sclass = sold->Sclass; snew->Sfl = sold->Sfl; snew->Sflags = sold->Sflags; if (snew->Sclass == SCfastpar || snew->Sclass == SCshadowreg) { snew->Spreg = sold->Spreg2; snew->Spreg2 = NOREG; sold->Spreg2 = NOREG; } type_free(sold->Stype); sold->Stype = type_fake(sia[si].ty0); sold->Stype->Tcount++; snew->Stype = type_fake(sia[si].ty1); snew->Stype->Tcount++; SYMIDX sinew = symbol_add(snew); for (int i = sinew; i > si + n + 1; --i) { globsym.tab[i] = globsym.tab[i - 1]; globsym.tab[i]->Ssymnum += 1; } globsym.tab[si + n + 1] = snew; snew->Ssymnum = si + n + 1; sia2[si + n].canSlice = true; sia2[si + n].si0 = si + n; sia2[si + n].ty0 = sia[si].ty0; sia2[si + n].ty1 = sia[si].ty1; ++n; any = true; } } if (!any) goto Ldone; } for (int si = 0; si < globsym.top; si++) { Symbol *s = globsym.tab[si]; assert(s->Ssymnum == si); } for (block *b = startblock; b; b = b->Bnext) { if (b->Belem) sliceStructs_Replace(sia2, b->Belem); } Ldone: free(sia); }
char *template_mangle(symbol *s,param_t *arglist) { /* mangling ::= '$' template_name { type | expr } type ::= "T" mangled type expr ::= integer | string | address | float | double | long_double integer ::= "I" dimension string ::= "S" string address ::= "R" zname float ::= "F" hex_digits double ::= "D" hex_digits long_double ::= "L" hex_digits */ param_t *p; assert(s); symbol_debug(s); //assert(s->Sclass == SCtemplate); //printf("\ntemplate_mangle(s = '%s', arglist = %p)\n", s->Sident, arglist); //arglist->print_list(); MangleInuse m; mangle.znamei = 0; mangle.argi = 0; mangle.np = mangle.buf; mangle.buf[BUFIDMAX + 1] = 0x55; if (NEWTEMPMANGLE) STR("?$"); else CHAR('$'); // BUG: this is for templates nested inside class scopes. // Need to check if it creates names that are properly unmanglable. cpp_zname(s->Sident); if (s->Sscope) cpp_scope(s->Sscope); for (p = arglist; p; p = p->Pnext) { if (p->Ptype) { /* Argument is a type */ if (!NEWTEMPMANGLE) CHAR('T'); cpp_argument_list(p->Ptype, 1); } else if (p->Psym) { CHAR('V'); // this is a 'class' name, but it should be a 'template' name cpp_ecsu_name(p->Psym); } else { /* Argument is an expression */ elem *e = p->Pelem; tym_t ty = tybasic(e->ET->Tty); char *p; char a[2]; int ni; char c; L2: switch (e->Eoper) { case OPconst: switch (ty) { case TYfloat: ni = FLOATSIZE; c = 'F'; goto L1; case TYdouble_alias: case TYdouble: ni = DOUBLESIZE; c = 'D'; goto L1; case TYldouble: ni = LNGDBLSIZE; c = 'L'; goto L1; L1: if (NEWTEMPMANGLE) CHAR('$'); CHAR(c); p = (char *)&e->EV.Vdouble; while (ni--) { char c; #if __GNUC__ static char hex[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; #else static char hex[16] = "0123456789ABCDEF"; #endif c = *p++; CHAR(hex[c & 15]); CHAR(hex[(c >> 4) & 15]); } break; default: #ifdef DEBUG if (!tyintegral(ty) && !tymptr(ty)) elem_print(e); #endif assert(tyintegral(ty) || tymptr(ty)); if (NEWTEMPMANGLE) STR("$0"); else CHAR('I'); cpp_dimension(el_tolongt(e)); break; } break; case OPstring: if (NEWTEMPMANGLE) STR("$S"); else CHAR('S'); if (e->EV.ss.Voffset) synerr(EM_const_init); // constant initializer expected cpp_string(e->EV.ss.Vstring,e->EV.ss.Vstrlen); break; case OPrelconst: if (e->EV.sp.Voffset) synerr(EM_const_init); // constant initializer expected s = e->EV.sp.Vsym; if (NEWTEMPMANGLE) { STR("$1"); cpp_decorated_name(s); } else { CHAR('R'); cpp_zname(s->Sident); } break; case OPvar: if (e->EV.sp.Vsym->Sflags & SFLvalue && tybasic(e->ET->Tty) != TYstruct) { e = e->EV.sp.Vsym->Svalue; goto L2; } else if (e->EV.sp.Vsym->Sclass == SCconst /*&& pstate.STintemplate*/) { CHAR('V'); // pretend to be a class name cpp_zname(e->EV.sp.Vsym->Sident); break; } default: #if SCPP #ifdef DEBUG if (!errcnt) elem_print(e); #endif synerr(EM_const_init); // constant initializer expected assert(errcnt); #endif break; } } } *mangle.np = 0; //printf("template_mangle() = '%s'\n", mangle.buf); assert(strlen(mangle.buf) <= BUFIDMAX); assert(mangle.buf[BUFIDMAX + 1] == 0x55); return mangle.buf; }
static void sliceStructs_Gather(SymInfo *sia, elem *e) { while (1) { switch (e->Eoper) { case OPvar: { SYMIDX si = e->EV.sp.Vsym->Ssymnum; if (si >= 0 && sia[si].canSlice) { assert(si < globsym.top); unsigned sz = tysize(e->Ety); if (sz == 2 * REGSIZE && !tyfv(e->Ety)) { // Rewritten as OPpair later } else if (sz == REGSIZE && (e->Eoffset == 0 || e->Eoffset == REGSIZE)) { if (!sia[si].accessSlice) { sia[si].ty0 = TYnptr; sia[si].ty1 = TYnptr; } sia[si].accessSlice = true; if (e->Eoffset == 0) sia[si].ty0 = tybasic(e->Ety); else sia[si].ty1 = tybasic(e->Ety); } else { sia[si].canSlice = false; } } return; } default: if (OTassign(e->Eoper)) { if (OTbinary(e->Eoper)) sliceStructs_Gather(sia, e->E2); // Assignment to a whole var will disallow SROA if (e->E1->Eoper == OPvar) { elem *e1 = e->E1; SYMIDX si = e1->EV.sp.Vsym->Ssymnum; if (si >= 0 && sia[si].canSlice) { assert(si < globsym.top); if (tysize(e1->Ety) != REGSIZE || (e1->Eoffset != 0 && e1->Eoffset != REGSIZE)) { sia[si].canSlice = false; } } return; } e = e->E1; break; } if (OTunary(e->Eoper)) { e = e->E1; break; } if (OTbinary(e->Eoper)) { sliceStructs_Gather(sia, e->E2); e = e->E1; break; } return; } } }
void outdata(symbol *s) { #if HTOD return; #endif dt_t *dtstart,*dt; targ_size_t datasize,a; int seg; targ_size_t offset; int flags; char *p; tym_t ty; int tls; symbol_debug(s); #ifdef DEBUG debugy && dbg_printf("outdata('%s')\n",s->Sident); #endif //printf("outdata('%s', ty=x%x)\n",s->Sident,s->Stype->Tty); //symbol_print(s); // Data segment variables are always live on exit from a function s->Sflags |= SFLlivexit; dtstart = s->Sdt; s->Sdt = NULL; // it will be free'd #if SCPP && TARGET_WINDOS if (eecontext.EEcompile) { s->Sfl = (s->ty() & mTYfar) ? FLfardata : FLextern; s->Sseg = UNKNOWN; goto Lret; // don't output any data } #endif datasize = 0; tls = 0; ty = s->ty(); if (ty & mTYexport && config.wflags & WFexpdef && s->Sclass != SCstatic) obj_export(s,0); // export data definition for (dt = dtstart; dt; dt = dt->DTnext) { //printf("dt = %p, dt = %d\n",dt,dt->dt); switch (dt->dt) { case DT_abytes: { // Put out the data for the string, and // reserve a spot for a pointer to that string #if ELFOBJ || MACHOBJ datasize += size(dt->Dty); dt->DTabytes += elf_data_cdata(dt->DTpbytes,dt->DTnbytes,&dt->DTseg); #else targ_size_t *poffset; datasize += size(dt->Dty); if (tybasic(dt->Dty) == TYcptr) { seg = cseg; poffset = &Coffset; } #if SCPP else if (tybasic(dt->Dty) == TYfptr && dt->DTnbytes > config.threshold) { seg = obj_fardata(s->Sident,dt->DTnbytes,&offset); poffset = &offset; } #endif else { seg = DATA; poffset = &Doffset; } dt->DTseg = seg; dt->DTabytes += *poffset; obj_bytes(seg,*poffset,dt->DTnbytes,dt->DTpbytes); *poffset += dt->DTnbytes; #endif break; } case DT_ibytes: datasize += dt->DTn; break; case DT_nbytes: //printf("DT_nbytes %d\n", dt->DTnbytes); datasize += dt->DTnbytes; break; case DT_symsize: #if MARS assert(0); #else dt->DTazeros = type_size(s->Stype); #endif goto case_azeros; case DT_azeros: /* A block of zeros */ //printf("DT_azeros %d\n", dt->DTazeros); case_azeros: datasize += dt->DTazeros; if (dt == dtstart && !dt->DTnext && s->Sclass != SCcomdat) { /* first and only, so put in BSS segment */ switch (ty & mTYLINK) { #if OMFOBJ case mTYfar: // if far data seg = obj_fardata(s->Sident,datasize,&s->Soffset); s->Sfl = FLfardata; break; #endif case mTYcs: seg = cseg; Coffset = align(datasize,Coffset); s->Soffset = Coffset; Coffset += datasize; s->Sfl = FLcsdata; break; case mTYthread: { seg_data *pseg = obj_tlsseg_bss(); #if ELFOBJ || MACHOBJ s->Sseg = pseg->SDseg; elf_data_start(s, datasize, pseg->SDseg); obj_lidata(pseg->SDseg, pseg->SDoffset, datasize); #else targ_size_t TDoffset = pseg->SDoffset; TDoffset = align(datasize,TDoffset); s->Soffset = TDoffset; TDoffset += datasize; pseg->SDoffset = TDoffset; #endif seg = pseg->SDseg; s->Sfl = FLtlsdata; tls = 1; break; } default: #if ELFOBJ || MACHOBJ seg = elf_data_start(s,datasize,UDATA); obj_lidata(s->Sseg,s->Soffset,datasize); #else seg = UDATA; UDoffset = align(datasize,UDoffset); s->Soffset = UDoffset; UDoffset += datasize; #endif s->Sfl = FLudata; // uninitialized data break; } #if ELFOBJ || MACHOBJ assert(s->Sseg != UNKNOWN); if (s->Sclass == SCglobal || s->Sclass == SCstatic) objpubdef(s->Sseg,s,s->Soffset); /* do the definition */ /* if a pubdef to be done */ #else s->Sseg = seg; if (s->Sclass == SCglobal) /* if a pubdef to be done */ objpubdef(seg,s,s->Soffset); /* do the definition */ #endif searchfixlist(s); if (config.fulltypes && !(s->Sclass == SCstatic && funcsym_p)) // not local static cv_outsym(s); #if SCPP out_extdef(s); #endif goto Lret; } break; case DT_common: assert(!dt->DTnext); outcommon(s,dt->DTazeros); goto Lret; case DT_xoff: { symbol *sb = dt->DTsym; if (tyfunc(sb->ty())) #if SCPP nwc_mustwrite(sb); #else ; #endif else if (sb->Sdt) // if initializer for symbol outdata(sb); // write out data for symbol } case DT_coff: datasize += size(dt->Dty); break; case DT_1byte: datasize++; break; default: #ifdef DEBUG dbg_printf("dt = %p, dt = %d\n",dt,dt->dt); #endif assert(0); } }
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 outdata(symbol *s) { #if HTOD return; #endif int seg; targ_size_t offset; int flags; const int codeseg = cseg; symbol_debug(s); #ifdef DEBUG debugy && dbg_printf("outdata('%s')\n",s->Sident); #endif //printf("outdata('%s', ty=x%x)\n",s->Sident,s->Stype->Tty); //symbol_print(s); // Data segment variables are always live on exit from a function s->Sflags |= SFLlivexit; dt_t *dtstart = s->Sdt; s->Sdt = NULL; // it will be free'd targ_size_t datasize = 0; tym_t ty = s->ty(); #if SCPP && TARGET_WINDOS if (eecontext.EEcompile) { s->Sfl = (s->ty() & mTYfar) ? FLfardata : FLextern; s->Sseg = UNKNOWN; goto Lret; // don't output any data } #endif if (ty & mTYexport && config.wflags & WFexpdef && s->Sclass != SCstatic) objmod->export_symbol(s,0); // export data definition for (dt_t *dt = dtstart; dt; dt = dt->DTnext) { //printf("\tdt = %p, dt = %d\n",dt,dt->dt); switch (dt->dt) { case DT_abytes: { // Put out the data for the string, and // reserve a spot for a pointer to that string datasize += size(dt->Dty); // reserve spot for pointer to string #if TARGET_SEGMENTED if (tybasic(dt->Dty) == TYcptr) { dt->DTseg = codeseg; dt->DTabytes += Offset(codeseg); goto L1; } else if (tybasic(dt->Dty) == TYfptr && dt->DTnbytes > config.threshold) { targ_size_t foffset; dt->DTseg = objmod->fardata(s->Sident,dt->DTnbytes,&foffset); dt->DTabytes += foffset; L1: objmod->write_bytes(SegData[dt->DTseg],dt->DTnbytes,dt->DTpbytes); break; } else #endif { dt->DTabytes += objmod->data_readonly(dt->DTpbytes,dt->DTnbytes,&dt->DTseg); } break; } case DT_ibytes: datasize += dt->DTn; break; case DT_nbytes: //printf("DT_nbytes %d\n", dt->DTnbytes); datasize += dt->DTnbytes; break; case DT_azeros: /* A block of zeros */ //printf("DT_azeros %d\n", dt->DTazeros); case_azeros: datasize += dt->DTazeros; if (dt == dtstart && !dt->DTnext && s->Sclass != SCcomdat && (s->Sseg == UNKNOWN || s->Sseg <= UDATA)) { /* first and only, so put in BSS segment */ switch (ty & mTYLINK) { #if TARGET_SEGMENTED case mTYfar: // if far data s->Sseg = objmod->fardata(s->Sident,datasize,&s->Soffset); s->Sfl = FLfardata; break; case mTYcs: s->Sseg = codeseg; Offset(codeseg) = _align(datasize,Offset(codeseg)); s->Soffset = Offset(codeseg); Offset(codeseg) += datasize; s->Sfl = FLcsdata; break; #endif case mTYthreadData: assert(config.objfmt == OBJ_MACH && I64); case mTYthread: { seg_data *pseg = objmod->tlsseg_bss(); s->Sseg = pseg->SDseg; objmod->data_start(s, datasize, pseg->SDseg); if (config.objfmt == OBJ_OMF) pseg->SDoffset += datasize; else objmod->lidata(pseg->SDseg, pseg->SDoffset, datasize); s->Sfl = FLtlsdata; break; } default: s->Sseg = UDATA; objmod->data_start(s,datasize,UDATA); objmod->lidata(s->Sseg,s->Soffset,datasize); s->Sfl = FLudata; // uninitialized data break; } assert(s->Sseg && s->Sseg != UNKNOWN); if (s->Sclass == SCglobal || (s->Sclass == SCstatic && config.objfmt != OBJ_OMF)) // if a pubdef to be done objmod->pubdefsize(s->Sseg,s,s->Soffset,datasize); // do the definition searchfixlist(s); if (config.fulltypes && !(s->Sclass == SCstatic && funcsym_p)) // not local static cv_outsym(s); #if SCPP out_extdef(s); #endif goto Lret; } break; case DT_common: assert(!dt->DTnext); outcommon(s,dt->DTazeros); goto Lret; case DT_xoff: { symbol *sb = dt->DTsym; if (tyfunc(sb->ty())) #if SCPP nwc_mustwrite(sb); #else ; #endif else if (sb->Sdt) // if initializer for symbol { if (!s->Sseg) s->Sseg = DATA; outdata(sb); // write out data for symbol } } case DT_coff: datasize += size(dt->Dty); break; default: #ifdef DEBUG dbg_printf("dt = %p, dt = %d\n",dt,dt->dt); #endif assert(0); } }
type *TypeEnum::toCtype() { if (ctype) return ctype; //printf("TypeEnum::toCtype() '%s'\n", sym->toChars()); type *t; Type *tm = mutableOf(); if (tm->ctype && tybasic(tm->ctype->Tty) == TYenum) { Symbol *s = tm->ctype->Ttag; assert(s); t = type_alloc(TYenum); t->Ttag = (Classsym *)s; // enum tag name t->Tcount++; t->Tnext = tm->ctype->Tnext; t->Tnext->Tcount++; // Add modifiers switch (mod) { case 0: assert(0); break; case MODconst: case MODwild: t->Tty |= mTYconst; break; case MODimmutable: t->Tty |= mTYimmutable; break; case MODshared: t->Tty |= mTYshared; break; case MODshared | MODwild: case MODshared | MODconst: t->Tty |= mTYshared | mTYconst; break; default: assert(0); } ctype = t; } else if (sym->memtype && sym->memtype->toBasetype()->ty == Tint32) { t = type_enum(sym->toPrettyChars(), sym->memtype->toCtype()); tm->ctype = t; ctype = t; } else if(sym->memtype) { t = ctype = sym->memtype->toCtype(); } else { t = ctype = type_alloc(Tint32); } if (0 && global.params.symdebug) sym->toDebug(); //printf("t = %p, Tflags = x%x\n", t, t->Tflags); return t; }
/************************************* * 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); } }
type *TypeEnum::toCtype() { if (ctype) return ctype; //printf("TypeEnum::toCtype() '%s'\n", sym->toChars()); type *t; Type *tm = mutableOf(); if (tm->ctype && tybasic(tm->ctype->Tty) == TYenum) { Symbol *s = tm->ctype->Ttag; assert(s); t = type_alloc(TYenum); t->Ttag = (Classsym *)s; // enum tag name t->Tcount++; t->Tnext = tm->ctype->Tnext; t->Tnext->Tcount++; // Add modifiers switch (mod) { case 0: assert(0); break; case MODconst: case MODwild: t->Tty |= mTYconst; break; case MODimmutable: t->Tty |= mTYimmutable; break; case MODshared: t->Tty |= mTYshared; break; case MODshared | MODwild: case MODshared | MODconst: t->Tty |= mTYshared | mTYconst; break; default: assert(0); } ctype = t; } else if (sym->memtype->toBasetype()->ty == Tint32) { Symbol *s = symbol_calloc(sym->toPrettyChars()); s->Sclass = SCenum; s->Senum = (enum_t *) MEM_PH_CALLOC(sizeof(enum_t)); s->Senum->SEflags |= SENforward; // forward reference slist_add(s); t = type_alloc(TYenum); t->Ttag = (Classsym *)s; // enum tag name t->Tcount++; t->Tnext = sym->memtype->toCtype(); t->Tnext->Tcount++; s->Stype = t; slist_add(s); tm->ctype = t; ctype = t; } else { t = ctype = sym->memtype->toCtype(); } //printf("t = %p, Tflags = x%x\n", t, t->Tflags); return t; }
targ_size_t type_size(type *t) { targ_size_t s; unsigned long u; tym_t tyb; type_debug(t); tyb = tybasic(t->Tty); #ifdef DEBUG if (tyb >= TYMAX) /*type_print(t),*/ dbg_printf("tyb = x%lx\n",(long)tyb); #endif assert(tyb < TYMAX); s = tysize[tyb]; if (s == (targ_size_t) -1) { switch (tyb) { // in case program plays games with function pointers #if TARGET_SEGMENTED case TYffunc: case TYfpfunc: case TYfsfunc: case TYf16func: #endif case TYhfunc: case TYnfunc: /* in case program plays games with function pointers */ case TYnpfunc: case TYnsfunc: case TYifunc: case TYjfunc: #if SCPP case TYmfunc: if (ANSI) synerr(EM_unknown_size,"function"); /* size of function is not known */ #endif s = 1; break; case TYarray: if (t->Tflags & TFsizeunknown) { #if SCPP synerr(EM_unknown_size,"array"); /* size of array is unknown */ #endif t->Tflags &= ~TFsizeunknown; } if (t->Tflags & TFvla) { s = tysize[pointertype]; break; } s = type_size(t->Tnext); u = t->Tdim * (unsigned long) s; #if TX86 && SCPP type_chksize(u); #endif s = u; break; case TYstruct: t = t->Ttag->Stype; /* find main instance */ /* (for const struct X) */ if (t->Tflags & TFsizeunknown) { #if SCPP template_instantiate_forward(t->Ttag); if (t->Tflags & TFsizeunknown) synerr(EM_unknown_size,t->Tty & TYstruct ? prettyident(t->Ttag) : "struct"); t->Tflags &= ~TFsizeunknown; #endif } assert(t->Ttag); s = t->Ttag->Sstruct->Sstructsize; break; #if SCPP case TYenum: if (t->Ttag->Senum->SEflags & SENforward) synerr(EM_unknown_size, prettyident(t->Ttag)); s = type_size(t->Tnext); break; #endif case TYvoid: #if SCPP && TARGET_WINDOS // GNUC allows it, so we will, too synerr(EM_void_novalue); // voids have no value #endif s = 1; break; #if SCPP case TYref: case TYmemptr: case TYvtshape: s = tysize(tym_conv(t)); break; case TYident: synerr(EM_unknown_size, t->Tident); s = 1; break; #endif #if MARS case TYref: s = tysize(TYnptr); break; #endif default: #ifdef DEBUG WRTYxx(t->Tty); #endif assert(0); } } return s; }
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); }