/*  $VER: vbcc (regs.c) $Revision: 1.31 $   */
/*  Registerzuteilung           */

#include "opt.h"

static char FILE_[]=__FILE__;

typedef struct regp {int treg;Var *tvar,*tmp;} regp;
int sregsa[MAXR+1];

void do_load_parms(regp [],flowgraph *);
void load_one_parm(int,int,Var *,Var *,flowgraph *);

#ifndef NO_OPTIMIZER

int (*savings)[MAXR+1],regu[MAXR+1];
int *rvlist;

static int const_vars;

static bvtype bregs[BVSIZE(MAXR+1)/sizeof(bvtype)];
static bvtype bregsm[BVSIZE(MAXR+1)/sizeof(bvtype)];

Var *lparms[MAXR+1];

static void insert_cobj(obj *o,obj *co);

#ifndef HAVE_TARGET_RALLOC
int reg_prio[MAXR+1];
#define cost_move_reg(x,y) 1
#define cost_load_reg(x,y) 2
#define cost_save_reg(x,y) 2
#define cost_pushpop_reg(x) 4

int cost_savings(IC *p,int r,obj *o)
{
  int c=p->code;
  if(o->flags&VKONST) return 0;
  if(o->flags&DREFOBJ){
    int t;
    if(o==&p->q1){
      t=q1typ(p);
    }else if(o==&p->q2){
      t=q2typ(p);
    }else{
      t=ztyp(p);
    }
    if(regok(r,o->v->vtyp->flags,t)) return 4;
  }
  if(c==SETRETURN&&r==p->z.reg&&!(o->flags&DREFOBJ)) return 3;
  if(c==GETRETURN&&r==p->q1.reg&&!(o->flags&DREFOBJ)) return 3;
  return 2;
}
#endif

int cmp_savings(const void *v1,const void *v2)
/*  Vergleichsfkt, um rvlist nach savings zu sortieren  */
{
    return savings[*(int *)v2][0]-savings[*(int *)v1][0];
}
int entry_load(flowgraph *fg,int i)
/*  Testet, ob die Variable in Register i am Anfang von Block fg geladen    */
/*  werden muss, d.h. ein Vorgaenger sie nicht im selben Register hat.      */
{
    flowlist *lp;
    Var *v=fg->regv[i];
    int vi=v->index;
    lp=fg->in;

    while(lp){
        if(lp->graph&&lp->graph->regv[i]!=v&&(vi>=vcount-rcount||BTST(lp->graph->av_out,vi))) return 1;
        lp=lp->next;
    }
    return 0;
}

/* checks if variable i can be held in a register in loop start-end */
/* currently we check whether an IC uses/modifies the variable and */
/* has a corresponding drefobj which might alias the variable */
int check_cacheable(int i,flowgraph *start,flowgraph *end)
{
  int j,k;
  IC *p;Var *v;
  flowgraph *g;
  /* constants are always cacheable */
  if(vilist[i]->identifier[0]==' ')
    return 1;
  if(i<vcount-rcount){
    /* simple local variables are also easy */
    v=vilist[i];
    if(v->storage_class!=STATIC&&v->storage_class!=EXTERN&&!(v->flags&USEDASADR))
      return 1;
  }
  if(disable&16384) return 0;
  for(g=start;g;g=g->normalout){
    for(p=g->start;p;p=p->next){
      for(j=0;j<p->use_cnt;j++){
	k=p->use_list[j].v->index;
	if(p->use_list[j].flags&DREFOBJ)
	  k+=vcount-rcount;
	if(k==i&&(p->code==CALL||(p->q1.flags&DREFOBJ)||(p->q2.flags&DREFOBJ)))
	  return 0;
      }
      for(j=0;j<p->change_cnt;j++){
	k=p->change_list[j].v->index;
	if(p->change_list[j].flags&DREFOBJ)
	  k+=vcount-rcount;
	if(k==i&&(p->code==CALL||(p->z.flags&DREFOBJ)))
	  return 0;
      }
      if(p==g->end) break;
    }
    if(g==end) break;
  }
  /*printf("assigned static %s to reg\n",vilist[i]->identifier);*/
  return 1;
}
int exit_save(flowgraph *fg,int i)
/*  Testet, ob die Variable in Register i am Ende von Block fg gespeichert  */
/*  werden muss, d.h. der Vorgaenger eines Nachfolgers nicht dieselbe       */
/*  Variable im selben Register hat.                                        */
{
    flowlist *lp;
    /* Konstanten muessen nie gespeichert werden */
    if(fg->regv[i]->index>=vcount-rcount) return 0;
    if(!fg->normalout){
      /* letzter Block, statische Variablen und drefobjs aktiv */
      int sc=fg->regv[i]->storage_class;
      return sc==STATIC||sc==EXTERN;
    }
    if((fg->normalout&&(!fg->end||fg->end->code!=BRA))&&BTST(fg->normalout->av_in,fg->regv[i]->index)){
        if(fg->normalout->regv[i]!=fg->regv[i]) return 1;
        lp=fg->normalout->in;
        while(lp){
            if(lp->graph&&lp->graph->regv[i]!=fg->regv[i]) return 1;
            lp=lp->next;
        }
    }
    if(fg->branchout&&BTST(fg->branchout->av_in,fg->regv[i]->index)){
        if(fg->branchout->regv[i]!=fg->regv[i]) return 1;
        lp=fg->branchout->in;
        while(lp){
            if(lp->graph&&lp->graph->regv[i]!=fg->regv[i]) return 1;
            lp=lp->next;
        }
    }
    return 0;
}
void load_reg_parms(flowgraph *fg)
/*  Laedt Registerparameter, falls noetig.                              */
{
    int i,j; Var *v;
    regp regp[MAXR+1]={0};
/*     for(i=1;i<=MAXR;i++){ regp[i].treg=0;regp[i].tvar=0;} */
    for(i=0;i<vcount-rcount;i++){
        v=vilist[i];
        if((v->flags&REGPARM)&&(fg->regv[abs(v->reg)]!=v||(v->flags&CONVPARAMETER))&&(BTST(fg->av_in,i)||(v->flags&USEDASADR))){
	  regp[abs(v->reg)].tvar=v;
	  for(j=1;j<=MAXR;j++)
	    if(fg->regv[j]==v||lparms[j]==v) regp[abs(v->reg)].treg=j;
	}
    }
    do_load_parms(regp,fg);
}

static int check_retreg(flowgraph *fg)
{
  IC *p;int retreg=0;
  for(p=fg->start;p;p=p->next){
    if(p->code==SETRETURN&&p->z.reg>=0){
      retreg=p->z.reg;
      break;
    }
  }
  for(p=last_ic;retreg&&p;p=p->prev){
    if(p->code==FREEREG){
      if(p->q1.reg==retreg)
	retreg=0;
    }else
      break;
  }
  if(DEBUG&1024) printf("check_retreg: %s\n",regnames[retreg]);
  return retreg;
}
void insert_regs(flowgraph *fg1)
/*  Fuegt Registervariablen in die ICs ein.                             */
{
  int i,exit_block;IC *p,*lic=0,*new;flowgraph *lfg=0,*fg;
    if(DEBUG&9216) printf("inserting register variables\n");
    fg=fg1;
    while(fg){
        if(DEBUG&8192) printf("block %d:\n",fg->index);
	if(fg!=fg1&&!fg->branchout&&!fg->normalout&&fg->start==fg->end)
	  exit_block=1;
	else
	  exit_block=0;
        p=fg->start;
        while(p){
            for(i=1;i<=MAXR;i++){
                if(!fg->regv[i]) continue;
                if(p->code==ALLOCREG&&p->q1.reg==i) ierror(0);
                if((p->q1.flags&(VAR|REG|DONTREGISTERIZE))==VAR&&p->q1.v==fg->regv[i]&&(p->q1.v->index<vcount-rcount||cost_savings(p,i,&p->q1)>0)){
		    if(p->q1.v->index>=vcount-rcount)
		      p->q1.flags&=~(KONST|VAR|VARADR);
		    p->q1.flags|=REG;
                    p->q1.reg=i;
                }
                if((p->q2.flags&(VAR|REG|DONTREGISTERIZE))==VAR&&p->q2.v==fg->regv[i]&&(p->q2.v->index<vcount-rcount||cost_savings(p,i,&p->q2)>0)){
		    if(p->q2.v->index>=vcount-rcount)
		      p->q2.flags&=~(KONST|VAR|VARADR);
		    p->q2.flags|=REG;
                    p->q2.reg=i;
                }
                if((p->z.flags&(VAR|REG|DONTREGISTERIZE))==VAR&&p->z.v==fg->regv[i]&&(p->z.v->index<vcount-rcount||cost_savings(p,i,&p->z)>0)){
		  if(p->z.v->index>=vcount-rcount)
		    p->z.flags&=~(KONST|VAR|VARADR);
		  p->z.flags|=REG;
		  p->z.reg=i;
                }
            }
	    /* evtl. const-Variablen ruecksubstituieren */
	    if((p->q1.flags&VAR)&&p->q1.v->index>=vcount-rcount){
	      insert_cobj(&p->q1,&p->q1.v->cobj);
	    }
	    if((p->q2.flags&VAR)&&p->q2.v->index>=vcount-rcount){
	      insert_cobj(&p->q2,&p->q2.v->cobj);
	    }
	    if((p->z.flags&VAR)&&p->z.v->index>=vcount-rcount){
	      insert_cobj(&p->z,&p->z.v->cobj);
	    }
            if(DEBUG&8192) pric2(stdout,p);
            if(p==fg->end) break;
            p=p->next;
        }
        if(fg->start&&fg->start->code==LABEL&&fg!=fg1) lic=fg->start;
        for(i=1;i<=MAXR;i++){
            if(fg->regv[i]){
                if(DEBUG&8192){
                    printf("(%s),%ld(%p) assigned to %s\n",fg->regv[i]->identifier,zm2l(fg->regv[i]->offset),(void *)fg->regv[i],regnames[i]);
                    if(fg->regv[i]->index>=vcount-rcount||BTST(fg->av_in,fg->regv[i]->index)) printf("active at the start of block\n");
                    if(fg->regv[i]->index<vcount-rcount&&BTST(fg->av_out,fg->regv[i]->index)) printf("active at the end of block\n");
                }

                if(fg->regv[i]->index<vcount-rcount&&BTST(fg->av_out,fg->regv[i]->index)){
                /*  Variable beim Austritt aktiv?   */
                    if(exit_save(fg,i)){
		      IC *tp;int mark_retreg=0;
                        if(DEBUG&8192) printf("\thave to save it at end of block\n");
                        new=new_IC();
                        new->line=0;
                        new->file=0;
                        new->code=ASSIGN;
                        new->typf=fg->regv[i]->vtyp->flags;
                        /* vla */
                        if(ISARRAY(new->typf)) 
                          new->typf=POINTER_TYPE(fg->regv[i]->vtyp->next);
                        /*  cc  */
                        if(new->typf==0) ierror(0);
                        new->q1.flags=VAR|REG;
                        new->q1.val.vmax=l2zm(0L);
                        new->q1.v=fg->regv[i];
                        new->q1.reg=i;
                        new->q2.flags=0;
                        new->q2.val.vmax=szof(fg->regv[i]->vtyp);
                        new->z.flags=VAR|DONTREGISTERIZE;
                        new->z.val.vmax=l2zm(0L);
                        new->z.v=fg->regv[i];
                        new->q1.am=new->q2.am=new->z.am=0;
                        new->use_cnt=new->change_cnt=0;
                        new->use_list=new->change_list=0;
                        /*  Vor FREEREGs und evtl. Branch+COMPARE/TEST setzen   */
                        if(fg->end){
                            tp=fg->end;
			    if(!tp->next){
			      mark_retreg=check_retreg(fg1);
			      if(DEBUG&1024) printf("save at end, markretreg=%s\n",regnames[mark_retreg]);
			    }
                            while(tp!=fg->start&&(tp->code==FREEREG||tp->code==SETRETURN||tp->code==NOP)){
			      if(tp->code==SETRETURN) mark_retreg=0;
			      tp=tp->prev;
			    }
                            if(tp&&tp->code>=BEQ&&tp->code<=BRA){
                                if(tp->code<BRA){
                                    int c;
                                    do{
                                        tp=tp->prev;
                                        c=tp->code;
                                        if(c!=FREEREG&&c!=COMPARE&&c!=TEST) ierror(0);
                                    }while(c!=COMPARE&&c!=TEST);
                                }
                                tp=tp->prev;
                            }
                        }else tp=lic;
                        insert_IC_fg(fg,tp,new);
			if(mark_retreg){
			  IC *nop;
			  insert_allocreg(fg,tp,ALLOCREG,mark_retreg);
			  insert_allocreg(fg,new,FREEREG,mark_retreg);
			  nop=new_IC();
			  nop->code=NOP;
			  nop->q1.flags=REG;
			  nop->q1.reg=mark_retreg;
			  insert_IC_fg(fg,new,nop);
			}
                    }
                }
                if(fg->regv[i]->index>=vcount-rcount||BTST(fg->av_in,fg->regv[i]->index)){
		  if(!exit_block&&(fg==fg1||entry_load(fg,i))&&(fg!=fg1||!(fg->regv[i]->flags&REGPARM))){
                        if(DEBUG&8192) printf("\thave to load it at start of block\n");
                        new=new_IC();
                        new->line=0;
                        new->file=0;
                        new->code=ASSIGN;
                        new->typf=fg->regv[i]->vtyp->flags;
                        /* vla */
                        if(ISARRAY(new->typf)) 
                          new->typf=POINTER_TYPE(fg->regv[i]->vtyp->next);
                        /*  cc  */
                        if(new->typf==0) ierror(0);
			if(fg->regv[i]->index>=vcount-rcount){
			  insert_cobj(&new->q1,&fg->regv[i]->cobj);
			}else{
			  new->q1.flags=VAR|DONTREGISTERIZE;
			  new->q1.val.vmax=l2zm(0L);
			  new->q1.v=fg->regv[i];
			}
			new->q2.flags=0;
                        new->q2.val.vmax=szof(fg->regv[i]->vtyp);
			if(fg->regv[i]->index>=vcount-rcount)
			  new->z.flags=REG;
			else
			  new->z.flags=VAR|REG;
                        new->z.val.vmax=l2zm(0L);
                        new->z.v=fg->regv[i];
                        new->z.reg=i;
                        new->q1.am=new->q2.am=new->z.am=0;
                        new->use_cnt=new->change_cnt=0;
                        new->use_list=new->change_list=0;
                        insert_IC_fg(fg,lic,new);
                    }
                }
                if(!lfg||!lfg->regv[i]) insert_allocreg(fg,lic,ALLOCREG,i);
                if(!fg->normalout||!fg->normalout->regv[i])
                    insert_allocreg(fg,fg->end?fg->end:lic,FREEREG,i);
            }
        }
        if(fg->end) lic=fg->end;
        lfg=fg;
        fg=fg->normalout;
    }
    load_reg_parms(fg1);
}

