// Copyright (C) 1985-1998 by Symantec // Copyright (C) 2000-2012 by Digital Mars // All Rights Reserved // http://www.digitalmars.com // Written by Walter Bright /* * This source file is made available for personal use * only. The license is in /dmd/src/dmd/backendlicense.txt * or /dm/src/dmd/backendlicense.txt * For any other uses, please contact Digital Mars. */ #if !SPP #include #include #include #include #include "cc.h" #include "el.h" #include "oper.h" #include "code.h" #include "global.h" #include "type.h" static char __file__[] = __FILE__; /* for tassert.h */ #include "tassert.h" STATIC void el_weights(int bi,elem *e,unsigned weight); #ifndef __DMC__ #undef __cdecl #define __cdecl #endif static int __cdecl weight_compare(const void *e1,const void *e2); static int nretblocks; static vec_t regrange[REGMAX]; static int *weights; #define WEIGHTS(bi,si) weights[bi * globsym.top + si] /****************************************** */ void cgreg_init() { if (!(config.flags4 & CFG4optimized)) return; // Use calloc() instead because sometimes the alloc is too large //printf("1weights: dfotop = %d, globsym.top = %d\n", dfotop, globsym.top); weights = (int *) calloc(1,dfotop * globsym.top * sizeof(weights[0])); assert(weights); nretblocks = 0; for (int bi = 0; bi < dfotop; bi++) { block *b = dfo[bi]; if (b->BC == BCret || b->BC == BCretexp) nretblocks++; if (b->Belem) { //printf("b->Bweight = x%x\n",b->Bweight); el_weights(bi,b->Belem,b->Bweight); } } memset(regrange,0,sizeof(regrange)); // Make adjustments to symbols we might stick in registers for (size_t i = 0; i < globsym.top; i++) { unsigned sz; symbol *s = globsym.tab[i]; //printf("candidate '%s' for register\n",s->Sident); if (s->Srange) s->Srange = vec_realloc(s->Srange,dfotop); // Determine symbols that are not candidates if (!(s->Sflags & GTregcand) || !s->Srange || (sz = type_size(s->Stype)) == 0 || (tysize(s->ty()) == -1) || (I16 && sz > REGSIZE) || (tyfloating(s->ty()) && !(config.fpxmmregs && tyxmmreg(s->ty()))) ) { #ifdef DEBUG if (debugr) printf("not considering variable '%s' for register\n",s->Sident); #endif s->Sflags &= ~GTregcand; continue; } switch (s->Sclass) { case SCparameter: case SCfastpar: // Do not put parameters in registers if they are not used // more than twice (otherwise we have a net loss). if (s->Sweight <= 2 && !tyxmmreg(s->ty())) { #ifdef DEBUG if (debugr) printf("parameter '%s' weight %d is not enough\n",s->Sident,s->Sweight); #endif s->Sflags &= ~GTregcand; continue; } break; } if (sz == 1) s->Sflags |= GTbyte; if (!s->Slvreg) s->Slvreg = vec_calloc(dfotop); //printf("dfotop = %d, numbits = %d\n",dfotop,vec_numbits(s->Srange)); assert(vec_numbits(s->Srange) == dfotop); } } /****************************************** */ void cgreg_term() { if (config.flags4 & CFG4optimized) { for (size_t i = 0; i < globsym.top; i++) { Symbol *s = globsym.tab[i]; vec_free(s->Srange); vec_free(s->Slvreg); s->Srange = NULL; s->Slvreg = NULL; } for (size_t i = 0; i < arraysize(regrange); i++) { if (regrange[i]) { vec_free(regrange[i]); regrange[i] = NULL; } } free(weights); weights = NULL; } } /********************************* */ void cgreg_reset() { for (size_t j = 0; j < arraysize(regrange); j++) if (!regrange[j]) regrange[j] = vec_calloc(dfotop); else vec_clear(regrange[j]); } /******************************* * Registers used in block bi. */ void cgreg_used(unsigned bi,regm_t used) { for (size_t j = 0; used; j++) { if (used & 1) // if register j is used vec_setbit(bi,regrange[j]); used >>= 1; } } /************************* * Run through a tree calculating symbol weights. */ STATIC void el_weights(int bi,elem *e,unsigned weight) { while (1) { elem_debug(e); int op = e->Eoper; if (!OTleaf(op)) { // This prevents variable references within common subexpressions // from adding to the variable's usage count. if (e->Ecount) { if (e->Ecomsub) weight = 0; else e->Ecomsub = 1; } if (OTbinary(op)) { el_weights(bi,e->E2,weight); if ((OTopeq(op) || OTpost(op)) && e->E1->Eoper == OPvar) { if (weight >= 10) weight += 10; else weight++; } } e = e->E1; } else { switch (op) { case OPvar: Symbol *s = e->EV.sp.Vsym; if (s->Ssymnum != -1 && s->Sflags & GTregcand) { s->Sweight += weight; //printf("adding %d weight to '%s' (block %d, Ssymnum %d), giving Sweight %d\n",weight,s->Sident,bi,s->Ssymnum,s->Sweight); if (weights) WEIGHTS(bi,s->Ssymnum) += weight; } break; } return; } } } /***************************************** * Determine 'benefit' of assigning symbol s to register reg. * Benefit is roughly the number of clocks saved. * A negative value means that s cannot or should not be assigned to reg. */ int cgreg_benefit(Symbol *s,int reg, Symbol *retsym) { int benefit; int benefit2; block *b; int bi; int gotoepilog; int retsym_cnt; //printf("cgreg_benefit(s = '%s', reg = %d)\n", s->Sident, reg); vec_sub(s->Slvreg,s->Srange,regrange[reg]); int si = s->Ssymnum; Lagain: //printf("again\n"); benefit = 0; retsym_cnt = 0; // Make sure we have enough uses to justify // using a register we must save if (fregsaved & mask[reg] & mfuncreg) benefit -= 1 + nretblocks; foreach (bi,dfotop,s->Srange) { int inoutp; int inout; b = dfo[bi]; switch (b->BC) { case BCjcatch: case BCcatch: case BC_except: case BC_finally: case BC_ret: s->Sflags &= ~GTregcand; goto Lcant; // can't assign to register } if (vec_testbit(bi,s->Slvreg)) { benefit += WEIGHTS(bi,si); //printf("WEIGHTS(%d,%d) = %d, benefit = %d\n",bi,si,WEIGHTS(bi,si),benefit); inout = 1; if (s == retsym && (reg == AX || reg == XMM0) && b->BC == BCretexp) { benefit += 1; retsym_cnt++; //printf("retsym, benefit = %d\n",benefit); if (s->Sfl == FLreg && !vec_disjoint(s->Srange,regrange[reg])) goto Lcant; // don't spill if already in register } } else inout = -1; // Look at predecessors to see if we need to load in/out of register gotoepilog = 0; L2: inoutp = 0; benefit2 = 0; for (list_t bl = b->Bpred; bl; bl = list_next(bl)) { block *bp = list_block(bl); int bpi = bp->Bdfoidx; if (!vec_testbit(bpi,s->Srange)) continue; if (gotoepilog && bp->BC == BCgoto) { if (vec_testbit(bpi,s->Slvreg)) { if (inout == -1) benefit2 -= bp->Bweight; // need to mov into mem } else { if (inout == 1) benefit2 -= bp->Bweight; // need to mov into reg } } else if (vec_testbit(bpi,s->Slvreg)) { switch (inoutp) { case 0: inoutp = 1; if (inout != 1) { if (gotoepilog) { vec_clearbit(bpi,s->Slvreg); goto Lagain; } benefit2 -= b->Bweight; // need to mov into mem } break; case 1: break; case -1: if (gotoepilog == 0) { gotoepilog = 1; goto L2; } vec_clearbit(bpi,s->Slvreg); goto Lagain; } } else { switch (inoutp) { case 0: inoutp = -1; if (inout != -1) { if (gotoepilog) { vec_clearbit(bi,s->Slvreg); goto Lagain; } benefit2 -= b->Bweight; // need to mov into reg } break; case 1: if (gotoepilog == 0) { gotoepilog = 1; goto L2; } if (inout == 1) { vec_clearbit(bi,s->Slvreg); goto Lagain; } goto Lcant; case -1: break; } } } //printf("benefit2 = %d\n", benefit2); benefit += benefit2; } #ifdef DEBUG //printf("2weights: dfotop = %d, globsym.top = %d\n", dfotop, globsym.top); if (benefit > s->Sweight + retsym_cnt) printf("s = '%s', benefit = %d, Sweight = %d, retsym_cnt = x%x\n",s->Sident,benefit,s->Sweight, retsym_cnt); #endif assert(benefit <= s->Sweight + retsym_cnt); return benefit; Lcant: return -1; // can't assign to reg } /********************************************* * Determine if block gets symbol loaded by predecessor epilog (1), * or by prolog (0). */ int cgreg_gotoepilog(block *b,Symbol *s) { int bi = b->Bdfoidx; int inout; if (vec_testbit(bi,s->Slvreg)) inout = 1; else inout = -1; // Look at predecessors to see if we need to load in/out of register int gotoepilog = 0; int inoutp = 0; for (list_t bl = b->Bpred; bl; bl = list_next(bl)) { block *bp = list_block(bl); int bpi = bp->Bdfoidx; if (!vec_testbit(bpi,s->Srange)) continue; if (vec_testbit(bpi,s->Slvreg)) { switch (inoutp) { case 0: inoutp = 1; if (inout != 1) { if (gotoepilog) goto Lcant; } break; case 1: break; case -1: if (gotoepilog == 0) { gotoepilog = 1; goto Lret; } goto Lcant; } } else { switch (inoutp) { case 0: inoutp = -1; if (inout != -1) { if (gotoepilog) goto Lcant; } break; case 1: if (gotoepilog == 0) { gotoepilog = 1; goto Lret; } goto Lcant; case -1: break; } } } Lret: return gotoepilog; Lcant: assert(0); return -1; // can't assign to reg } /********************************** * Determine block prolog code - it's either * assignments to register, or storing register back in memory. */ void cgreg_spillreg_prolog(block *b,Symbol *s,code **pcstore,code **pcload) { list_t bl; code *cstore = *pcstore; code *cload = *pcload; int bi = b->Bdfoidx; //printf("cgreg_spillreg_prolog(block %d, s = '%s')\n",bi,s->Sident); int inoutp; if (vec_testbit(bi,s->Slvreg)) { inoutp = 1; // If it's startblock, and it's a spilled parameter, we // need to load it if (s->Sflags & SFLspill && bi == 0 && (s->Sclass == SCparameter || s->Sclass == SCfastpar)) { goto Lload; } } else inoutp = -1; if (cgreg_gotoepilog(b,s)) return; // Look at predecessors to see if we need to load in/out of register for (bl = b->Bpred; bl; bl = list_next(bl)) { { block *bp = list_block(bl); int bpi = bp->Bdfoidx; if (!vec_testbit(bpi,s->Srange)) continue; if (vec_testbit(bpi,s->Slvreg)) { if (inoutp != -1) continue; } else { if (inoutp != 1) continue; } } Lload: #ifdef DEBUG if (debugr) { int sz = type_size(s->Stype); if (inoutp == -1) printf("B%d: prolog moving %s into '%s'\n",bi,regstring[s->Sreglsw],s->Sident); else printf("B%d: prolog moving '%s' into %s:%s\n", bi, s->Sident, regstring[s->Sregmsw], sz > REGSIZE ? regstring[s->Sreglsw] : ""); } #endif code* c = gen_spill_reg(s, inoutp == 1); if (inoutp == -1) cstore = cat(cstore,c); else cload = cat(cload,c); break; } // Store old register values before loading in new ones *pcstore = cstore; *pcload = cload; } /********************************** * Determine block epilog code - it's either * assignments to register, or storing register back in memory. */ void cgreg_spillreg_epilog(block *b,Symbol *s,code **pcstore,code **pcload) { code *cstore = *pcstore; code *cload = *pcload; int bi = b->Bdfoidx; //printf("cgreg_spillreg_epilog(block %d, s = '%s')\n",bi,s->Sident); //assert(b->BC == BCgoto); if (!cgreg_gotoepilog(list_block(b->Bsucc),s)) return; int inoutp; if (vec_testbit(bi,s->Slvreg)) inoutp = 1; else inoutp = -1; // Look at successors to see if we need to load in/out of register for (list_t bl = b->Bsucc; bl; bl = list_next(bl)) { block *bp = list_block(bl); int bpi = bp->Bdfoidx; if (!vec_testbit(bpi,s->Srange)) continue; if (vec_testbit(bpi,s->Slvreg)) { if (inoutp != -1) continue; } else { if (inoutp != 1) continue; } #ifdef DEBUG if (debugr) { if (inoutp == 1) printf("B%d: epilog moving %s into '%s'\n",bi,regstring[s->Sreglsw],s->Sident); else printf("B%d: epilog moving '%s' into %s\n",bi,s->Sident,regstring[s->Sreglsw]); } #endif code* c = gen_spill_reg(s, inoutp == -1); if (inoutp == 1) cstore = cat(cstore,c); else cload = cat(cload,c); break; } // Store old register values before loading in new ones *pcstore = cstore; *pcload = cload; } /*************************** * Map symbol s into registers [NOREG,reglsw] or [regmsw, reglsw]. */ void cgreg_map(Symbol *s, unsigned regmsw, unsigned reglsw) { //assert(I64 || reglsw < 8); if (vec_disjoint(s->Srange,regrange[reglsw]) && (regmsw == NOREG || vec_disjoint(s->Srange,regrange[regmsw])) ) { s->Sfl = FLreg; vec_copy(s->Slvreg,s->Srange); } else { s->Sflags |= SFLspill; // Already computed by cgreg_benefit() //vec_sub(s->Slvreg,s->Srange,regrange[reglsw]); if (s->Sfl == FLreg) // if reassigned { switch (s->Sclass) { case SCauto: case SCregister: case SCtmp: case SCfastpar: s->Sfl = FLauto; break; case SCbprel: s->Sfl = FLbprel; break; case SCparameter: s->Sfl = FLpara; break; #if PSEUDO_REGS case SCpseudo: s->Sfl = FLpseudo; break; #endif case SCstack: s->Sfl = FLstack; break; default: #ifdef DEBUG symbol_print(s); #endif assert(0); } } } s->Sreglsw = reglsw; s->Sregm = mask[reglsw]; mfuncreg &= ~mask[reglsw]; if (regmsw != NOREG) vec_subass(s->Slvreg,regrange[regmsw]); vec_orass(regrange[reglsw],s->Slvreg); if (regmsw == NOREG) { #if DEBUG if (debugr) { printf("symbol '%s' %s in register %s\n ", s->Sident, (s->Sflags & SFLspill) ? "spilled" : "put", regstring[reglsw]); vec_println(s->Slvreg); } #endif } else { assert(regmsw < 8); s->Sregmsw = regmsw; s->Sregm |= mask[regmsw]; mfuncreg &= ~mask[regmsw]; vec_orass(regrange[regmsw],s->Slvreg); #if DEBUG if (debugr) printf("symbol '%s' %s in register pair %s\n", s->Sident, (s->Sflags & SFLspill) ? "spilled" : "put", regm_str(s->Sregm)); #endif } } /******************************************** * The register variables in this mask can not be in registers. * "Unregister" them. */ void cgreg_unregister(regm_t conflict) { if (pass == PASSfinal) pass = PASSreg; // have to codegen at least one more time for (int i = 0; i < globsym.top; i++) { symbol *s = globsym.tab[i]; if (s->Sfl == FLreg && s->Sregm & conflict) { s->Sflags |= GTunregister; } } } /****************************************** * Do register assignments. * Returns: * !=0 redo code generation * 0 no more register assignments */ struct Reg // data for trial register assignment { Symbol *sym; int reglsw; int regmsw; int benefit; }; int cgreg_assign(Symbol *retsym) { int flag = FALSE; // assume no changes /* First do any 'unregistering' which might have happened in the last * code gen pass. */ for (size_t si = 0; si < globsym.top; si++) { symbol *s = globsym.tab[si]; if (s->Sflags & GTunregister) { #if DEBUG if (debugr) { printf("symbol '%s' %s register %s\n ", s->Sident, (s->Sflags & SFLspill) ? "unspilled" : "unregistered", regstring[s->Sreglsw]); vec_println(s->Slvreg); } #endif flag = TRUE; s->Sflags &= ~(GTregcand | GTunregister | SFLspill); if (s->Sfl == FLreg) { switch (s->Sclass) { case SCauto: case SCregister: case SCtmp: case SCfastpar: s->Sfl = FLauto; break; case SCbprel: s->Sfl = FLbprel; break; case SCparameter: s->Sfl = FLpara; break; #if PSEUDO_REGS case SCpseudo: s->Sfl = FLpseudo; break; #endif case SCstack: s->Sfl = FLstack; break; default: #ifdef DEBUG symbol_print(s); #endif assert(0); } } } } vec_t v = vec_calloc(dfotop); // Find symbol t, which is the most 'deserving' symbol that should be // placed into a register. Reg t; t.sym = NULL; t.benefit = 0; for (size_t si = 0; si < globsym.top; si++) { symbol *s = globsym.tab[si]; Reg u; u.sym = s; if (!(s->Sflags & GTregcand) || s->Sflags & SFLspill || // Keep trying to reassign retsym into AX (s->Sfl == FLreg && !(s == retsym && s->Sregm != mAX && s->Sregm != mXMM0)) ) { #ifdef DEBUG if (debugr) if (s->Sfl == FLreg) printf("symbol '%s' is in reg %s\n",s->Sident,regm_str(s->Sregm)); else if (s->Sflags & SFLspill) printf("symbol '%s' spilled in reg %s\n",s->Sident,regm_str(s->Sregm)); else if (!(s->Sflags & GTregcand)) printf("symbol '%s' is not a reg candidate\n",s->Sident); else printf("symbol '%s' is not a candidate\n",s->Sident); #endif continue; } tym_t ty = s->ty(); unsigned sz = tysize(ty); #ifdef DEBUG if (debugr) { printf("symbol '%3s', ty x%x weight x%x sz %d\n ", s->Sident,ty,s->Sweight,(int)sz); vec_println(s->Srange); } #endif // Select sequence of registers to try to map s onto char *pseq; // sequence to try for LSW char *pseqmsw = NULL; // sequence to try for MSW, NULL if none if (tyxmmreg(ty)) { static char sequence[] = {XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6,XMM7,NOREG}; pseq = sequence; } else if (I64) { if (sz == REGSIZE * 2) { static char seqmsw[] = {CX,DX,NOREG}; static char seqlsw[] = {AX,BX,SI,DI,NOREG}; pseq = seqlsw; pseqmsw = seqmsw; } else { // R10 is reserved for the static link static char sequence[] = {AX,CX,DX,SI,DI,R8,R9,R11,BX,R12,R13,R14,R15,BP,NOREG}; pseq = sequence; } } else if (I32) { if (sz == REGSIZE * 2) { static char seqlsw[] = {AX,BX,SI,DI,NOREG}; static char seqmsw[] = {CX,DX,NOREG}; pseq = seqlsw; pseqmsw = seqmsw; } else { static char sequence[] = {AX,CX,DX,BX,SI,DI,BP,NOREG}; pseq = sequence; } } else { assert(I16); if (typtr(ty)) { // For pointer types, try to pick index register first static char seqidx[] = {BX,SI,DI,AX,CX,DX,BP,NOREG}; pseq = seqidx; } else { // Otherwise, try to pick index registers last static char sequence[] = {AX,CX,DX,BX,SI,DI,BP,NOREG}; pseq = sequence; } } u.benefit = 0; for (int i = 0; pseq[i] != NOREG; i++) { unsigned reg = pseq[i]; // Symbols used as return values should only be mapped into return value registers if (s == retsym && !(mask[reg] & (mAX | mXMM0))) continue; // If BP isn't available, can't assign to it if (reg == BP && !(allregs & mBP)) continue; #if 0 && TARGET_LINUX // Need EBX for static pointer if (reg == BX && !(allregs & mBX)) continue; #endif if (s->Sflags & GTbyte && !(mask[reg] & BYTEREGS)) continue; int benefit = cgreg_benefit(s,reg,retsym); #ifdef DEBUG if (debugr) { printf(" %s",regstring[reg]); vec_print(regrange[reg]); printf(" %d\n",benefit); } #endif if (benefit > u.benefit) { // successful assigning of lsw unsigned regmsw = NOREG; // Now assign MSW if (pseqmsw) { for (unsigned regj = 0; 1; regj++) { regmsw = pseqmsw[regj]; if (regmsw == NOREG) goto Ltried; // tried and failed to assign MSW if (regmsw == reg) // can't assign msw and lsw to same reg continue; #ifdef DEBUG if (debugr) { printf(".%s",regstring[regmsw]); vec_println(regrange[regmsw]); } #endif if (vec_disjoint(s->Slvreg,regrange[regmsw])) break; } } vec_copy(v,s->Slvreg); u.benefit = benefit; u.reglsw = reg; u.regmsw = regmsw; } Ltried: ; } if (u.benefit > t.benefit) { t = u; vec_copy(t.sym->Slvreg,v); } } if (t.sym && t.benefit > 0) { cgreg_map(t.sym,t.regmsw,t.reglsw); flag = TRUE; } /* See if any scratch registers have become available that we can use. * Scratch registers are cheaper, as they don't need save/restore. * All floating point registers are scratch registers, so no need * to do this for them. */ if ((I32 || I64) && // not worth the bother for 16 bit code !flag && // if haven't already assigned registers in this pass (mfuncreg & ~fregsaved) & ALLREGS && // if unused non-floating scratch registers !(funcsym_p->Sflags & SFLexit)) // don't need save/restore if function never returns { for (size_t si = 0; si < globsym.top; si++) { symbol *s = globsym.tab[si]; if (s->Sfl == FLreg && // if assigned to register mask[s->Sreglsw] & fregsaved && // and that register is not scratch type_size(s->Stype) <= REGSIZE && // don't bother with register pairs !tyfloating(s->ty())) // don't assign floating regs to non-floating regs { s->Sreglsw = findreg((mfuncreg & ~fregsaved) & ALLREGS); s->Sregm = mask[s->Sreglsw]; flag = TRUE; #ifdef DEBUG if (debugr) printf("re-assigned '%s' to %s\n",s->Sident,regstring[s->Sreglsw]); #endif break; } } } vec_free(v); return flag; } ////////////////////////////////////// // Qsort() comparison routine for array of pointers to Symbol's. static int __cdecl weight_compare(const void *e1,const void *e2) { Symbol **psp1; Symbol **psp2; psp1 = (Symbol **)e1; psp2 = (Symbol **)e2; return (*psp2)->Sweight - (*psp1)->Sweight; } #endif