static Node *slicebase(Simp *s, Node *n, Node *off) { Node *t, *u, *v; Type *ty; int sz; t = rval(s, n, NULL); u = NULL; ty = tybase(exprtype(n)); switch (ty->type) { case Typtr: u = t; break; case Tyarray: u = addr(s, t, base(exprtype(n))); break; case Tyslice: u = load(addr(s, t, mktyptr(n->line, base(exprtype(n))))); break; default: die("Unslicable type %s", tystr(n->expr.type)); } /* safe: all types we allow here have a sub[0] that we want to grab */ if (off) { off = ptrsized(s, rval(s, off, NULL)); sz = tysize(n->expr.type->sub[0]); v = mul(off, disp(n->line, sz)); return add(u, v); } else { return u; } }
targ_size_t size(tym_t ty) { int sz = (tybasic(ty) == TYvoid) ? 1 : tysize(ty); #ifdef DEBUG if (sz == -1) WRTYxx(ty); #endif assert(sz!= -1); return sz; }
size_t size(Node *n) { Type *t; if (n->type == Nexpr) t = n->expr.type; else t = n->decl.type; return tysize(t); }
bool xmmIsAligned(elem *e) { if (tyvector(e->Ety) && e->Eoper == OPvar) { Symbol *s = e->EV.sp.Vsym; if (s->Salignsize() < 16 || e->EV.sp.Voffset & (16 - 1) || tysize(e->Ety) > STACKALIGN ) return false; // definitely not aligned } return true; // assume aligned }
static Node *simpucon(Simp *s, Node *n, Node *dst) { Node *tmp, *u, *tag, *elt, *sz; Node *r; Type *ty; Ucon *uc; size_t i; /* find the ucon we're constructing here */ ty = tybase(n->expr.type); uc = NULL; for (i = 0; i < ty->nmemb; i++) { if (!strcmp(namestr(n->expr.args[0]), namestr(ty->udecls[i]->name))) { uc = ty->udecls[i]; break; } } if (!uc) die("Couldn't find union constructor"); if (dst) tmp = dst; else tmp = temp(s, n); /* Set the tag on the ucon */ u = addr(s, tmp, mktype(n->line, Tyuint)); tag = mkintlit(n->line, uc->id); tag->expr.type = mktype(n->line, Tyuint); append(s, set(deref(u), tag)); /* fill the value, if needed */ if (!uc->etype) return tmp; elt = rval(s, n->expr.args[1], NULL); u = addk(u, Wordsz); if (stacktype(uc->etype)) { elt = addr(s, elt, uc->etype); sz = disp(n->line, tysize(uc->etype)); r = mkexpr(n->line, Oblit, u, elt, sz, NULL); } else { r = set(deref(u), elt); } append(s, r); return tmp; }
unsigned xmmload(tym_t tym, bool aligned) { unsigned op; if (tysize(tym) == 32) aligned = false; switch (tybasic(tym)) { case TYuint: case TYint: case TYlong: case TYulong: op = LODD; break; // MOVD case TYfloat: case TYcfloat: case TYifloat: op = LODSS; break; // MOVSS case TYllong: case TYullong: op = LODQ; break; // MOVQ case TYdouble: case TYcdouble: case TYidouble: op = LODSD; break; // MOVSD case TYfloat8: case TYfloat4: op = aligned ? LODAPS : LODUPS; break; // MOVAPS / MOVUPS case TYdouble4: case TYdouble2: op = aligned ? LODAPD : LODUPD; break; // MOVAPD / MOVUPD 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 = aligned ? LODDQA : LODDQU; break; // MOVDQA / MOVDQU default: printf("tym = x%x\n", tym); assert(0); } return op; }
static Node *intconvert(Simp *s, Node *from, Type *to, int issigned) { Node *r; size_t fromsz, tosz; fromsz = size(from); tosz = tysize(to); r = rval(s, from, NULL); if (fromsz > tosz) { r = mkexpr(from->line, Otrunc, r, NULL); } else if (tosz > fromsz) { if (issigned) r = mkexpr(from->line, Oswiden, r, NULL); else r = mkexpr(from->line, Ozwiden, r, NULL); } r->expr.type = to; return r; }
static Node *idxaddr(Simp *s, Node *seq, Node *idx) { Node *a, *t, *u, *v; /* temps */ Node *r; /* result */ Type *ty; size_t sz; a = rval(s, seq, NULL); ty = exprtype(seq)->sub[0]; if (exprtype(seq)->type == Tyarray) t = addr(s, a, ty); else if (seq->expr.type->type == Tyslice) t = load(addr(s, a, mktyptr(seq->line, ty))); else die("Can't index type %s\n", tystr(seq->expr.type)); assert(t->expr.type->type == Typtr); u = rval(s, idx, NULL); u = ptrsized(s, u); sz = tysize(ty); v = mul(u, disp(seq->line, sz)); r = add(t, v); return r; }
static void sliceStructs_Replace(SymInfo *sia, elem *e) { while (1) { switch (e->Eoper) { case OPvar: { Symbol *s = e->EV.sp.Vsym; SYMIDX si = s->Ssymnum; //printf("e: %d %d\n", si, sia[si].canSlice); //elem_print(e); if (si >= 0 && sia[si].canSlice) { if (tysize(e->Ety) == 2 * REGSIZE) { // Rewrite e as (si0 OPpair si0+1) elem *e1 = el_calloc(); el_copy(e1, e); e1->Ety = sia[si].ty0; elem *e2 = el_calloc(); el_copy(e2, e); Symbol *s1 = globsym.tab[sia[si].si0 + 1]; // +1 for second slice e2->Ety = sia[si].ty1; e2->EV.sp.Vsym = s1; e2->Eoffset = 0; e->Eoper = OPpair; e->E1 = e1; e->E2 = e2; } else if (e->Eoffset == 0) // the first slice of the symbol is the same as the original { } else { Symbol *s1 = globsym.tab[sia[si].si0 + 1]; // +1 for second slice e->EV.sp.Vsym = s1; e->Eoffset = 0; //printf("replaced with:\n"); //elem_print(e); } } return; } default: if (OTunary(e->Eoper)) { e = e->E1; break; } if (OTbinary(e->Eoper)) { sliceStructs_Replace(sia, e->E2); e = e->E1; break; } return; } } }
size_t tysize(Type *t) { size_t sz; size_t i; sz = 0; if (!t) die("size of empty type => bailing."); switch (t->type) { case Tyvoid: die("void has no size"); return 1; case Tybool: case Tyint8: case Tybyte: case Tyuint8: return 1; case Tyint16: case Tyuint16: return 2; case Tyint: case Tyint32: case Tyuint: case Tyuint32: case Tychar: /* utf32 */ return 4; case Typtr: case Tyfunc: case Tyvalist: /* ptr to first element of valist */ return Ptrsz; case Tyint64: case Tylong: case Tyuint64: case Tyulong: return 8; /*end integer types*/ case Tyfloat32: return 4; case Tyfloat64: return 8; case Tyslice: return 2*Ptrsz; /* len; ptr */ case Tyname: return tysize(t->sub[0]); case Tyarray: t->asize = fold(t->asize, 1); assert(exprop(t->asize) == Olit); return t->asize->expr.args[0]->lit.intval * tysize(t->sub[0]); case Tytuple: for (i = 0; i < t->nsub; i++) { sz = tyalign(sz, tysize(t->sub[i])); sz += tysize(t->sub[i]); } for (i = 0; i < t->nsub; i++) sz = tyalign(sz, tysize(t->sub[i])); return sz; break; case Tystruct: for (i = 0; i < t->nmemb; i++) { sz = tyalign(sz, size(t->sdecls[i])); sz += size(t->sdecls[i]); } /* the whole struct size should match the biggest alignment */ for (i = 0; i < t->nmemb; i++) sz = tyalign(sz, size(t->sdecls[i])); return sz; break; case Tyunion: sz = Wordsz; for (i = 0; i < t->nmemb; i++) if (t->udecls[i]->etype) sz = max(sz, tysize(t->udecls[i]->etype) + Wordsz); return align(sz, Ptrsz); break; case Tybad: case Tyvar: case Typaram: case Tyunres: case Ntypes: die("Type %s does not have size; why did it get down to here?", tystr(t)); break; } return -1; }
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",tyb); #endif assert(tyb < TYMAX); s = tysize[tyb]; if (s == (targ_size_t) -1) { switch (tyb) { // in case program plays games with function pointers case TYffunc: case TYfpfunc: #if TX86 case TYnfunc: /* in case program plays games with function pointers */ case TYhfunc: case TYnpfunc: case TYnsfunc: case TYfsfunc: case TYf16func: case TYifunc: case TYjfunc: #endif #if SCPP 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 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; } 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->tdata()[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) { if (global.params.is64bit) { // Order of assignment of pointer or integer parameters static const unsigned char argregs[6] = { DI,SI,DX,CX,R8,R9 }; int r = 0; int xmmcnt = XMM0; 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 < sizeof(argregs)/sizeof(argregs[0])) { if (type_jparam(sp->Stype)) { sp->Sclass = SCfastpar; sp->Spreg = argregs[r]; sp->Sfl = FLauto; ++r; } } if (xmmcnt <= XMM7) { if (tyfloating(ty) && tysize(ty) <= 8) { sp->Sclass = SCfastpar; sp->Spreg = xmmcnt; sp->Sfl = FLauto; ++xmmcnt; } } } } else { // First parameter goes in register Symbol *sp = params[0]; if ((tyf == TYjfunc || tyf == TYmfunc) && type_jparam(sp->Stype)) { sp->Sclass = SCfastpar; sp->Spreg = (tyf == TYjfunc) ? AX : CX; sp->Sfl = FLauto; //printf("'%s' is SCfastpar\n",sp->Sident); } } } if (func->fbody) { block *b; Blockx bx; Statement *sbody; localgot = NULL; sbody = func->fbody; memset(&bx,0,sizeof(bx)); bx.startblock = block_calloc(); bx.curblock = bx.startblock; bx.funcsym = s; bx.scope_index = -1; bx.classdec = cd; bx.member = func; bx.module = getModule(); irs.blx = &bx; #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 { elem *e = el_una(OPucall, TYvoid, el_var(s)); esharedctor = el_combine(esharedctor, e); } else #endif if (isStaticCtorDeclaration()) { elem *e = el_una(OPucall, TYvoid, el_var(s)); ector = el_combine(ector, e); } // If static destructor #if DMDV2 if (isSharedStaticDtorDeclaration()) // must come first because it derives from StaticDtorDeclaration { elem *e; #if STATICCTOR e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_FATEXIT]), el_ptr(s)); esharedctor = el_combine(esharedctor, e); shareddtorcount++; #else SharedStaticDtorDeclaration *f = isSharedStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ esharedctorgates.push(f); } e = el_una(OPucall, TYvoid, el_var(s)); eshareddtor = el_combine(e, eshareddtor); #endif } else #endif if (isStaticDtorDeclaration()) { elem *e; #if STATICCTOR e = el_bin(OPcall, TYvoid, el_var(rtlsym[RTLSYM_FATEXIT]), el_ptr(s)); ector = el_combine(ector, e); dtorcount++; #else StaticDtorDeclaration *f = isStaticDtorDeclaration(); assert(f); if (f->vgate) { /* Increment destructor's vgate at construction time */ ectorgates.push(f); } e = el_una(OPucall, TYvoid, el_var(s)); edtor = el_combine(e, edtor); #endif } // If unit test if (isUnitTestDeclaration()) { elem *e = el_una(OPucall, TYvoid, el_var(s)); etest = el_combine(etest, e); } if (global.errors) return; writefunc(s); if (isExport()) obj_export(s, Poffset); for (size_t i = 0; i < irs.deferToObj->dim; i++) { Dsymbol *s = irs.deferToObj->tdata()[i]; s->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 }
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 = tysize(TYldouble); 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; }
code *orthxmm(elem *e, regm_t *pretregs) { //printf("orthxmm(e = %p, *pretregs = %s)\n", e, regm_str(*pretregs)); elem *e1 = e->E1; elem *e2 = e->E2; // float + ifloat is not actually addition if ((e->Eoper == OPadd || e->Eoper == OPmin) && ((tyreal(e1->Ety) && tyimaginary(e2->Ety)) || (tyreal(e2->Ety) && tyimaginary(e1->Ety)))) { regm_t retregs = *pretregs & XMMREGS; if (!retregs) retregs = XMMREGS; unsigned reg; regm_t rretregs; unsigned rreg; if (tyreal(e1->Ety)) { reg = findreg(retregs); rreg = findreg(retregs & ~mask[reg]); retregs = mask[reg]; rretregs = mask[rreg]; } else { // Pick the second register, not the first rreg = findreg(retregs); rretregs = mask[rreg]; reg = findreg(retregs & ~rretregs); retregs = mask[reg]; } assert(retregs && rretregs); CodeBuilder cdb; cdb.append(codelem(e1,&retregs,FALSE)); // eval left leaf cdb.append(scodelem(e2, &rretregs, retregs, TRUE)); // eval right leaf retregs |= rretregs; if (e->Eoper == OPmin) { unsigned nretregs = XMMREGS & ~retregs; unsigned sreg; // hold sign bit unsigned sz = tysize(e1->Ety); cdb.append(allocreg(&nretregs,&sreg,e2->Ety)); targ_size_t signbit = 0x80000000; if (sz == 8) signbit = 0x8000000000000000LL; cdb.append(movxmmconst(sreg, sz, signbit, 0)); cdb.append(getregs(nretregs)); unsigned xop = (sz == 8) ? XORPD : XORPS; // XORPD/S rreg,sreg cdb.gen2(xop,modregxrmx(3,rreg-XMM0,sreg-XMM0)); } if (retregs != *pretregs) cdb.append(fixresult(e,retregs,pretregs)); return cdb.finish(); } regm_t retregs = *pretregs & XMMREGS; if (!retregs) retregs = XMMREGS; CodeBuilder cdb; cdb.append(codelem(e1,&retregs,FALSE)); // eval left leaf unsigned reg = findreg(retregs); regm_t rretregs = XMMREGS & ~retregs; cdb.append(scodelem(e2, &rretregs, retregs, TRUE)); // eval right leaf unsigned rreg = findreg(rretregs); unsigned op = xmmoperator(e1->Ety, e->Eoper); /* We should take advantage of mem addressing modes for OP XMM,MEM * but we do not at the moment. */ if (OTrel(e->Eoper)) { retregs = mPSW; cdb.gen2(op,modregxrmx(3,rreg-XMM0,reg-XMM0)); checkSetVex(cdb.last(), e1->Ety); return cdb.finish(); } else cdb.append(getregs(retregs)); cdb.gen2(op,modregxrmx(3,reg-XMM0,rreg-XMM0)); checkSetVex(cdb.last(), e1->Ety); if (retregs != *pretregs) cdb.append(fixresult(e,retregs,pretregs)); 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 checkSetVex(code *c, tym_t ty) { if (config.avx || tysize(ty) == 32) { unsigned vreg = (c->Irm >> 3) & 7; if (c->Irex & REX_R) vreg |= 8; int VEX_L = 0; // TODO: This is too simplistic, depending on the instruction, vex.vvvv // encodes NDS, NDD, DDS, or no operand (NOO). The code below assumes // NDS (non-destructive source), except for the incomplete list of 2 // operand instructions (NOO) handled by the switch. switch (c->Iop) { case LODSS: case LODSD: case STOSS: case STOSD: if ((c->Irm & 0xC0) == 0xC0) break; case LODAPS: case LODUPS: case LODAPD: case LODUPD: case LODDQA: case LODDQU: case LODD: case LODQ: case STOAPS: case STOUPS: case STOAPD: case STOUPD: case STODQA: case STODQU: case STOD: case STOQ: case COMISS: case COMISD: case UCOMISS: case UCOMISD: case MOVDDUP: case MOVSHDUP: case MOVSLDUP: case VBROADCASTSS: case PSHUFD: case PSHUFHW: case PSHUFLW: vreg = 0; // for 2 operand vex instructions break; case VBROADCASTSD: case VBROADCASTF128: VEX_L = 1; vreg = 0; // for 2 operand vex instructions break; } unsigned op = 0xC4000000 | (c->Iop & 0xFF); switch (c->Iop & 0xFFFFFF00) { #define MM_PP(mm, pp) ((mm << 16) | (pp << 8)) case 0x00000F00: op |= MM_PP(1,0); break; case 0x00660F00: op |= MM_PP(1,1); break; case 0x00F30F00: op |= MM_PP(1,2); break; case 0x00F20F00: op |= MM_PP(1,3); break; case 0x660F3800: op |= MM_PP(2,1); break; case 0x660F3A00: op |= MM_PP(3,1); break; default: printf("Iop = %x\n", c->Iop); assert(0); #undef MM_PP } c->Iop = op; c->Ivex.pfx = 0xC4; c->Ivex.r = !(c->Irex & REX_R); c->Ivex.x = !(c->Irex & REX_X); c->Ivex.b = !(c->Irex & REX_B); c->Ivex.w = (c->Irex & REX_W) != 0; c->Ivex.l = (tysize(ty) == 32) | VEX_L; c->Ivex.vvvv = ~vreg; c->Iflags |= CFvex; checkSetVex3(c); }
/*************** * 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(); }
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; } } }
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) { // Rewrite as OPpair later sia[si].usePair = true; /* OPpair cannot handle XMM registers, cdpair() and fixresult() */ if (tyfloating(sia[si].ty0) || tyfloating(sia[si].ty1)) sia[si].canSlice = false; } 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); // Cannot slice float fields if the symbol is also accessed using OPpair (see above) if (sia[si].usePair && (tyfloating(sia[si].ty0) || tyfloating(sia[si].ty1))) sia[si].canSlice = false; } 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; } } }