void do_loop_regs(flowgraph *start,flowgraph *end,int intask)
/*  Macht die Variablenzuweisung in Schleife start-end.                 */
/*  Wenn end==0 Registerzuweisung fuer die ganze Funktion, ansonsten    */
/*  fuer die Schleife, die zum Header start gehoert.                    */
{
  flowgraph *g,*lend;
  int i,r,iterations;
  Var *lregs[MAXR+1]={0};
  unsigned char regu[MAXR+1]={0};
  bvtype *isused=mymalloc(vsize);
  /*  Berechnen, wieviel ungefaehr eingespart wird, wenn eine Variable    */
  /*  fuer diese Schleife in einem best. Register gehalten wird.          */
  /*  Die savings in einer Schleife werden multipliziert, um das          */
  /*  Laden/Speichern ausserhalb der Schleife geringer zu wichten.        */
  /*    if(end&&(!g->normalout||!g->normalout->loopend||g->normalout->loopend->normalout->index!=-2)) ierror(0);*/
  /*  alle auf 0  */

  for(i=0;i<vcount-rcount+const_vars;i++){
    if(i<vcount-rcount&&(vilist[i]->vtyp->flags&VOLATILE)){
      for(r=1;r<=MAXR;r++){
	savings[i][r]=INT_MIN;
      }
    }else{
      for(r=1;r<=MAXR;r++){
	savings[i][r]=0;
      }
    }
  }
  if(end){
    Var *v;
    /* estimated number of iterations, avoid too big values */
    if(optsize)
      iterations=1;
    else
      iterations=4;
    /*  Evtl. Kosten fuers Laden/Speichern beim Ein-/Austritt in die    */
    /*  Schleife.                                                       */
    end=start->normalout->loopend;
    g=end->normalout;
    if(DEBUG&9216) printf("assigning regs to blocks %d to %d\n",start->normalout->index,end->index);
    /*  Werte modifizieren, falls Variable am Anfang/Ende der Schleife  */
    /*  geladen/gespeichert werden muss.                                */
    /*  FIXME: Evtl. noetige stores/loads in anderen Bloecken!          */
    for(i=0;i<vcount-rcount+const_vars;i++){
      v=vilist[i];
      if(i>=vcount-rcount||BTST(start->av_in,i)){
 	for(r=1;r<=MAXR;r++)
	  if(start->regv[r]!=v&&savings[i][r]!=INT_MIN)
	    savings[i][r]-=cost_load_reg(r,v);
      }
      if(i<vcount-rcount&&BTST(g->av_out,i)){
	for(r=1;r<=MAXR;r++)
	  if(g->regv[r]!=v&&savings[i][r]!=INT_MIN)
	    savings[i][r]-=cost_save_reg(r,v);
      }
    }
    /*  Werte modifizieren, falls eine andere Variable gespeichert oder */
    /*  geladen werden muss. Hmm..stimmt das so?                        */
    for(r=1;r<=MAXR;r++){
      v=start->regv[r];
      if(v&&v->index<vcount-rcount&&BTST(start->av_in,v->index)){
	for(i=0;i<vcount-rcount;i++)
	  if(v->index!=i&&savings[i][r]!=INT_MIN)
	    savings[i][r]-=cost_load_reg(r,vilist[i]);
      }
      if(v&&v->index<vcount-rcount&&BTST(g->av_out,v->index)){
	for(i=0;i<vcount-rcount;i++)
	  if(v->index!=i&&savings[i][r]!=INT_MIN)
	    savings[i][r]-=cost_save_reg(r,vilist[i]);
      }
    }
    g=start->normalout;
  }else{
    iterations=1;
    /*  Bei Registervergabe fuer die ganze Funktion muessen alle beim   */
    /*  Eintritt der Funktion aktiven Variablen geladen werden.         */
    /* statics have to be saved at the end */
    if(DEBUG&9216) printf("assigning regs to whole function\n");
    for(i=0;i<vcount-rcount+const_vars;i++){
      if(i<vcount-rcount&&(vilist[i]->storage_class==STATIC||vilist[i]->storage_class==EXTERN)){
	for(r=1;r<=MAXR;r++)
	  if(savings[i][r]!=INT_MIN)
	    savings[i][r]-=cost_save_reg(r,vilist[i]);
      }
      if(i>=vcount-rcount||BTST(start->av_in,i)){
	int pr=abs(vilist[i]->reg);
	for(r=1;r<=MAXR;r++){
	  if(savings[i][r]!=INT_MIN){
	    if(pr==0){
	      savings[i][r]-=cost_load_reg(r,vilist[i]);
	    }else if(!regsa[r]){
	      if(r==pr)
		savings[i][r]+=cost_save_reg(r,vilist[i]);
	      else
		/* factor of 2, because load_parms is not always optimal */
		savings[i][r]+=(cost_save_reg(r,vilist[i])-cost_move_reg(pr,r))/2;
	    }
	  }
	}
      }
    }
    /*  Nonscratchregister muessen geladen/gesichert werden. */
    /*FIXME: einige unschoene Effekte(?) */
    if(!intask){
      for(r=1;r<=MAXR;r++){
	if(!regscratch[r]&&!regsa[r]){
	  for(i=0;i<vcount-rcount+const_vars;i++)
	    if(savings[i][r]!=INT_MIN)
	      savings[i][r]-=cost_pushpop_reg(r);
	}
      }
    }
    g=start;
  }
  if(DEBUG&9216) printf("calculating approximate savings\n");

  lend=0;
  for(;g;g=g->normalout){
    IC *p;Var *v;
    int t,vt;
    if(!end&&!lend){
      lend=g->loopend;
      if(lend&&iterations==1&&!optsize) iterations=4;
    }

    /*  Wenn das Register in dem Block benutzt wird, muss man es retten */
    for(r=1;r<=MAXR;r++){
      if(BTST(g->regused,r)||(reg_pair(r,&rp)&&(BTST(g->regused,rp.r1)||BTST(g->regused,rp.r2)))){
	int vi;
	if(g->regv[r]) vi=g->regv[r]->index; else vi=-1;
	for(i=0;i<vcount-rcount+const_vars;i++){
	  if(i>=vcount-rcount||BTST(g->av_out,i)||BTST(g->av_gen,i))
	    if(vi!=i&&savings[i][r]!=INT_MIN)
	      savings[i][r]-=iterations*(cost_load_reg(r,vilist[i])+cost_save_reg(r,vilist[i]));
	}
      }
    }
    memcpy(isused,g->av_out,vsize);
    p=g->end;
    while(p){
      if((p->q1.flags&(VAR|VARADR|REG))==VAR){
	v=p->q1.v;vt=v->vtyp->flags;
        /* vla */
        if(ISARRAY(vt)&&is_vlength(v->vtyp))
          vt=POINTER_TYPE(v->vtyp->next);
	if((optflags&1024)||((v->storage_class==AUTO||v->storage_class==REGISTER)&&!(v->flags&USEDASADR))){
	  i=v->index;
	  if(p->code==ADDRESS||((p->code==ASSIGN||p->code==PUSH)&&(q1typ(p)&NQ)==(CHAR)&&!zmeqto(p->q2.val.vmax,l2zm(1L)))){
	    for(r=1;r<=MAXR;r++)
	      savings[i][r]=INT_MIN;
	  }else{
	    for(r=1;r<=MAXR;r++){
	      if(!regsa[r]&&!BTST(g->regused,r)&&savings[i][r]!=INT_MIN&&regok(r,vt,-1)&&(!reg_pair(r,&rp)||(!regsa[rp.r1]&&!regsa[rp.r2]&&!BTST(g->regused,rp.r1)&&!BTST(g->regused,rp.r2)))){
		int s=cost_savings(p,r,&p->q1);
		if(s==INT_MIN||savings[i][r]==INT_MIN)
		  savings[i][r]=INT_MIN;
		else
		  savings[i][r]+=iterations*s;
	      }
	    }
	  }
	}
      }
      if((p->q2.flags&(VAR|VARADR|REG))==VAR){
	v=p->q2.v;vt=v->vtyp->flags;
        /* vla */
        if(ISARRAY(vt)&&is_vlength(v->vtyp))
          vt=POINTER_TYPE(v->vtyp->next);
	if((optflags&1024)||((v->storage_class==AUTO||v->storage_class==REGISTER)&&!(v->flags&USEDASADR))){
	  i=v->index;
	  for(r=1;r<=MAXR;r++){
	    if(!regsa[r]&&!BTST(g->regused,r)&&savings[i][r]!=INT_MIN&&regok(r,vt,-1)&&(!reg_pair(r,&rp)||(!regsa[rp.r1]&&!regsa[rp.r2]&&!BTST(g->regused,rp.r1)&&!BTST(g->regused,rp.r2)))){
	      int s=cost_savings(p,r,&p->q2);
	      if(s==INT_MIN||savings[i][r]==INT_MIN)
		savings[i][r]=INT_MIN;
	      else
		savings[i][r]+=iterations*s;
	    }
	  }
	}
      }
      if((p->z.flags&(VAR|VARADR|REG))==VAR){
	v=p->z.v;vt=v->vtyp->flags;
        if(ISARRAY(vt)&&is_vlength(v->vtyp))
          vt=POINTER_TYPE(v->vtyp->next);
	if((optflags&1024)||((v->storage_class==AUTO||v->storage_class==REGISTER)&&!(v->flags&USEDASADR))){
	  i=v->index;
	  if(p->code==ASSIGN&&(ztyp(p)&NQ)==(CHAR)&&!zmeqto(p->q2.val.vmax,l2zm(1L))){
	    for(r=1;r<=MAXR;r++)
	      savings[i][r]=INT_MIN;
	  }
	  for(r=1;r<=MAXR;r++){
	    if(!regsa[r]&&!BTST(g->regused,r)&&savings[i][r]!=INT_MIN&&regok(r,vt,-1)&&(!reg_pair(r,&rp)||(!regsa[rp.r1]&&!regsa[rp.r2]&&!BTST(g->regused,rp.r1)&&!BTST(g->regused,rp.r2)))){
	      int s=cost_savings(p,r,&p->z);
	      if(s==INT_MIN||savings[i][r]==INT_MIN)
		savings[i][r]=INT_MIN;
	      else
		savings[i][r]+=iterations*s;
	    }
	  }
	}
      }
      /* bei Funktionsaufruf zerstoert... */
      if(p->code==CALL){
	if((p->q1.flags&(VAR|DREFOBJ))==VAR&&p->q1.v->fi&&(p->q1.v->fi->flags&ALL_REGS)&&!(disable&2048)){
	  for(i=0;i<vcount-rcount+const_vars;i++){
	    if(i>=vcount-rcount||BTST(isused,i)){
	      for(r=1;r<=MAXR;r++){
		if(regscratch[r]&&savings[i][r]!=INT_MIN&&BTST(p->q1.v->fi->regs_modified,r))
		  savings[i][r]-=iterations*(cost_load_reg(r,vilist[i])+cost_save_reg(r,vilist[i]));
	      }
	    }	  
	  }
	}else{
	  for(i=0;i<vcount-rcount+const_vars;i++){
	    if(i>=vcount-rcount||BTST(isused,i)){
	      for(r=1;r<=MAXR;r++){
		if(regscratch[r]&&savings[i][r]!=INT_MIN)
		  savings[i][r]-=iterations*(cost_load_reg(r,vilist[i])+cost_save_reg(r,vilist[i]));
	      }
	    }
	  }
	}
      }
      if(p==g->start) break;
      if(p->change_cnt!=0||p->use_cnt!=0)
	av_update(p,isused);
      p=p->prev;
    }
    if(g==end) break;
    if(!end){
      if(g==lend) {lend=0;iterations=1;}
    }
  }
  /*  Maximum ermitteln   */
  memset(bregs,0,sizeof(bregs));
  for(i=0;i<vcount-rcount+const_vars;i++){
    int m=0,t;Var *v;
    v=vilist[i];t=v->vtyp->flags;
    if(ISARRAY(t)&&is_vlength(v->vtyp))
      t=POINTER_TYPE(v->vtyp->next);
    if(!(optflags&1024)&&((v->storage_class!=AUTO&&v->storage_class!=REGISTER)||(v->flags&USEDASADR))){
      for(r=0;r<=MAXR;r++)
	savings[i][r]=INT_MIN;
    }else{
      int one=0;
      for(r=1;r<=MAXR;r++){
	/*  Variables should have been put to argument registers by local_regs()  */
	/*if(r==abs(v->reg)&&!(v->flags&REGPARM)) savings[i][r]=INT_MAX;*/
	if(regsa[r]||!regok(r,t,-1)) savings[i][r]=INT_MIN;
	if(savings[i][r]>m){
	  m=savings[i][r];
	  one=r;
	}else if(savings[i][r]==m){
	  one=0;
	}
      }
      savings[i][0]=m;
      if(one) BSET(bregs,one); /* mark one register has unique value */
    }
  }
  if(DEBUG&8192){
    for(i=0;i<vcount-rcount+const_vars;i++){
      printf("(%s),%ld(best=%d):\n",vilist[i]->identifier,zm2l(vilist[i]->offset),savings[i][0]);
      for(r=1;r<=MAXR;r++)
	printf("%s=%d ",regnames[r],savings[i][r]);
      printf("\n");
    }
    for(r=0;r<=MAXR;r++)
      if(BTST(bregs,r))
	printf("reg %s unique\n",regnames[r]);
  }
  /*  Suchen, welche Variablen/Registerkombination das beste Ergebnis */
  /*  liefert. Nur angenaehert, da sonst wohl zu aufwendig. Simplex?  */
  for(i=0;i<vcount-rcount+const_vars;i++) rvlist[i]=i;
  vqsort(rvlist,vcount-rcount+const_vars,sizeof(*rvlist),cmp_savings);
  for(i=0;i<vcount-rcount+const_vars;i++){
    int use=0,m=0,prio=0,vi;
    vi=rvlist[i];
    /*if(vilist[vi]->flags&USEDASADR) continue;*/
    if(DEBUG&8192) printf("%d: (%s),%ld(best=%d)\n",i,vilist[vi]->identifier,zm2l(vilist[vi]->offset),savings[vi][0]);
    for(r=1;r<=MAXR;r++){
      if(!lregs[r]&&!regu[r]&&savings[vi][r]>=m&&(!reg_pair(r,&rp)||(!regu[rp.r1]&&!regu[rp.r2]))){
	int better=0;
	if(savings[vi][r]>m){
	  better=1;
	}else if(savings[vi][r]==m){
	  if(reg_prio[r]>prio){
	    better=1;
	  }else if(BTST(bregs,use)&&!BTST(bregs,r)){
	    better=1;
	  }
	}
	if(better){
	  m=savings[vi][r];prio=reg_prio[r];
	  use=r;
	}
      }
    }
    if(m>0&&check_cacheable(vi,start,end)){
      if(DEBUG&9216) printf("assigned (%s),%ld(%p) to %s, saving=%d\n",vilist[vi]->identifier,zm2l(vilist[vi]->offset),(void *)vilist[vi],regnames[use],m);
      lregs[use]=vilist[vi];
      regu[use]=1;
      if(reg_pair(use,&rp))
	regu[rp.r1]=regu[rp.r2]=1;
    }
  }
  /*  Registervariablen in alle Bloecke der Schleife eintragen    */
  /*  dabei beruecksichtigen, dass sie in manchen Bloecken nicht  */
  /*  in Register kommen koennen, wenn das Register da schon von  */
  /*  local_regs benutzt wird                                     */
  /*  Gegebenenfalls auch in Header/Footer einer Schleife         */
  /*  eintragen.                                                  */
  if(DEBUG&9216) printf("propagate register vars\n");
  for(g=start;g;g=g->normalout){
    for(r=1;r<=MAXR;r++){
      if(lregs[r]&&!BTST(g->regused,r)&&(!reg_pair(r,&rp)||(!BTST(g->regused,rp.r1)&&!BTST(g->regused,rp.r2)))){
	/*  Falls Variable schon in anderem Register, loeschen  */
	for(i=1;i<=MAXR;i++){
	  if(g->regv[i]==lregs[r]) g->regv[i]=0;
	  if(reg_pair(i,&rp)){
	    if(rp.r1==r||rp.r2==r) g->regv[i]=0;
	  }
	}
	g->regv[r]=lregs[r];
	if(reg_pair(r,&rp)) g->regv[rp.r1]=g->regv[rp.r2]=0;
      }
    }
    if(end&&g==end->normalout) break;
  }
}
void block_regs(flowgraph *fg)
/*  macht die Variablenzuweisung fuer einzelne Bloecke  */
{
    flowgraph *g,**fgp;
    int i,r,changed,fgz;
    if(DEBUG&9216) printf("block_regs\n");

    savings=mymalloc((vcount-rcount)*sizeof(*savings));
    rvlist=mymalloc((vcount-rcount)*sizeof(*rvlist));

    /*  Array auf Bloecke im Flussgraphen mangels doppelter Verkettung  */
    fgp=mymalloc(basic_blocks*sizeof(*fgp));
    g=fg;fgz=0;
    while(g){
        fgp[fgz]=g;fgz++;
        g=g->normalout;
    }
    if(fgz>basic_blocks) ierror(0); else basic_blocks=fgz;
    /*  alle auf 0  */
    do{
        changed=0;
        if(DEBUG&9216) printf("block_regs pass\n");
        for(fgz=basic_blocks-1;fgz>=0;fgz--){
            IC *p;Var *v;flowlist *lp;
            int t,vt;
            g=fgp[fgz];
            if(DEBUG&8192) printf("assigning regs to block %d\n",g->index);
            /*  berechnen, wieviel ungefaehr eingespart wird, wenn eine Variable    */
            /*  fuer diesen Block in einem best. Register gehalten wird             */
            if(DEBUG&8192) printf("calculating approximate savings\n");

            for(i=0;i<vcount-rcount;i++){
                for(r=1;r<=MAXR;r++){
                    if(!g->regv[r]||g->regv[r]->index!=i){
                        int w=0;
                        /*  Variable muss evtl. geladen/gespeichert werden  */
                        if(BTST(g->av_in,i)) w--;
                        if(BTST(g->av_out,i)) w--;
                        savings[i][r]=w;
                    }
                }
            }
            if(g->calls>0){
            /*  bei Funktionsaufrufen muessen Scratchregister gespeichert werden */
                for(r=1;r<=MAXR;r++)
                    if(regscratch[r])
                        for(i=0;i<vcount-rcount;i++) savings[i][r]-=g->calls*2;
            }
            /*  Wenn Vorgaenger/Nachfolger selbe Variable im selben */
            /*  Register hat, entfaellt Laden/Speichern in diesem   */
            /*  Block und vermutlich auch im Vorgaenger/Nachfolger  */
            /*  nicht immer, aber naeherungsweise...                */
            lp=g->in;
            while(lp){
                if(lp->graph){
                    for(r=1;r<=MAXR;r++){
                        if(lp->graph->regv[r]&&BTST(g->av_in,lp->graph->regv[r]->index)) savings[lp->graph->regv[r]->index][r]+=2;
                    }
                }
                lp=lp->next;
            }
            if(g->branchout){
                for(r=1;r<=MAXR;r++){
                    if(g->branchout->regv[r]&&BTST(g->av_out,g->branchout->regv[r]->index)) savings[g->branchout->regv[r]->index][r]+=2;
                }
            }
            if(g->normalout&&(!g->normalout->end||g->normalout->end->code!=BRA)){
                for(r=1;r<=MAXR;r++){
                    if(g->normalout->regv[r]&&BTST(g->av_out,g->normalout->regv[r]->index)) savings[g->normalout->regv[r]->index][r]+=2;
                }
            }

            p=g->start;
            while(p){
                if((p->q1.flags&(VAR|VARADR|REG))==VAR){
                    v=p->q1.v;
                    if((v->storage_class==AUTO||v->storage_class==REGISTER)&&!(v->flags&USEDASADR)){
                        vt=v->vtyp->flags&NU;
                        i=v->index;
                        if(p->q1.flags&DREFOBJ) t=p->typf&NU; else t=0;
                        for(r=1;r<=MAXR;r++){
                            if(!regsa[r]&&!BTST(g->regused,r)){
                                /*  extra saving, falls passendes Reg fuer DREF */
                                if(t&&regok(r,vt,t)) savings[i][r]++;
                                if(regok(r,vt,0)) savings[i][r]++;
                            }
                        }
                    }
                }
                if((p->q2.flags&(VAR|VARADR|REG))==VAR){
                    v=p->q2.v;
                    if((v->storage_class==AUTO||v->storage_class==REGISTER)&&!(v->flags&USEDASADR)){
                        vt=v->vtyp->flags&NU;
                        i=v->index;
                        if(p->q2.flags&DREFOBJ) t=p->typf&NU; else t=0;
                        for(r=1;r<=MAXR;r++){
                            if(!regsa[r]&&!BTST(g->regused,r)){
                                /*  extra saving, falls passendes Reg fuer DREF */
                                if(t&&regok(r,vt,t)) savings[i][r]++;
                                if(regok(r,vt,0)) savings[i][r]++;
                            }
                        }
                    }
                }
                if((p->z.flags&(VAR|VARADR|REG))==VAR){
                    v=p->z.v;
                    if((v->storage_class==AUTO||v->storage_class==REGISTER)&&!(v->flags&USEDASADR)){
                        vt=v->vtyp->flags&NU;
                        i=v->index;
                        if(p->z.flags&DREFOBJ) t=p->typf&NU; else t=0;
                        for(r=1;r<=MAXR;r++){
                            if(!regsa[r]&&!BTST(g->regused,r)){
                                /*  extra saving, falls passendes Reg fuer DREF */
                                if(t&&regok(r,vt,t)) savings[i][r]++;
                                if(regok(r,vt,0)) savings[i][r]++;
                            }
                        }
                    }
                }
                if(p==g->end) break;
                p=p->next;
            }
            /*  moegliche Kandidaten suchen; muss nicht immer die beste */
            /*  Kombination finden, sollte aber bei lokaler Vergabe     */
            /*  selten einen Unterschied machen                         */
            for(r=1;r<=MAXR;r++){
                if(g->regv[r]||BTST(g->regused,r)) continue;
                for(i=0;i<vcount-rcount;i++){
                    if(savings[i][r]>0){
                        int flag;Var *v=vilist[i];
                        /*  Variable schon in anderem Register? */
                        for(flag=1;flag<=MAXR;flag++)
                            if(g->regv[flag]==v){flag=-1;break;}
                        if(flag>0){
                            if(DEBUG&9216) printf("assigned (%s),%ld(%p) to %s; saving=%d\n",vilist[i]->identifier,zm2l(vilist[i]->offset),(void *)vilist[i],regnames[r],savings[i][r]);
			    if(vilist[i]&&!vilist[i]->vtyp->flags) ierror(0);
                            g->regv[r]=vilist[i];
                            changed=1;
                            break;
                        }
                    }
                }
            }
        }
    }while(changed);
    /*  jetzt nochmal globale Register vergeben */
/*    do_loop_regs(fgp[0],fgp[basic_blocks-1]);*/

    free(fgp);
    free(rvlist);
    free(savings);
}

