// Copyright (C) 1985-1998 by Symantec
// Copyright (C) 2000-2010 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        <stdio.h>
#include        <string.h>
#include        <stdlib.h>
#include        <time.h>
#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()
{   block *b;
    int bi;

    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 (bi = 0; bi < dfotop; bi++)
    {   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 (int i = 0; i < globsym.top; i++)
    {   unsigned sz;
        symbol *s = globsym.tab[i];

        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) ||
            ((I32 || I64) && tyfloating(s->ty()))
           )
        {
            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)
                {
                    #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()
{   int i;
    Symbol *s;

    if (config.flags4 & CFG4optimized)
    {
        for (i = 0; i < globsym.top; i++)
        {
            s = globsym.tab[i];
            vec_free(s->Srange);
            vec_free(s->Slvreg);
            s->Srange = NULL;
            s->Slvreg = NULL;
        }

        for (i = 0; i < arraysize(regrange); i++)
            if (regrange[i])
            {   vec_free(regrange[i]);
                    regrange[i] = NULL;
            }

        free(weights);
        weights = NULL;
    }
}

/*********************************
 */

void cgreg_reset()
{   unsigned j;

    for (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)
{   int j;

    for (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)
{   int op;
    Symbol *s;

    while (1)
    {   elem_debug(e);

        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:
                    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;
    list_t bl;
    int bi;
    int si;
    int gotoepilog;
    int retsym_cnt;

    //printf("cgreg_benefit(s = '%s', reg = %d)\n", s->Sident, reg);

    vec_sub(s->Slvreg,s->Srange,regrange[reg]);
    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 && 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 (bl = b->Bpred; bl; bl = list_next(bl))
        {   block *bp;
            int bpi;

            bp = list_block(bl);
            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)
{
    list_t bl;
    int bi;
    int si;
    int gotoepilog;
    int inoutp;
    int inout;

    bi = b->Bdfoidx;
    si = s->Ssymnum;

    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
    gotoepilog = 0;
    inoutp = 0;
    for (bl = b->Bpred; bl; bl = list_next(bl))
    {   block *bp;
        int bpi;

        bp = list_block(bl);
        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 *cload;
    code *cstore;
    code *c;
    code cs;
    int inoutp;
    int sz;
    elem *e;
    regm_t keepmsk;
    int bi;

    e = NULL;
    cstore = *pcstore;
    cload = *pcload;
    bi = b->Bdfoidx;
    sz = type_size(s->Stype);

    //printf("cgreg_spillreg_prolog(block %d, s = '%s')\n",bi,s->Sident);

    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;
        int bpi;

        bp = list_block(bl);
        bpi = bp->Bdfoidx;
        if (!vec_testbit(bpi,s->Srange))
            continue;
//      if (bp->BC == BCgoto)
//          continue;                   // already taken care of
        if (vec_testbit(bpi,s->Slvreg))
        {
            if (inoutp == -1)
            {   // MOV mem[ESP],reg
                cs.Iop = 0x89;
                keepmsk = RMstore;
                #ifdef DEBUG
                if (debugr)
                    printf("B%d: prolog moving %s into '%s'\n",bi,regstring[s->Sreglsw],s->Sident);
                #endif
            }
            else
                continue;
        }
        else
        {
            if (inoutp == 1)
            {
        Lload:
                // MOV reg,mem[ESP]
                cs.Iop = 0x8B;
                keepmsk = RMload;
                #ifdef DEBUG
                if (debugr)
                {   if (sz > REGSIZE)
                        printf("B%d: prolog moving '%s' into %s:%s\n",bi,s->Sident,regstring[s->Sregmsw],regstring[s->Sreglsw]);
                    else
                        printf("B%d: prolog moving '%s' into %s\n",bi,s->Sident,regstring[s->Sreglsw]);
                }
                #endif
            }
            else
                continue;
        }
        if (!e)
            e = el_var(s);              // so we can trick getlvalue() into
                                        // working for us
        cs.Iop ^= (sz == 1);
        c = getlvalue(&cs,e,keepmsk);
        cs.orReg(s->Sreglsw);
        c = gen(c,&cs);
        if (sz > REGSIZE)
        {
            cs.setReg(s->Sregmsw);
            getlvalue_msw(&cs);
            c = gen(c,&cs);
        }
        if (inoutp == -1)
            cstore = cat(cstore,c);
        else
            cload = cat(cload,c);
        break;
    }
    el_free(e);

    // 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)
{
    list_t bl;
    code *cload;
    code *cstore;
    code *c;
    code cs;
    int inoutp;
    int sz;
    elem *e;
    regm_t keepmsk;
    int bi;

    e = NULL;
    cstore = *pcstore;
    cload = *pcload;
    bi = b->Bdfoidx;
    sz = type_size(s->Stype);

    //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;

    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 (bl = b->Bsucc; bl; bl = list_next(bl))
    {   block *bp;
        int bpi;

        bp = list_block(bl);
        bpi = bp->Bdfoidx;
        if (!vec_testbit(bpi,s->Srange))
            continue;
        if (vec_testbit(bpi,s->Slvreg))
        {
            if (inoutp == -1)
            {
                // MOV reg,mem[ESP]
                cs.Iop = 0x8B;
                keepmsk = RMload;
                #ifdef DEBUG
                if (debugr)
                    printf("B%d: epilog moving '%s' into %s\n",bi,s->Sident,regstring[s->Sreglsw]);
                #endif
            }
            else
                continue;
        }
        else
        {
            if (inoutp == 1)
            {   // MOV mem[ESP],reg
                cs.Iop = 0x89;
                keepmsk = RMstore;
                #ifdef DEBUG
                if (debugr)
                    printf("B%d: epilog moving %s into '%s'\n",bi,regstring[s->Sreglsw],s->Sident);
                #endif
            }
            else
                continue;
        }
        if (!e)
            e = el_var(s);              // so we can trick getlvalue() into
                                        // working for us
        cs.Iop ^= (sz == 1);
        c = getlvalue(&cs,e,keepmsk);
        cs.orReg(s->Sreglsw);
        c = gen(c,&cs);
        if (sz > REGSIZE)
        {
            cs.setReg(s->Sregmsw);
            getlvalue_msw(&cs);
            c = gen(c,&cs);
        }
        if (inoutp == 1)
            cstore = cat(cstore,c);
        else
            cload = cat(cload,c);
        break;
    }
    el_free(e);

    // 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)
{
    Reg t;
    vec_t v;

    int si;
    int flag = FALSE;

    /* First do any 'unregistering' which might have happened in the last
     * code gen pass.
     */
    for (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);
                }
            }
        }
    }

    v = vec_calloc(dfotop);

    // Find symbol t, which is the most 'deserving' symbol that should be
    // placed into a register.
    t.sym = NULL;
    t.benefit = 0;
    for (si = 0; si < globsym.top; si++)
    {   symbol *s = globsym.tab[si];
        Reg u;
        unsigned reg;
        tym_t ty;
        unsigned sz;


        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))
           )
        {
            #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
                printf("symbol '%s' is not a candidate\n",s->Sident);
            #endif
            continue;
        }

        char *pseq;
        char *pseqmsw = NULL;

        ty = s->ty();
        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

        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++)
        {   int benefit;

            reg = pseq[i];

            if (reg != AX && s == retsym)
                continue;
            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;

            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 in MSW
                if (sz > REGSIZE && sz <= 2 * REGSIZE)
                {   unsigned regj;

                    for (regj = 0; 1; regj++)
                    {
                        regmsw = pseqmsw[regj];
                        if (regmsw == NOREG)
                            goto Ltried;
                        if (regmsw == 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 registers have become available that we can use.
    if ((I32 || I64) && !flag && (mfuncreg & ~fregsaved) & ALLREGS &&
        !(funcsym_p->Sflags & SFLexit))
    {
        for (int i = 0; i < globsym.top; i++)
        {   symbol *s;

            s = globsym.tab[i];
            if (s->Sfl == FLreg && mask[s->Sreglsw] & fregsaved &&
                type_size(s->Stype) <= REGSIZE)
            {
                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