void loop_regs(flowgraph *fg,int intask)
/*  weist Variablen in Schleifen Register zu    */
{
    flowgraph *g;
    if(disable&4096) return;
    if(DEBUG&9216) printf("assigning regs to function\n");
    savings=mymalloc((vcount-rcount+const_vars)*sizeof(*savings));
    rvlist=mymalloc((vcount-rcount+const_vars)*sizeof(*rvlist));
    do_loop_regs(fg,0,intask);
    if(DEBUG&9216) printf("assigning regs in loops\n");
    for(g=fg;g;g=g->normalout){
        if(g->index==-1&&!(disable&32768)) do_loop_regs(g,g,0);
    }
    free(rvlist);
    free(savings);
}
void insert_allocreg(flowgraph *fg,IC *p,int code,int reg)
/*  Fuegt ein ALLOCREG/FREEREG (in code) hinter p ein - bei p==0 in */
/*  first_ic.                                                       */
{
    IC *new=new_IC();
    /*    printf("%s %s",code==FREEREG?"freereg":"allocreg",regnames[reg]);pric2(stdout,p);*/
    new->line=0;
    new->file=0;
    BSET(fg->regused,reg);
    if(reg_pair(reg,&rp)){
	BSET(fg->regused,rp.r1);
	BSET(fg->regused,rp.r2);
	regused[rp.r1]=regused[rp.r2]=1;
    }
    regused[reg]=1;
    new->code=code;
    new->typf=0;
    new->q1.am=new->q2.am=new->z.am=0;
    new->q1.flags=REG;
    new->q1.reg=reg;
    new->q2.flags=new->z.flags=0;
    new->use_cnt=new->change_cnt=0;
    new->use_list=new->change_list=0;
    insert_IC_fg(fg,p,new);
}

Var *lregv[MAXR+1],*first_const,*last_const;
flowgraph *lfg;
static int is_header;

/* re-inserts a cobj into an obj */
static void insert_cobj(obj *o,obj *co)
{
  int dt=o->dtyp,df=o->flags&DREFOBJ;
  *o=*co;
  o->flags|=df;
  o->dtyp=dt;
}

static Var *find_const_var(obj *o,int t)
{
  Var *v;
  for(v=first_const;v;v=v->next){
    if((v->ctyp&NQ)==(t&NQ)&&!compare_objs(o,&v->cobj,t))
      return v;
  }
  return 0;
}

static long cidx;

static Var *add_const_var(obj *o,int t)
{
  Var *v;
  static type vt={VOID},tt;
  int m=o->flags;
  if(o->flags&DREFOBJ){
    t=o->dtyp;
    o->flags&=~DREFOBJ;
  }
  if(v=find_const_var(o,t)){
    o->flags=m;
    return v;
  }
  v=mymalloc(sizeof(*v));
  v->storage_class=AUTO;
  v->cobj=*o;
  v->ctyp=t;
  tt.flags=t;
  if(ISPOINTER(t)) tt.next=&vt;
  v->vtyp=clone_typ(&tt);
  v->next=0;
  v->identifier=" constant";
  v->flags=0;
  v->reg=0;
#ifdef HAVE_TARGET_ATTRIBUTES
  v->tattr=0;
#endif
  cidx++;
  v->offset=l2zm(cidx);
  if(last_const){
      last_const->next=v;
  }else{
      first_const=v;
      const_vars=0;
  }
  last_const=v;
  v->index=vcount-rcount+const_vars;
  const_vars++;
  /* Wir ueberschreiben die Eintraege von vcount-rcount bis vcount. */
  /* Da sie nicht mehr benutzt werden, sollte das ok sein.          */
  vilist=myrealloc(vilist,sizeof(*vilist)*(vcount-rcount+const_vars));
  vilist[vcount-rcount+const_vars-1]=v;
  o->flags=m;
  return v;
}

void create_const_vars(flowgraph *fg)
{
  IC *p;int t;
  Var *v;
  if(DEBUG&1024) printf("creating const-vars\n");
  cidx=0;
  const_vars=0;
  first_const=last_const=0;
  for(;fg;fg=fg->normalout){
    for(p=fg->start;p;p=p->next){
      if(p->q1.flags&(KONST|VARADR)){
	t=q1typ(p);
	v=add_const_var(&p->q1,t);
	p->q1.flags&=~(KONST|VARADR);
	p->q1.flags|=(VAR|VKONST);
	p->q1.v=v;
	p->q1.val.vmax=l2zm(0L);
      }
      if(p->q2.flags&(KONST|VARADR)){
	t=q2typ(p);
	v=add_const_var(&p->q2,t);
	p->q2.flags&=~(KONST|VARADR);
	p->q2.flags|=(VAR|VKONST);
	p->q2.v=v;
	p->q2.val.vmax=l2zm(0L);
      }
      if(p->z.flags&(KONST|VARADR)){
	t=ztyp(p);
	v=add_const_var(&p->z,t);
	p->z.flags&=~(KONST|VARADR);
	p->z.flags|=(VAR|VKONST);
	p->z.v=v;
	p->z.val.vmax=l2zm(0L);
      }
      if(p==fg->end) break;
    }
  }
  if(DEBUG&1024){
    Var *v;
    printf("const-vars:\n");
    for(v=first_const;v;v=v->next){
      printf("%d: ",v->index);
      probj(stdout,&v->cobj,v->ctyp);
      printf(" (t=%d)\n",v->ctyp);
    }
  }
}

void free_const_vars(void)
{
  Var *v,*m;
  if(DEBUG&1024) printf("free_const_vars()\n");
  for(v=first_const;v;){
    m=v->next;
    free(v);
    v=m;
  }
}

#if 0
int free_hreg(flowgraph *fg,IC *p,int reg,int mustr)
/*  Macht das Register reg frei, damit es als lokale Variable im IC p   */
/*  zur Verfuegung steht. Wenn mustr!=0, muss das Register unbedingt    */
/*  freigemacht werden, ansonsten kann davon abgesehen werden.          */
{
    IC *m,*first;Var *v;
    int preg[MAXR+1]={0},calls[MAXR+1]={0},rreg,i;
    first=0;
    v=lregv[reg];
    if(!v)
      ierror(0);
    if(DEBUG&8192) printf("free_hreg %s,%s,%d\n",regnames[reg],v->identifier,mustr);
    if(v->reg&&!*v->identifier) ierror(0);
    for(m=p;m;m=m->next){
      if(m->code==CALL){
	if((m->q1.flags&(VAR|DREFOBJ))==VAR&&m->q1.v->fi&&(m->q1.v->fi->flags&ALL_REGS)){
	  for(i=1;i<=MAXR;i++)
	    if(BTST(m->q1.v->fi->regs_modified,i)) calls[i]++;
	}else{
	  for(i=1;i<=MAXR;i++)
	    if(regscratch[i]) calls[i]++;
	}
      }
      if(m->code==ALLOCREG){
	preg[m->q1.reg]=1;
	if(m->q1.reg==reg) ierror(0);
      }
      if(m->code==FREEREG){
	preg[m->q1.reg]=1;
	if(m->q1.reg==reg) break;
      }
      if(!USEQ2ASZ){
	if((m->q2.flags&VAR)&&m->q2.v==v&&(m->z.flags&(REG|DREFOBJ))==REG&&
	   (!(m->z.flags&VAR)||m->z.v!=v))
	  preg[m->z.reg]=1;
      }
      if(((m->q1.flags&VAR)&&m->q1.v==v)||
	 ((m->q2.flags&VAR)&&m->q2.v==v)||
	 ((m->z.flags&(VAR|DREFOBJ))==(VAR|DREFOBJ)&&m->z.v==v))
	first=m;
      /*        if((m->z.flags&(REG|DREFOBJ))==REG&&m->z.reg==reg) break;*/
    }
    if(!first) {pric(stdout,p);ierror(0);}
    rp.r1=rp.r2=0;
    for(rreg=0,i=1;i<=MAXR;i++){
        if(preg[i]||regu[i]||regsa[i]||!regok(i,v->vtyp->flags,0)) continue;
	if(reg_pair(i,&rp)&&(preg[rp.r1]||preg[rp.r2]||regu[rp.r1]||regu[rp.r2]||regsa[rp.r1]||regsa[rp.r2])) continue;
        if(calls[i]==0&&regscratch[i]){rreg=i;break;}
	if(calls[i]==0) rreg=i;
    }
    if(!rreg&&!mustr) return 0;
    for(m=p;m!=first->next;m=m->next){
        if((m->q1.flags&VAR)&&m->q1.v==v)
            {if(!rreg) m->q1.flags&=~REG; else m->q1.reg=rreg;}
        if((m->q2.flags&VAR)&&m->q2.v==v)
            {if(!rreg) m->q2.flags&=~REG; else m->q2.reg=rreg;}
	if((m->z.flags&VAR)&&m->z.v==v)
	  {if(!rreg) m->z.flags&=~REG; else m->z.reg=rreg;} 
#if 0
	if((m->z.flags&(VAR|DREFOBJ))==VAR&&m->z.v==v)
	  break;
#endif
    }
    if(rreg){
	lregv[rreg]=lregv[reg];
	regused[rreg]=1;regu[rreg]=1;BSET(fg->regused,rreg);
	if(reg_pair(rreg,&rp)){
	    regused[rp.r1]=1;regu[rp.r1]=1;BSET(fg->regused,rp.r1);
	    regused[rp.r2]=1;regu[rp.r2]=1;BSET(fg->regused,rp.r2);
	}
    }
    lregv[reg]=0;regu[reg]=0;
    if(reg_pair(reg,&rp))
	regu[rp.r1]=regu[rp.r2]=0;

    for(m=first->next;m&&m->code==FREEREG;m=m->next){
        if(m->q1.reg==reg){
            if(!rreg) remove_IC_fg(fg,m); else m->q1.reg=rreg;
/*            if(rreg) insert_allocreg(fg,first,FREEREG,rreg);*/
            return rreg;
        }
    }
    insert_allocreg(fg,first->prev,ALLOCREG,reg);
    if(rreg)
      insert_allocreg(fg,first,FREEREG,rreg);
    return rreg;
}
#endif
int replace_local_reg(obj *o)
/*  tested, ob o eine Scratch-Variable ist und ersetzt sie gegebenenfalls   */
{
    int i;Var *v;
    if((o->flags&(VAR|REG|VARADR))==VAR){
        v=o->v;i=v->index;
        if((BTST(lfg->av_kill,i)||is_header)&&!BTST(lfg->av_out,i)){
            for(i=1;i<=MAXR;i++){
                if(lregv[i]==v){
                    o->flags|=(REG|SCRATCH);
/*                    o->flags&=~VAR;*/
                    o->reg=i;
                    return i;
                }
            }
        }
    }
    return 0;
}
void local_combine(flowgraph *fg)
/*  Versucht, Zuweisungen der Form (x)->tmp in ein direkt folgendes IC */
/*  einzugliedern. Nur einfache Tests, da nur loads eliminiert werden  */
/*  sollen, die zum leichten Erkennen per cse explizit wurden.         */
{
  IC *p,*pprev;int i,cl;
  bvtype *used=mymalloc(vsize);
  if(DEBUG&1024) printf("local combining\n");
  for(;fg;fg=fg->normalout){
    memcpy(used,fg->av_out,vsize);
    for(p=fg->end;p;){
      if(p->code==NOP||(!p->q1.flags&&!p->q2.flags)){
	if(p==fg->start) break;
	if(p->z.flags&VAR) BSET(used,p->z.v->index);
	p=p->prev;
	continue;
      }
      pprev=p->prev;
      while(pprev&&pprev->code==NOP) pprev=pprev->prev;
      if(pprev&&p->code==ASSIGN&&zmeqto(p->q2.val.vmax,sizetab[p->typf&NQ])&&(p->q1.flags&(VAR|DREFOBJ))==VAR&&pprev->z.flags==p->q1.flags&&p->q1.v==pprev->z.v&&(!(p->z.flags&VAR)||p->z.v!=p->q1.v)&&ztyp(pprev)==q1typ(p)&&!BTST(used,p->q1.v->index)&&(pprev->code!=ASSIGN||zmeqto(pprev->q2.val.vmax,sizetab[pprev->typf&NQ]))){
	/* x op y ->tmp; move tmp->*p => x op y ->*p */
	if(DEBUG&1024){
	  printf("local combine(3):\n");
	  pric2(stdout,pprev);pric2(stdout,p);
	}
	pprev->z=p->z;
	pprev->change_cnt=p->change_cnt;
	pprev->change_list=p->change_list;
	p->change_cnt=0;
	p->change_list=0;
	pprev->use_list=myrealloc(pprev->use_list,(pprev->use_cnt+p->use_cnt)*VLS);
	memcpy(&pprev->use_list[pprev->use_cnt],p->use_list,p->use_cnt*VLS);
	pprev->use_cnt+=p->use_cnt;
	p->q1.flags=p->z.flags=0;
	p->code=NOP;
	/* restart */
	memcpy(used,fg->av_out,vsize);
	p=fg->end;
	continue;
      }
      if((pprev)&&pprev->code==ASSIGN&&zmeqto(pprev->q2.val.vmax,sizetab[pprev->typf&NQ])
	 &&zmeqto(pprev->q2.val.vmax,sizetab[p->typf&NQ])
	 &&((pprev->q1.flags&DREFOBJ)||(static_cse&&(pprev->q1.flags&(VAR|VARADR))==VAR&&(pprev->q1.v->storage_class==EXTERN||pprev->q1.v->storage_class==STATIC)))&&(pprev->z.flags&(VAR|DREFOBJ))==VAR
	 &&!BTST(used,pprev->z.v->index)&&pprev->z.v->storage_class==AUTO
	 &&!(pprev->z.v->flags&USEDASADR)
	 &&((p->code!=ASSIGN&&p->code!=PUSH)||zmeqto(p->q2.val.vmax,sizetab[p->typf&NQ]))){
	cl=0;
	i=pprev->z.v->index;
	if((p->z.flags&VAR)&&p->z.v->index==i) cl=4;
	if((p->q1.flags&(VAR|DREFOBJ))==VAR){
	  if(p->q1.v->index==i&&zmeqto(p->q1.val.vmax,pprev->z.val.vmax))
	    cl|=1;
	}
	if((p->q2.flags&(VAR|DREFOBJ))==VAR){
	  if(p->q2.v->index==i&&zmeqto(p->q2.val.vmax,pprev->z.val.vmax))
	    cl|=2;
	}
	if(cl==1||cl==2){
	  if(cl!=2||USEQ2ASZ||compare_objs(&pprev->q1,&p->z,p->typf)){
	    if(DEBUG&1024){printf("local combine:\n");pric2(stdout,pprev);pric2(stdout,p);}
	    if(cl==1)
	      p->q1=pprev->q1;
	    else
	      p->q2=pprev->q1;
	    p->use_list=myrealloc(p->use_list,(p->use_cnt+pprev->use_cnt)*VLS);
	    memcpy(&p->use_list[p->use_cnt],pprev->use_list,pprev->use_cnt*VLS);
	    p->use_cnt+=pprev->use_cnt;
	    pprev->code=NOP;
	    pprev->q1.flags=pprev->z.flags=0;
	    pprev->typf=0;
	    /* restart */
	    memcpy(used,fg->av_out,vsize);
	    p=fg->end;
	    continue;
	  }
	}
      }
      if(p==fg->start) break;
      if(p->q1.flags&VAR) BSET(used,p->q1.v->index);
      if(p->q2.flags&VAR) BSET(used,p->q2.v->index);
      if(p->z.flags&VAR) BSET(used,p->z.v->index);
      p=p->prev;
    }
  }
  free(used);
}

/* Fuegt evtl. noetige allocreg/freereg nach local_regs in Block ein. */
/* Kann durch free_hreg noetig werden. */
void fix_local_allocreg(flowgraph *fg)
{
  char regs[MAXR+1]={0};
  IC *p=fg->end;
  while(p){
    if(p->code==ALLOCREG){
      if(!regs[p->q1.reg]) ierror(0);
      regs[p->q1.reg]=0;
    }else if(p->code==FREEREG){
      if(regs[p->q1.reg]){
	ierror(0);
      }
      regs[p->q1.reg]=1;
    }else{
      if((p->q1.flags&REG)&&!regs[p->q1.reg]){
	insert_allocreg(fg,p,FREEREG,p->q1.reg);
	regs[p->q1.reg]=1;
      }
      if((p->q2.flags&REG)&&!regs[p->q2.reg]){
	insert_allocreg(fg,p,FREEREG,p->q2.reg);
	regs[p->q2.reg]=1;
      }
      if((p->z.flags&(REG|DREFOBJ))==(REG|DREFOBJ)&&!regs[p->z.reg]){
	insert_allocreg(fg,p,FREEREG,p->z.reg);
	regs[p->z.reg]=1;
      }
      if((p->z.flags&(REG|DREFOBJ))==REG){
	if(!(p->q1.flags&REG)||p->q1.reg!=p->z.reg){
	  if(!(p->q2.flags&REG)||p->q2.reg!=p->z.reg){
	    IC *p2;
	    for(p2=p->prev;p2;p2=p2->prev){
	      if(p2->code==ALLOCREG&&p2->q1.reg==p->z.reg) break;
	      if(p2->code!=ALLOCREG&&p2->code!=FREEREG) break;
	    }
	    if(p2->code!=ALLOCREG||p2->q1.reg!=p->z.reg){
	      insert_allocreg(fg,p->prev,ALLOCREG,p->z.reg);
	    }
	  }
	}
      }
    }
    if(p==fg->start) return;
    p=p->prev;
  }
}

/* searches back to determine the best local register for v 
   register-pairs must not be alive in the same instruction as
   one of the halves */
int find_best_local_reg(IC *fp,Var *v,int preferred)
{
  int r,used,tmp,savings[MAXR+1]={0};
  IC *p=fp;

  if(v->reg&&!*v->identifier)
    return abs(v->reg);
  if(v->vtyp->flags&VOLATILE)
    return 0;
  if(!USEQ2ASZ&&(p->q2.flags&VAR)&&p->q2.v==v)
    savings[preferred]=INT_MIN;
  else
    savings[preferred]=1;
  if(reg_pair(preferred,&rp)){
    savings[rp.r1]=INT_MIN;
    savings[rp.r2]=INT_MIN;
  }
  for(r=1;r<=MAXR;r++){
    if(regu[r]||regsa[r]==REGSA_NEVER){
      savings[r]=INT_MIN;
      if(reg_pair(r,&rp)){
	savings[rp.r1]=INT_MIN;
	savings[rp.r2]=INT_MIN;
      }
    }else if(reg_pair(r,&rp)){
      if(rp.r1==preferred||rp.r2==preferred||regu[rp.r1]||regsa[rp.r1]==REGSA_NEVER||regu[rp.r2]||regsa[rp.r2]==REGSA_NEVER)
	savings[r]=INT_MIN;
    }
    if(savings[r]!=INT_MIN&&!regok(r,v->vtyp->flags,-1))
      savings[r]=INT_MIN;
  }
  for(r=1;r<=MAXR;r++){
    if(savings[r]!=INT_MIN&&reg_pair(r,&rp)){
      if(regu[rp.r1]||regu[rp.r2]||regsa[rp.r1]==REGSA_NEVER||regsa[rp.r2]==REGSA_NEVER)
	savings[r]=INT_MIN;
    }
  }
  while(1){
    if(!p){
      if(!is_header)
	ierror(0);
      break;
    }
    if(!p||(p!=fp&&p->code>=LABEL&&p->code<=BRA)) return 0;
    if(p!=fp){
      if((p->z.flags&VAR)&&p->z.v->reg&&!*p->z.v->identifier){
	if(p->z.v==v)
	  return abs(p->z.v->reg);
	else
	  savings[abs(p->z.v->reg)]=INT_MIN;
      }
    }
    used=0;
    if((p->q1.flags&VAR)&&p->q1.v==v){
      if(p->code==ADDRESS) return 0;
      if((p->code==ASSIGN||p->code==PUSH)&&(q1typ(p)&NQ)==(CHAR)&&!zmeqto(p->q2.val.vmax,l2zm(1L)))
	return 0;
      used=1;
      for(r=1;r<=MAXR;r++){
	if(savings[r]!=INT_MIN){
	  tmp=cost_savings(p,r,&p->q1);
	  if(tmp==INT_MIN)
	    savings[r]=INT_MIN;
	  else
	    savings[r]+=tmp;
	}
      }
    }
    if((p->q2.flags&VAR)&&p->q2.v==v){
      used=1;
      for(r=1;r<=MAXR;r++){
	if(savings[r]!=INT_MIN){
	  tmp=cost_savings(p,r,&p->q2);
	  if(tmp==INT_MIN)
	    savings[r]=INT_MIN;
	  else
	    savings[r]+=tmp;
	}
      }
    }
    if((p->z.flags&VAR)&&p->z.v==v){
      if(p->code==ASSIGN&&(ztyp(p)&NQ)==(CHAR)&&!zmeqto(p->q2.val.vmax,l2zm(1L)))
	return 0;
      for(r=1;r<=MAXR;r++){
	if(savings[r]!=INT_MIN){
	  tmp=cost_savings(p,r,&p->z);
	  if(tmp==INT_MIN)
	    savings[r]=INT_MIN;
	  else
	    savings[r]+=tmp;
	}
      }
      if(used==0&&!(p->z.flags&DREFOBJ))
	break;
    }
    if(p->code==CALL){
      if((p->q1.flags&(VAR|DREFOBJ))==VAR&&p->q1.v->fi&&(p->q1.v->fi->flags&ALL_REGS)&&!(disable&2048)){
	for(r=1;r<=MAXR;r++){
	  if(regscratch[r]&&BTST(p->q1.v->fi->regs_modified,r))
	    savings[r]=INT_MIN;
	}
      }else{
	for(r=1;r<=MAXR;r++){
	  if(regscratch[r])
	    savings[r]=INT_MIN;
	}
      }
    }

    /* check for register arguments */
    if((p->z.flags&(VAR|DREFOBJ))==VAR&&!*p->z.v->identifier&&p->z.v->reg){
      if(p->code==ASSIGN&&(p->q1.flags&(VAR|DREFOBJ))==VAR&&p->q1.v==v){
	savings[abs(p->z.v->reg)]++;
      }else{
	savings[abs(p->z.v->reg)]=INT_MIN;
	if(reg_pair(abs(p->z.v->reg),&rp)){
	  savings[rp.r1]=INT_MIN;
	  savings[rp.r2]=INT_MIN;
	}
      }
    }

    p=p->prev;
  }
  r=0;
  savings[0]=0;
  for(tmp=1;tmp<=MAXR;tmp++){
    if(savings[tmp]>savings[r]){
      r=tmp;
    }else if(savings[tmp]==savings[r]){
      if(reg_prio[tmp]>reg_prio[r])
	r=tmp;
      else if(reg_prio[tmp]==reg_prio[r]){
	if(regscratch[tmp]&&!regscratch[r])
	  r=tmp;
	else if(regscratch[tmp]==regscratch[r]&&BTST(bregs,r)&&!BTST(bregs,tmp))
	  /* if equal, prefer to leave registers used by parameters */
	  r=tmp;
      }
    }
  }
  return r;
}

static bvtype *late_changes;

void search_late_opt(flowgraph *fg,IC *n,obj *o)
{
  Var *v=o->v;
  IC *p=n->prev,*cp;
  if(!zmeqto(o->val.vmax,Z0)) return;
  if(o->v->reg!=0) return;
  while(1){
    if(!p) ierror(0);
    if(p->code==LABEL) ierror(0);
    if((p->q1.flags&VAR)&&p->q1.v==v) return;
    if((p->q2.flags&VAR)&&p->q2.v==v) return;
    if((p->z.flags&VAR)&&p->z.v==v){
      if(p->z.flags&DREFOBJ) return;
      if(p->code==CONVERT&&!must_convert(p->typf,p->typf2,0)){
	int ucnt=0;
	if((p->q1.flags&DREFOBJ)&&(o->flags&DREFOBJ)) return;
	if(p->q1.flags&VARADR) return;
	if((n->q1.flags&VAR)&&n->q1.v==o->v) ucnt++;
	if((n->q2.flags&VAR)&&n->q2.v==o->v) ucnt++;
	if((n->z.flags&(VAR|DREFOBJ))==(VAR|DREFOBJ)&&n->q1.v==o->v) ucnt++;
	if(ucnt!=1) return;
	memset(late_changes,vsize,0);
	if((p->q1.flags&(VAR|VARADR))==VAR){
	  int i=p->q1.v->index;
	  if(p->q1.flags&DREFOBJ) i+=vcount-rcount;
	  for(cp=p;cp&&cp!=n;cp=cp->next){
	    ic_changes(cp,late_changes);
	    if(BTST(late_changes,i)) return;
	  }
	}
	if(DEBUG&1024){printf("removing CONVERT (!must_convert)\n");pric2(stdout,p);pric2(stdout,n);}
	o->v=p->q1.v;
	o->val.vmax=p->q1.val.vmax;
	if(p->q1.flags&DREFOBJ){
	  o->flags|=DREFOBJ;
	  o->dtyp=p->q1.dtyp;
	}
	n->use_list=myrealloc(n->use_list,VLS*(n->use_cnt+p->use_cnt));
	memcpy(&n->use_list[n->use_cnt],&p->use_list[0],p->use_cnt*VLS);
	n->use_cnt+=p->use_cnt;
	remove_IC_fg(fg,p);
      }
#if 0/* BIGENDIAN||LITTLEENDIAN*/
      if(p->code==CONVERT&&ISINT(p->typf)&&ISINT(p->typf2)&&(p->typf&NQ)<=(p->typf2&NQ)){
	if((p->q1.flags&DREFOBJ)&&(o->flags&DREFOBJ)) return;
	if(DEBUG&1024){printf("removing CONVERT (int=>smallint)\n");pric2(stdout,p);pric2(stdout,n);}
	o->v=p->q1.v;
	o->val.vmax=p->q1.val.vmax;
	if(p->q1.flags&DREFOBJ){
	  o->flags|=DREFOBJ;
	  o->dtyp=p->q1.dtyp;
	}
#if BIGENDIAN
	o->val.vmax=zmadd(o->val.vmax,zmsub(sizetab[p->typf2&NQ],sizetab[p->typf&NQ]));
#endif
	remove_IC_fg(fg,p);
      }
#endif
      return;
    }
    if(p==fg->start) {pric2(stdout,n);ierror(0);}
    p=p->prev;
  }
}


void late_opt(flowgraph *fg)
{
  IC *p;int j;bvtype *isused;
  isused=mymalloc(vsize);
  late_changes=mymalloc(vsize);
  while(fg){
    if(DEBUG&1024) printf("late opt pass block %d\n",fg->index);
    memcpy(isused,fg->av_out,vsize);
    for(p=fg->end;p;p=p->prev){
      if(p->code!=NOP&&p->code!=ADDRESS&&(p->q1.flags&(VAR|VARADR))==VAR){
	j=p->q1.v->index;
	if(BTST(fg->av_kill,j)&&!BTST(fg->av_out,j)&&!BTST(isused,j))
	  search_late_opt(fg,p,&p->q1);
      }
      if(p->code!=NOP&&(p->q2.flags&(VAR|VARADR))==VAR){
	j=p->q2.v->index;
	if(BTST(fg->av_kill,j)&&!BTST(fg->av_out,j)&&!BTST(isused,j))
	  search_late_opt(fg,p,&p->q2);
      }
      if((p->z.flags&(VAR|DREFOBJ))==(VAR|DREFOBJ)){
	j=p->z.v->index;
	if(BTST(fg->av_kill,j)&&!BTST(fg->av_out,j)&&!BTST(isused,j))
	  search_late_opt(fg,p,&p->z);
      }
      if(p==fg->start) break;
      if(p->change_cnt||p->use_cnt)
	av_update(p,isused);
    }
    fg=fg->normalout;
  }
  free(isused);
  free(late_changes);
}

void local_regs(flowgraph *fg)
/*  versucht Variablen, die nur innerhalb eines Basic Blocks benutzt    */
/*  werden (kill==true und out==false), Register zuzuweisen.            */
{
  IC *p;
  int i,j,t,r,nr,mustalloc,savings,prio,nr1,nr2;
  late_opt(fg);
  if(DEBUG&1024) print_flowgraph(fg);
  bvtype *inmem=mymalloc(vsize);
  if(DEBUG&9216) printf("assigning temporary variables to registers\n");
  memset(inmem,0,vsize);
  memset(bregs,0,sizeof(bregs));
  for(i=0;i<=MAXR;i++) lparms[i]=0;
  /* mark registers used by parameters */
  for(p=first_ic;p;p=p->next){
    if((p->q1.flags&VAR)&&p->q1.v->reg) BSET(bregs,abs(p->q1.v->reg));
    if((p->q2.flags&VAR)&&p->q2.v->reg) BSET(bregs,abs(p->q2.v->reg));
    if((p->z.flags&VAR)&&p->z.v->reg) BSET(bregs,abs(p->z.v->reg));
  }
  memcpy(bregsm,bregs,sizeof(bregs));
  lfg=fg;
  while(lfg){
    if(DEBUG&1024) printf("block %d\n",lfg->index);
    for(i=1;i<=MAXR;i++){lregv[i]=0; regu[i]=(regsa[i]==REGSA_NEVER); lfg->regv[i]=0;}
    memset(&lfg->regused,0,RSIZE);
    lfg->calls=0;
    //    if(lfg==fg&&!lfg->in) is_header=1; else is_header=0;
    /* unmark registers used as register arguments */
    memcpy(bregs,bregsm,sizeof(bregs));
    for(p=lfg->start;p;p=p->next){
      if((p->z.flags&VAR)&&!*p->z.v->identifier&&p->z.v->reg!=0) BCLR(bregs,abs(p->z.v->reg));
      if(p==lfg->end) break;
    }
    p=lfg->end;
    while(p){
      nr=nr1=nr2=0;
      i=replace_local_reg(&p->z);
      if((p->z.flags&(VAR|DREFOBJ))==VAR){
	if(i){
	  lregv[i]=0;regu[i]=0;
	  if(reg_pair(i,&rp)){
	    nr1=rp.r1;
	    nr2=rp.r2;
	    regu[nr1]=regu[nr2]=0;
	  }
	  nr=i;mustalloc=1;
	  if(DEBUG&8192) printf("regu[%s] decremented to %d\n",regnames[i],regu[i]);
	}else{
	  BCLR(inmem,p->z.v->index);
	}
      }
      if(p->code!=ADDRESS){
	if(replace_local_reg(&p->q1)==nr) mustalloc=0;
	if(replace_local_reg(&p->q2)==nr) mustalloc=0;
      }
      if((p->q1.flags&(VAR|REG|VARADR))==VAR&&!(p->q1.v->flags&USEDASADR)&&(!(p->q1.v->vtyp->flags&VOLATILE)||p->q1.v->reg)&&(p->q1.v->storage_class==AUTO||p->q1.v->storage_class==REGISTER)){
	j=p->q1.v->index;
	if((BTST(lfg->av_kill,j)||is_header)&&!BTST(lfg->av_out,j)&&!BTST(inmem,j)){
	  r=find_best_local_reg(p,p->q1.v,nr);
	  if(r){
	    if(r!=nr) 
	      insert_allocreg(lfg,p,FREEREG,r);
	    else
	      mustalloc=0;
	    lregv[r]=p->q1.v;regused[r]=regu[r]=1;
	    if(reg_pair(r,&rp)){
	      regu[rp.r1]=regu[rp.r2]=1;
	      regused[rp.r1]=regused[rp.r2]=1;
	    }
	    if(replace_local_reg(&p->q1)!=r) ierror(0);
	    replace_local_reg(&p->q2);
	    replace_local_reg(&p->z);
	    if((DEBUG&9216)&&*p->q1.v->identifier) printf("temporary <%s> assigned to %s (v3)\n",p->q1.v->identifier,regnames[r]);
	    if(DEBUG&8192) printf("temporary <%s>(%p) assigned to %s (v3)\n",p->q1.v->identifier,(void *)p->q1.v,regnames[r]);
	  }else BSET(inmem,j);
	}
      }
      /*  hier wegen USEQ2ASZ aufpassen; kommutative ICs sollten so   */
      /*  angeordnet werden, dass ein evtl. Register rechts steht     */
      if((p->q2.flags&(VAR|REG|VARADR))==VAR&&!(p->q2.v->flags&USEDASADR)&&!(p->q2.v->vtyp->flags&VOLATILE)&&(p->q2.v->storage_class==AUTO||p->q2.v->storage_class==REGISTER)){
	j=p->q2.v->index;
	if((BTST(lfg->av_kill,j)||is_header)&&!BTST(lfg->av_out,j)&&!BTST(inmem,j)){
	  r=find_best_local_reg(p,p->q2.v,nr);
	  if(r){
	    if(r!=nr) insert_allocreg(lfg,p,FREEREG,r);
	    else mustalloc=0;
	    lregv[r]=p->q2.v;regused[r]=regu[r]=1;
	    if(reg_pair(r,&rp)){
	      regu[rp.r1]=regu[rp.r2]=1;
	      regused[rp.r1]=regused[rp.r2]=1;
	    }
	    if(replace_local_reg(&p->q2)!=r) ierror(0);
	    replace_local_reg(&p->z);
	    if((DEBUG&9216)&&*p->q2.v->identifier) printf("temporary <%s> assigned to %s (v1)\n",p->q2.v->identifier,regnames[r]);
	    if(DEBUG&8192) printf("temporary <%s>(%p) assigned to %s (v1)\n",p->q2.v->identifier,(void *)p->q2.v,regnames[r]);
	  }else BSET(inmem,j);
	}
      }
      if((p->z.flags&(VAR|REG|DREFOBJ))==(VAR|DREFOBJ)&&!(p->z.v->flags&USEDASADR)&&!(p->z.v->vtyp->flags&VOLATILE)&&(p->z.v->storage_class==AUTO||p->z.v->storage_class==REGISTER)){
	j=p->z.v->index;
	if((BTST(lfg->av_kill,j)||is_header)&&!BTST(lfg->av_out,j)&&!BTST(inmem,j)){
	  r=find_best_local_reg(p,p->z.v,nr);
	  if(r){
	    insert_allocreg(lfg,p,FREEREG,r);
	    lregv[r]=p->z.v;regused[r]=regu[r]=1;
	    if(reg_pair(r,&rp)){
	      regu[rp.r1]=regu[rp.r2]=1;
	      regused[rp.r1]=regused[rp.r2]=1;
	    }
	    if(replace_local_reg(&p->z)!=r){
	      for(i=1;i<=MAXR;i++) if(lregv[i]) printf("%d:%s=%s(%p)\n",i,regnames[i],lregv[i]->identifier,(void*)lregv[i]);
	      ierror(r);}
	    if((DEBUG&9216)&&*p->z.v->identifier) printf("temporary <%s> assigned to %s (v2)\n",p->z.v->identifier,regnames[r]);
	    if(DEBUG&8192) printf("temporary <%s>(%p) assigned to %s (v2)\n",p->z.v->identifier,(void *)p->z.v,regnames[r]);
	  }else BSET(inmem,j);
	}
      }
      if(p->code==CALL){
	lfg->calls++;
#if 0
	/*  falls Scratchregister bei Funktionsaufruf benutzt   */
	/*  wird, moeglichst auf ein anderes ausweichen         */
	for(i=1;i<=MAXR;i++){
	  if((p->q1.flags&(VAR|DREFOBJ))==VAR&&p->q1.v->fi&&(p->q1.v->fi->flags&ALL_REGS)){
	    if(lregv[i]&&regscratch[i]&&BTST(p->q1.v->fi->regs_modified,i)){
	      int j=lregv[i]->index;
	      if(lregv[i]->reg&&!*lregv[i]->identifier) ierror(0);
	      if(free_hreg(lfg,p,i,1/*0*/)==0) BSET(inmem,j);
	    }		    
	  }else{
	    if(lregv[i]&&regscratch[i]){
	      int j=lregv[i]->index;
	      if(lregv[i]->reg&&!*lregv[i]->identifier) ierror(0);
	      if(free_hreg(lfg,p,i,1/*0*/)==0) BSET(inmem,j);
	    }
	  }
	}
#endif
      }
      /*  die Faelle beachten, wenn schon im IC ein Register          */
      /*  angesprochen wird (sollte nur bei CALL und return auftreten */
      if(p->code==FREEREG){
	ierror(0);
      }
      if(p->code==ALLOCREG){
	ierror(0);
      }
      if(p==lfg->start) i=1; else i=0;
      p=p->prev;
      if(nr&&mustalloc) insert_allocreg(lfg,p,ALLOCREG,nr);
      if(i) break;
    }
    fix_local_allocreg(lfg);
    lfg=lfg->normalout;
    if(is_header){
      for(i=1;i<=MAXR;i++)
	lparms[i]=lregv[i];
    }
  }
  free(inmem);
}
void insert_saves(flowgraph *fg)
/*  fuegt speichern von Registern bei Funktionsaufrufen ein */
{
  int r,c,i;IC *p,*new,*n,*before,*after;Var *v;
  bvtype *isused=mymalloc(vsize);
  if(DEBUG&9216) printf("insert_saves\n");
  for(;fg;fg=fg->normalout){
   memcpy(isused,fg->av_out,vsize);
    p=fg->end;
    while(p){
      c=p->code;
      if(c==CALL){
	if(p==fg->start) n=0; else n=p->prev;
	for(r=1;r<=MAXR;r++){
	  i=0;
	  if((v=fg->regv[r])&&(v->index>=vcount-rcount||BTST(isused,v->index))){
	    if((p->q1.flags&(VAR|DREFOBJ))==VAR&&p->q1.v->fi&&(p->q1.v->fi->flags&ALL_REGS)&&!(disable&2048)){
	      if(regscratch[r]&&BTST(p->q1.v->fi->regs_modified,r)) i=1;
	    }else{
	      if(regscratch[r]) i=1;
	    }
	  }
	  if(i){
	    if(!n) before=fg->start->prev; else before=n;
	    after=p->next;
	    while(after&&(after->code==FREEREG||after->code==ALLOCREG||after->code==NOP))
	      after=after->next;
	    if(!after||after->code!=GETRETURN){
	      after=p;
	    }else{
	      if((after->z.flags&REG)&&after->z.reg==r) after->z.flags&=~REG;
	    }
	    if(v->index>=vcount-rcount){
	      /* it is a const_var; reload instead of store */
	      new=new_IC();
	      new->code=ASSIGN;
	      new->typf=v->vtyp->flags;
	      insert_cobj(&new->q1,&v->cobj);
	      new->q2.flags=0;
	      new->q2.val.vmax=szof(v->vtyp);
	      new->z.flags=REG;
	      new->z.reg=r;
	      new->z.val.vmax=l2zm(0L);
	      new->z.v=v;
	      new->file=p->file;
	      new->line=p->line;
	      /* fake, we do nothing with this afterwards */
	      new->use_cnt=new->change_cnt=0;
	      new->change_list=new->use_list=0;
	      insert_IC_fg(fg,after,new);
	    }else{
	      new=new_IC();
	      new->code=ASSIGN;
	      new->typf=v->vtyp->flags;
              /* vla */
              if(ISARRAY(new->typf))
                new->typf=POINTER_TYPE(v->vtyp->next);
	      new->q1.flags=VAR|REG;
	      new->q1.v=v;
	      new->q1.val.vmax=l2zm(0L);
	      new->q1.reg=r;
	      new->q2.flags=0;
	      new->q2.val.vmax=szof(v->vtyp);
	      new->z.flags=VAR;
	      new->z.val.vmax=l2zm(0L);
	      new->z.v=v;
	      new->file=p->file;
	      new->line=p->line;
	      /* fake, we do nothing with this afterwards */
	      new->use_cnt=new->change_cnt=0;
	      new->change_list=new->use_list=0;
	      insert_IC_fg(fg,before,new);
	      new=new_IC();
	      new->code=ASSIGN;
	      new->typf=v->vtyp->flags;
              /* vla */
              if(ISARRAY(new->typf))
                new->typf=POINTER_TYPE(v->vtyp->next);
	      new->q1.flags=VAR;
	      new->q1.v=v;
	      new->q1.val.vmax=l2zm(0L);
	      new->q2.flags=0;
	      new->q2.val.vmax=szof(v->vtyp);
	      new->z.flags=VAR|REG;
	      new->z.v=v;
	      new->z.val.vmax=l2zm(0L);
	      new->z.reg=r;
	      new->file=p->file;
	      new->line=p->line;
	      /* fake, we do nothing with this afterwards */
	      new->use_cnt=new->change_cnt=0;
	      new->change_list=new->use_list=0;
	      insert_IC_fg(fg,after,new);
	    }
	  }
	}
	if(p->change_cnt!=0||p->use_cnt!=0)
	  av_update(p,isused);
	p=n;
	continue;
      }
      if(p==fg->start) break;
      if(p->change_cnt!=0||p->use_cnt!=0)
	av_update(p,isused);
      p=p->prev;
    }
  }
  free(isused);
}


#endif

void insert_simple_allocreg(IC *p,int code,int reg)
/*  Fuegt ein ALLOCREG/FREEREG (in code) hinter p ein - bei p==0 in */
/*  first_ic.                                                       */
{
    IC *new=new_IC();
    new->line=0;
    new->file=0;
    regused[reg]=1;
    if(reg_pair(reg,&rp))
	regused[rp.r1]=regused[rp.r2]=1;
    new->code=code;
    new->typf=0;
    new->q1.am=new->q2.am=new->z.am=0;
    new->q1.flags=REG;
    new->q1.reg=reg;
    new->q2.flags=new->z.flags=0;
    new->use_cnt=new->change_cnt=0;
    new->use_list=new->change_list=0;
    insert_IC(p,new);
}

void load_simple_reg_parms(void)
/*  Laedt Registerparameter, falls noetig. Nicht-optimierende Version.  */
{
  int i,j; Var *v;
  regp regp[MAXR+1]={0};
/*   for(i=1;i<=MAXR;i++) {regp[i].treg=0;regp[i].tvar=0;} */
  for(i=0;i<=1;i++){
    if(i==0) v=vl3; else v=vl2;
    for(;v;v=v->next){
      if((v->flags&REGPARM)&&(regsv[abs(v->reg)]!=v||(v->flags&CONVPARAMETER))){
	regp[abs(v->reg)].tvar=v;
	for(j=1;j<=MAXR;j++)
	  if(regsv[j]==v) regp[abs(v->reg)].treg=j;
      }
    }
  }
  do_load_parms(regp,0);
}

extern zmax recalc_start_offset;

void do_load_parms(regp regp[],flowgraph *fg)
{
  int i,i1,i2,j,j1,j2,c,k,notdone;
  struct {int freg,treg;Var *tvar,*tmp;} order[MAXR]={0};
  if(DEBUG&1){
    printf("do_load_parms:\n");
    for(i=1;i<=MAXR;i++)
      if(regp[i].tvar)
	printf("%s->%s(%s)\n",regnames[i],regnames[regp[i].treg],regp[i].tvar->identifier);
  }
  do{
    c=0;
    do{
      notdone=0;
      for(i=1;i<=MAXR;i++){
	int notfree;
	if(reg_pair(i,&rp)){
	  i1=rp.r1;
	  i2=rp.r2;
	}else
	  i1=i2=0;
	if(!regp[i].tvar) continue;
	j=regp[i].treg;
	if(j&&reg_pair(j,&rp)){
	  j1=rp.r1;
	  j2=rp.r2;
	}else
	  j1=j2=0;
	notfree=0;
	for(k=1;k<=MAXR;k++){
	  if(regp[k].tvar&&reg_pair(k,&rp)&&(rp.r1==j||rp.r1==j1||rp.r1==j2||rp.r2==j||rp.r2==j1||rp.r2==j2)){
	    notfree=1;
	    break;
	  }
	}
	if(j==0||(!notfree&&!regp[j].tvar&&(!j1||!regp[j1].tvar)&&(!j2||!regp[j2].tvar))||i==j||regp[i].tmp){
	  order[c].freg=i;
	  order[c].treg=j;
	  order[c].tvar=regp[i].tvar;
	  if(regp[i].tmp){order[c].treg=0;order[c].tvar=regp[i].tmp;}
	  c++; notdone=1;
	  regp[i].treg=0;
	  regp[i].tvar=0;
	}
      }
    }while(notdone);
    for(i=c-1;i>=0;i--)
      load_one_parm(order[i].freg,order[i].treg,0,order[i].tvar,fg);
    notdone=0;
    /* first try to spill register-pair (probably suboptimal, but should
       avoid a deadlock) */
    for(i=1;i<=MAXR;i++){
      if(regp[i].tvar&&reg_pair(i,&rp))
	break;
    }
    if(i>MAXR){
      for(i=1;i<=MAXR;i++){
	if(regp[i].tvar)
	  break;
      }
    }
    if(i<=MAXR){
      static type dtyp={DOUBLE};
      if(!regp[i].tvar) ierror(0);
      if(regp[i].tvar->flags&CONVPARAMETER)
	regp[i].tmp=add_tmp_var(clone_typ(&dtyp));
      else
	regp[i].tmp=add_tmp_var(clone_typ(regp[i].tvar->vtyp));
      /* num_vars is already done, so mark it by -1 */
      regp[i].tmp->index=-1;
      /* allocate memory for recalc_offsets */
#ifndef NO_OPTIMIZER
      if(fg){
	regp[i].tmp->offset=l2zm(0L);
	if(zmleq(recalc_start_offset,regsize[i]))
	  recalc_start_offset=regsize[i];
      }
#endif
      load_one_parm(0,regp[i].treg,regp[i].tmp,regp[i].tvar,fg);
      notdone=1;
    }
  }while(notdone);
}
void load_one_parm(int freg,int treg,Var *fvar,Var *tvar,flowgraph *fg)
{
  IC *new;
  if(DEBUG&1) printf("lop: %s(%s)->%s(%s)\n",regnames[freg],fvar?fvar->identifier:empty,regnames[treg],tvar?tvar->identifier:empty);
  if(freg&&freg!=treg){
    if(fg){
#ifndef NO_OPTIMIZER
      insert_allocreg(fg,0,FREEREG,freg);
#endif
    }else
      insert_simple_allocreg(0,FREEREG,freg);
  }
  new=new_IC();
  new->line=0;
  new->file=0;
  if(tvar->flags&CONVPARAMETER){
    new->code=CONVERT;
    new->typf=tvar->vtyp->flags;
    new->typf2=DOUBLE;
  }else{
    new->code=ASSIGN;
    new->typf=tvar->vtyp->flags;
  }
  if(ISFLOAT(new->typf)) float_used=1;
  if(fvar){
    new->q1.flags=VAR;
    new->q1.v=fvar;
    new->q1.val.vmax=l2zm(0L);
  }else{
    new->q1.flags=REG;
    new->q1.reg=freg;
  }
  new->q2.flags=0;
  new->q2.val.vmax=szof(tvar->vtyp);
  if(treg)
    new->z.flags=REG|VAR;
  else
    new->z.flags=VAR;
  new->z.val.vmax=l2zm(0L);
  new->z.v=tvar;
  new->z.reg=treg;
  new->q1.am=new->q2.am=new->z.am=0;
  new->use_cnt=new->change_cnt=0;
  new->use_list=new->change_list=0;
  if((new->typf&NQ)==CHAR&&!regok(new->q1.reg,new->typf,0)){
    new->code=CONVERT;
    new->typf2=INT;
  }
  if(fg){
#ifndef NO_OPTIMIZER
    insert_IC_fg(fg,0,new);
    if(freg&&freg!=treg) insert_allocreg(fg,0,ALLOCREG,freg);
#endif
  }else{
    insert_IC(0,new);
    if(freg&&freg!=treg) insert_simple_allocreg(0,ALLOCREG,freg);
  }
  if(new->z.flags&REG){
    /*  ALLOCREG verschieben    */
    IC *p;
    if(fg){
#ifndef NO_OPTIMIZER
      insert_allocreg(fg,0,ALLOCREG,treg);
#endif
    }else
      insert_simple_allocreg(0,ALLOCREG,treg);
    for(p=new->next;p;p=p->next){
      if(p->code==ALLOCREG&&p->q1.reg==treg){
	if(fg){
#ifndef NO_OPTIMIZER
	  remove_IC_fg(fg,p);
#endif
	}else
	  remove_IC(p);
	break;
      }
    }
    if(!p) ierror(0);
  }
}

void simple_regs(void)
/*  haelt Variablen in Registern, simple Version            */
{
  int i2,i,j;int pri;Var *v;
  IC *icp,*start=first_ic;
  if(!first_ic) return;
  for(i=1;i<=MAXR;i++) regsv[i]=0;
  for(i2=0;i2<=MAXR*4;i2++){
    int only_best,pointertype=0;
    if(i2<=MAXR*2){i=i2;only_best=1;} else {i=i2/2;pointertype=only_best=0;}
    if(i<=MAXR&&regsv[i]) continue;
    if(i>MAXR&&regsv[i-MAXR]) continue;
    if(i>MAXR||!regsv[i]){
      if(i>MAXR){
	i-=MAXR;
	if(regsv[i]) continue;
      }else{
	/*  Ziehe Scratchregister vor, wenn kein Funktionsaufruf */
	/*  erfolgt, sonst erst andere                           */
	if(!regscratch[i]) continue;
      }
      if(regused[i]) continue;
      if(sregsa[i]==REGSA_TEMPS) continue;
      if(reg_pair(i,&rp)){
	if(regused[rp.r1]||regused[rp.r2]) continue;
      }
      if(simple_scratch[i]) continue;
      /*pri=2;*/ pri=0;
      for(j=0;j<=1;j++){
	if(j==0) v=vl3; else v=vl2;
	while(v){
	  if(v->storage_class==AUTO||v->storage_class==REGISTER){
	    if(!(v->flags&USEDASADR)&&!(v->vtyp->flags&VOLATILE)){
              int vt=v->vtyp->flags&NU;
              /* vla */
              if(ISARRAY(vt)&&is_vlength(v->vtyp))
                vt=POINTER_TYPE(v->vtyp->next);
	      if(only_best&&v->vtyp->next) pointertype=v->vtyp->next->flags;
	      if(v->priority>pri&&regok(i,vt,pointertype)){
		regsv[i]=v;pri=v->priority;
	      }
	    }
	  }
	  v=v->next;
	}
      }
    }
    if(regsv[i]){
      if(DEBUG&1) printf("Assigned <%s>(%p) to %s,%d\n",regsv[i]->identifier,(void *)regsv[i],regnames[i],i);
      regsv[i]->priority=0;regused[i]=1;
      if(reg_pair(i,&rp)) {regused[rp.r1]=regused[rp.r2]=1;}
      if(!zmleq(l2zm(0L),regsv[i]->offset)&&!(regsv[i]->flags&(CONVPARAMETER|REGPARM))){
	icp=new_IC();
	icp->line=0;
	icp->file=0;
	icp->q1.am=icp->q2.am=icp->z.am=0;
	icp->code=ASSIGN;
	icp->typf=regsv[i]->vtyp->flags&NU;
	icp->q1.flags=VAR;
	icp->q1.v=regsv[i];
	icp->q1.val.vmax=l2zm(0L);
	icp->q2.flags=0;
	icp->q2.val.vmax=szof(regsv[i]->vtyp);
	icp->z.flags=REG;
	icp->z.reg=i;
	icp->next=first_ic;
	icp->prev=0;
	first_ic->prev=icp;
	first_ic=icp;
      }
      icp=new_IC();
      icp->line=0;
      icp->file=0;
      icp->q1.am=icp->q2.am=icp->z.am=0;
      icp->code=ALLOCREG;
      icp->q1.flags=REG;
      icp->q1.reg=i;
      icp->q2.flags=icp->z.flags=icp->typf=0;
      icp->next=first_ic;
      icp->prev=0;
      first_ic->prev=icp;
      first_ic=icp;
      icp=new_IC();
      icp->q1.am=icp->q2.am=icp->z.am=0;
      icp->code=FREEREG;
      icp->q1.flags=REG;
      icp->q1.reg=i;
      icp->q2.flags=icp->z.flags=icp->typf=0;
      icp->next=0;
      add_IC(icp);
    }
  }
  icp=start;
  while(icp){
    if((icp->code==ALLOCREG||icp->code==FREEREG)&&regsv[icp->q1.reg]){
      /*  irgendwelche allocreg/freereg im Code entfernen     */
      /*  sollte nur beim Returnregister vorkommen            */
      IC *m=icp->next;
      remove_IC(icp);
      icp=m;continue;
    }
    for(i=1;i<=MAXR;i++){
      if(!regsv[i]) continue;
      if((icp->q1.flags&(VAR|DONTREGISTERIZE))==VAR&&icp->q1.v==regsv[i]){
	icp->q1.flags|=REG;
	icp->q1.reg=i;
      }
      if((icp->q2.flags&(VAR|DONTREGISTERIZE))==VAR&&icp->q2.v==regsv[i]){
	icp->q2.flags|=REG;
	icp->q2.reg=i;
      }
      if((icp->z.flags&(VAR|DONTREGISTERIZE))==VAR&&icp->z.v==regsv[i]){
	icp->z.flags|=REG;
	icp->z.reg=i;
      }
    }
    icp=icp->next;
  }
}


