/*  $VER: vbcc (loop.c) V0.8     */
/*  schleifenorientierte Optimierungen  */

#include "opt.h"

static char FILE_[]=__FILE__;

#define MOVE_IC 1
#define MOVE_COMP 2

/*  Liste, in die ICs eingetragen werden, die aus Schleifen */
/*  gezogen werden sollen.                                  */
struct movlist{
  struct movlist *next;
  struct IC *IC;
  struct flowgraph *target_fg;
  int flags;
};

struct movlist *first_mov,*last_mov;

int report_weird_code,report_suspicious_loops;

/*  Bitvektoren fuer schleifeninvariante ICs    */
bvtype *invariant,*inloop,*moved,*moved_completely;
bvtype *fg_tmp;
bvtype *not_movable;
size_t bsize;


/*  Liste, in die ICs fuer strength-reduction eingetragen   */
/*  werden.                                                 */
struct srlist{
  struct srlist *next;
  struct IC *ind_var;
  struct IC *IC;
  struct flowgraph *target_fg;
  /*  Hilfsvariable, falls eine aequivalente Operation schon reduziert    */
  /*  wurde.                                                              */
  struct Var *hv;
};

struct srlist *first_sr,*last_sr;

/*  Liste, in die Daten fuer loop-unrolling eingetragen werden. */
struct urlist{
  int flags;
  long total,unroll;
  struct IC *cmp,*branch,*ind;
  struct flowgraph *start,*head;
  struct urlist *next;
} *first_ur;

#define UNROLL_COMPLETELY 1
#define UNROLL_MODULO 2
#define UNROLL_INVARIANT 4
#define UNROLL_REVERSE 8
#define IND_ONLY_COUNTS 16
#define MULTIPLE_EXITS 32

/*  Hier werden Induktionsvariablen vermerkt    */
struct IC **ind_vars;

static struct flowgraph *first_fg;

#ifdef ALEX_REG
void IncrementLoopDepth(struct flowgraph* fg,int start,int end)
     /* erhoeht loop_depth in den Bloecken [start,end] */
{
  if ( (fg->index >= start) && (fg->index <= end) )
    {
      fg->loop_depth++;
    }
  if (fg->normalout)
    IncrementLoopDepth(fg->normalout,start,end);
}
#endif

int loops(struct flowgraph *fg,int footers)
/*  kennzeichnet Schleifen im Flussgraph; wenn footers!=0 werden darf eine  */
/*  Schleife nur einen gemeinsamen Austrittspunkt haben                     */
{
  int i,start,end,c=0;struct flowlist *lp;struct flowgraph *g,*loopend;
  if(DEBUG&1024) printf("searching loops\n");
  g=fg;
  while(g){
    start=g->index;
    end=-1;
    for(lp=g->in;lp;lp=lp->next){
      if(!lp->graph) continue;
      if(lp->graph->branchout==g||!lp->graph->end||lp->graph->end->code!=BRA){
	i=lp->graph->index;
	if(i>=start&&i>end){ end=i;loopend=lp->graph; }
      }
    }
    if(end>=0){
      /*  Schleife oder etwas aehnliches  */
      struct flowgraph *p=g;
      if(DEBUG&1024) printf("found possible loop from blocks %d to %d\n",start,end);
      if(1/*goto_used*/){
	if(DEBUG&1024) printf("have to check...\n");
	do{
	  if(!p||p->index>end) break;

	  /*  testen, ob aus der Schleife gesprungen wird */
	  if(p->branchout&&footers){
	    i=p->branchout->index;
	    if(i<start){
	      end=-1;
	      break;
	    }
	    if(i>end&&(DEBUG&1024)){
	      puts("jump out of loop");
	      if(p->branchout!=loopend->normalout){
		puts("no break");
		if(p->branchout->start->typf!=return_label) puts("no return");
	      }
	    }
	    if(i>end&&p->branchout!=loopend->normalout&&p->branchout->start->typf!=return_label){
	      /*  Sprung zu anderem als dem normalen Austritt oder return */
	      end=-1;
	      break;
	    }
	  }
	  /*  testen, ob in die Schleife gesprungen wird  */
	  if(p!=g){
	    for(lp=p->in;lp;lp=lp->next){
	      if(!lp->graph) continue;
	      if(lp->graph->branchout==p){
		i=lp->graph->index;
		if(i<start){
		  if(report_weird_code){error(175);report_weird_code=0;}
		  end=-1;
		  break;
		}
		if(i>end){
		  end=-1;
		  break;
		}
	      }
	    }
	  }
	  if(p->index==end) break;
	  p=p->normalout;
	}while(end>=0);
      }else{
	if(DEBUG&1024) printf("must be a loop, because there was no goto\n");
      }
    }
    if(end>=0){
      if(DEBUG&1024) printf("confirmed that it is a loop\n");
      g->loopend=loopend;
      c++;
#ifdef ALEX_REG
      IncrementLoopDepth(fg,start,end);
#endif
    }
    g=g->normalout;
  }
  return c;
}

struct flowgraph *create_loop_headers(struct flowgraph *fg,int av)
/*  Fuegt vor jede Schleife einen Kopf-Block ein, wenn noetig.  */
/*  Wenn av!=0 werden aktive Variablen korrekt uebertragen und  */
/*  diverse Registerlisten uebernommen und index=-1 gesetzt.    */
/*  Kann einen Block mehrmals in der ->in Liste eintragen       */
{
  struct flowgraph *g,*last,*new,*rg=fg;
  struct IC *lic,*lastic;
  if(DEBUG&1024) printf("creating loop-headers\n");
  g=fg;last=0;lastic=0;
  while(g){
    new=0;
    if(g->loopend){
      if(!last){
	struct flowlist *lp;
	new=new_flowgraph();
	rg=new;
	new->in=0;
	lic=new_IC();
	lic->code=LABEL;
	lic->typf=++label;
	lic->q1.flags=lic->q2.flags=lic->z.flags=0;
	lic->q1.am=lic->q2.am=lic->z.am=0;
	new->start=new->end=lic;
	lic->next=first_ic;
	lic->prev=0;
	first_ic->prev=lic;
	first_ic=lic;
	lp=mymalloc(sizeof(struct flowlist));
	lp->graph=new;
	lp->next=g->in;
	g->in=lp;
      }else{
	struct flowlist *lp,*nl,**ls;
	new=new_flowgraph();
	last->normalout=new;
	lic=new_IC();
	new->start=new->end=lic;
	lic->code=LABEL;
	lic->typf=++label;
	lic->q1.flags=lic->q2.flags=lic->z.flags=0;
	lic->q1.am=lic->q2.am=lic->z.am=0;
	if(lastic) lastic->next=lic;
	else   first_ic=lic;
	lic->prev=lastic;
	g->start->prev=lic;
	lic->next=g->start;
	lp=g->in;ls=&new->in;
	while(lp){
	  if(lp->graph&&lp->graph->index<g->index){
	    /*  Eintritt von oben soll in den Kopf  */
	    nl=mymalloc(sizeof(struct flowlist));
	    nl->graph=lp->graph;
	    nl->next=0;
	    (*ls)=nl;
	    ls=&nl->next;
	    if(lp->graph->branchout==g){
	      struct IC *p=lp->graph->end;
	      if(DEBUG&1024) printf("changing branch\n");
	      while(p&&p->code==FREEREG) p=p->prev;
	      if(!p||p->code<BEQ||p->code>BRA) ierror(0);
	      p->typf=lic->typf;
	      lp->graph->branchout=new;
	    }
	    lp->graph=new;
	  }
	  lp=lp->next;
	}
	if(!new->in) ierror(0);
      }
      if(new){
	if(DEBUG&1024) printf("must insert loop-header before block %d\n",g->index);
	basic_blocks++;
	new->branchout=0;
	new->loopend=0;
	if(av)
	  new->index=-1;
	else
	  new->index=basic_blocks;
	new->normalout=g;
	new->calls=0;
	new->loop_calls=0;
	new->rd_in=new->rd_out=new->rd_kill=new->rd_gen=0;
	new->ae_in=new->ae_out=new->ae_kill=new->ae_gen=0;
	new->cp_in=new->cp_out=new->cp_kill=new->cp_gen=0;
	if(!av){
	  new->av_in=new->av_out=new->av_kill=new->av_gen=0;
	}else{
	  new->av_in=mymalloc(vsize);
	  new->av_out=mymalloc(vsize);
	  new->av_gen=mymalloc(vsize);
	  new->av_kill=mymalloc(vsize);
	  memset(new->av_gen,0,vsize);
	  memset(new->av_kill,0,vsize);
	  memcpy(new->av_out,g->av_in,vsize);
	  memcpy(new->av_in,g->av_in,vsize);
	  memcpy(&new->regv,&g->regv,sizeof(new->regv));
	  memcpy(&new->regused,&g->regused,sizeof(new->regused));
	}
      }
    }
    last=g;if(last->end) lastic=last->end;
    g=g->normalout;
  }
  return rg;
}
struct flowgraph *create_loop_footers(struct flowgraph *fg,int av)
/*  Fuegt hinter jede Schleife einen Fuss-Block ein, wenn noetig.   */
/*  Wenn av!=0 werden aktive Variablen korrekt uebertragen und      */
/*  diverse Registerlisten uebernommen und index auf -2 gesetzt.    */
{
  struct flowgraph *g,*loopend,*out,*new;
  struct IC *lic;
  if(DEBUG&1024) printf("creating loop-footers\n");
  g=fg;
  while(g){
    new=0;
    loopend=g->loopend;
    if(loopend){
      struct flowlist *lp,*nl,**ls;
      out=loopend->normalout;
      new=new_flowgraph();
      new->normalout=out;
      loopend->normalout=new;
      lic=new_IC();
      lic->line=0;
      lic->file=0;
      new->start=new->end=lic;
      lic->code=LABEL;
      lic->typf=++label;
      lic->q1.flags=lic->q2.flags=lic->z.flags=0;
      lic->q1.am=lic->q2.am=lic->z.am=0;
      lic->use_cnt=lic->change_cnt=0;
      lic->use_list=lic->change_list=0;
      if(out) lp=out->in; else {lp=0;new->in=0;}
      ls=&new->in;
      while(lp){
	if(lp->graph&&lp->graph->index<=loopend->index&&lp->graph->index>=g->index){
	  /*  Austritt aus Schleife soll in den Fuss  */
	  nl=mymalloc(sizeof(struct flowlist));
	  nl->graph=lp->graph;
	  nl->next=0;
	  (*ls)=nl;
	  ls=&nl->next;
	  if(lp->graph->branchout==out){
	    struct IC *p=lp->graph->end;
	    if(DEBUG&1024) printf("changing branch\n");
	    while(p&&p->code==FREEREG) p=p->prev;
	    if(!p||p->code<BEQ||p->code>BRA) ierror(0);
	    p->typf=lic->typf;
	    lp->graph->branchout=new;
	  }
	  lp->graph=new;
	}
	lp=lp->next;
      }
      if(out&&!new->in) ierror(0);
      if(DEBUG&1024) printf("must insert loop-footer after block %d\n",loopend->index);
      basic_blocks++;
      new->branchout=0;
      new->loopend=0;
      if(av)
	new->index=-2;
      else
	new->index=basic_blocks;
      new->normalout=out;
      new->calls=0;
      new->loop_calls=0;
      new->rd_in=new->rd_out=new->rd_kill=new->rd_gen=0;
      new->ae_in=new->ae_out=new->ae_kill=new->ae_gen=0;
      new->cp_in=new->cp_out=new->cp_kill=new->cp_gen=0;
      if(!av){
	new->av_in=new->av_out=new->av_kill=new->av_gen=0;
      }else{
	new->av_in=mymalloc(vsize);
	new->av_out=mymalloc(vsize);
	new->av_kill=mymalloc(vsize);
	new->av_gen=mymalloc(vsize);
	memset(new->av_gen,0,vsize);
	memset(new->av_kill,0,vsize);
	if(out){
	  memcpy(new->av_out,out->av_in,vsize);
	  memcpy(new->av_in,out->av_in,vsize);
	}else{
	  memcpy(new->av_out,av_globals,vsize);
	  bvunite(new->av_out,av_statics,vsize);
	  memcpy(new->av_in,new->av_out,vsize);
	}
	memcpy(&new->regv,&g->regv,sizeof(new->regv));
	memcpy(&new->regused,&g->regused,sizeof(new->regused));
      }
      insert_IC_fg(new,loopend->end,lic);
    }
    g=g->normalout;
  }
  return fg;
}
void add_movable(struct IC *p,struct flowgraph *fg,int flags)
/*  Fuegt IC p, das aus der Schleife in Block fg mit Flags flags    */
/*  verschoben werden darf in eine Liste.                           */
{
  struct movlist *new=mymalloc(sizeof(*new));
  new->IC=p;
  new->target_fg=fg;
  new->flags=flags;
  new->next=0;
  if(last_mov){
    last_mov->next=new;
    last_mov=new;
  }else{
    first_mov=last_mov=new;
  }
  BSET(moved,p->defindex);
  if(flags==MOVE_IC) BSET(moved_completely,p->defindex);
}
int move_to_head(void)
/*  Geht die Liste mit verschiebbaren ICs durch und schiebt die ICs */
/*  in den Vorkopf der Schleife. Ausserdem wird die Liste           */
/*  freigegeben.                                                    */
/*  Der Rueckgabewert hat Bit 1 gesetzt, wenn ICs ganz verschoben   */
/*  wurden und Bit 2, falls eine Berechnung mit Hilfsvariable vor   */
/*  die Schleife gezogen wurde.                                     */
{
  struct IC **fglist; /* Letztes IC vor jedem Block   */
  struct flowgraph *g;struct IC *p;struct movlist *m;
  int changed=0;

  if(!first_mov) return 0;

  if(DEBUG&1024) printf("moving the ICs out of the loop\n");

  fglist=mymalloc((basic_blocks+1)*sizeof(*fglist));
  p=0;
  for(g=first_fg;g;g=g->normalout){
    if(g->index>basic_blocks) ierror(0);
    if(g->end) p=g->end;
    fglist[g->index]=p;
  }
  while(first_mov){
    p=first_mov->IC;
    g=first_mov->target_fg;
    if(first_mov->flags==MOVE_IC){
      if(DEBUG&1024) {printf("moving IC out of loop:\n");pric2(stdout,p);}
      if(!p->prev||!p->next) ierror(0);
      p->next->prev=p->prev;
      p->prev->next=p->next;
      insert_IC_fg(g,fglist[g->index],p);
      fglist[g->index]=p;
      changed|=1;
    }else if(1){
      struct Typ *t=new_typ();
      struct IC *new=new_IC();
      struct Var *v;
      if(DEBUG&1024) {printf("moving computation out of loop:\n");pric2(stdout,p);}
      t->flags=ztyp(p);
      if(p->code==COMPARE||p->code==TEST) t->flags=0;
      if(ISPOINTER(t->flags)){
	t->next=new_typ();
	t->next->flags=VOID;
      }
      v=add_tmp_var(t);
      *new=*p;
      new->z.flags=VAR;
      new->z.v=v;
      new->z.val.vmax=l2zm(0L);

      /*  Die neue Operation benutzt maximal, was die andere benutzte */
      /*  und aendert nur die Hilfsvariable.                          */
      if(have_alias){
	new->use_cnt=p->use_cnt;
	new->use_list=mymalloc(new->use_cnt*VLS);
	memcpy(new->use_list,p->use_list,new->use_cnt*VLS);
	new->change_cnt=1;
	new->change_list=mymalloc(VLS);
	new->change_list[0].v=v;
	new->change_list[0].flags=0;
      }
      insert_IC_fg(g,fglist[g->index],new);
      fglist[g->index]=new;
      p->code=ASSIGN;
      p->typf=t->flags;
      p->q1.flags=VAR;
      p->q1.v=v;
      p->q1.val.vmax=l2zm(0L);
      p->q2.flags=0;
      p->q2.val.vmax=szof(t);
      /*  Die Operation in der Schleife benutzt nun zusaetzlich   */
      /*  noch die Hilfsvariable.                                 */
      if(have_alias){
	void *m=p->use_list;
	p->use_cnt++;
	p->use_list=mymalloc(p->use_cnt*VLS);
	memcpy(&p->use_list[1],m,(p->use_cnt-1)*VLS);
	free(m);
	p->use_list[0].v=v;
	p->use_list[0].flags=0;
      }
      changed|=2;
    }
    m=first_mov->next;
    free(first_mov);
    first_mov=m;
  }
  if(DEBUG&1024) print_flowgraph(first_fg);
  free(fglist);
  return changed;
}
void calc_movable(struct flowgraph *start,struct flowgraph *end)
/*  Berechnet, welche Definitionen nicht aus der Schleife start-end     */
/*  verschoben werden duerfen. Eine Def. p von z darf nur verschoben    */
/*  werden, wenn keine andere Def. von p existiert und alle             */
/*  Verwendungen von z nur von p erreicht werden.                       */
/*  Benutzt rd_defs.                                                    */
{
  struct flowgraph *g;struct IC *p;
  int i,j,k,d;
  bvtype *changed_vars;
  if(DEBUG&1024) printf("calculating not_movable for blocks %d to %d\n",start->index,end->index);
  if(0/*!(optflags&1024)*/){
    memset(not_movable,UCHAR_MAX,dsize);
    return;
  }
  memset(not_movable,0,dsize);
  changed_vars=mymalloc(vsize);
  memset(changed_vars,0,vsize);
  for(i=0;i<vcount-rcount;i++){
    if(vilist[i]->vtyp->flags&VOLATILE) BSET(changed_vars,i);
    if(i<rcount){
      if(!vilist[i]->vtyp->next||(vilist[i]->vtyp->next->flags&VOLATILE)) BSET(changed_vars,i+vcount-rcount);
    }
  }
  for(g=start;g;g=g->normalout){
    if(!g->rd_in) ierror(0);
    memcpy(rd_defs,g->rd_in,dsize);
    for(p=g->start;p;p=p->next){
      for(j=0;j<p->change_cnt;j++){
	i=p->change_list[j].v->index;
	if(p->change_list[j].flags&DREFOBJ) i+=vcount-rcount;
	if(i>=vcount) continue;
	if(BTST(changed_vars,i)||is_volatile_ic(p)){
	  bvunite(not_movable,var_defs[i],dsize);
	}else{
	  BSET(changed_vars,i);
	}
      }
      for(k=0;k<p->use_cnt;k++){
	i=p->use_list[k].v->index;
	if(p->use_list[k].flags&DREFOBJ) i+=vcount-rcount;
	if(i>=vcount) continue;
	for(d=-1,j=1;j<=dcount;j++){
	  if(BTST(rd_defs,j)&&BTST(var_defs[i],j)){
	    if(d>=0){  /*  mehr als eine Def.  */
	      bvunite(not_movable,var_defs[i],dsize);
	      d=-1;break;
	    }else d=j;
	  }
	  if(BTST(rd_defs,UNDEF(j))&&BTST(var_defs[i],UNDEF(j))){
	    bvunite(not_movable,var_defs[i],dsize);
	    d=-1;break;
	  }
	}
      }
      /*  Das hier, um rd_defs zu aktualisieren.  */
      rd_change(p);
      if(p==g->end) break;
    }
    if(g==end) break;
  }
  free(changed_vars);
}
/*  Testet, ob Variable nur in der Schleife benutzt wird.               */
/* could be improved */
int used_in_loop_only(struct flowgraph *start,struct flowgraph *end,struct obj *o)
{
  struct Var *v;struct flowgraph *g;struct IC *p;
  if((o->flags&(VAR|DREFOBJ))!=VAR) return 0;
  v=o->v;
  if((v->flags&USEDASADR)||v->nesting==0||v->storage_class==EXTERN||v->storage_class==STATIC)
    return 0;
  for(g=first_fg;g;g=g->normalout){
    if(g==start) g=end->normalout;
    if(!g) break;
    for(p=g->start;p;p=p->next){
      if((p->q1.flags&VAR)&&p->q1.v==v) return 0;
      if((p->q2.flags&VAR)&&p->q2.v==v) return 0;
      if((p->z.flags&VAR)&&p->z.v==v) return 0;
      if(p==g->end) break;
    }
    if(g==end) break;
  }
  return 1;
}

/*  Testet, ob z immer ausgefuehrt wird, falls start in fg ausgefuehrt  */
/*  wird. fg_tmp ist ein Bitvektor, um zu merken, welche Bloecke sicher */
/*  zu z fuehren. Das ganze fuer die Schleife start-end.                */
/*  Wenn ignorecall!=0 ist, wird angenommen, dass jeder CALL            */
/*  zurueckkehrt (das ist nuetzlich fuer loop-unrolling).               */
int always_reached(struct flowgraph *start,struct flowgraph *end,struct flowgraph *fg,struct IC *z,int ignorecall)
{
  bvtype *bmk=fg_tmp;
  struct IC *p;struct flowgraph *g;
  int changed;
  
  for(p=z;p;p=p->prev){
    if(!ignorecall&&p->code==CALL) return 0;
    if(p==fg->start) break;
  }
  
  if(fg==start) return 1;
  
  memset(bmk,0,bsize);
  BSET(bmk,fg->index);
  
  do{
    changed=0;
    for(g=start;g;g=g->normalout){
      if(!BTST(bmk,g->index)){
	struct flowgraph *n=g->normalout;
	struct flowgraph *b=g->branchout;
	if(n||b){
	  if((!b||BTST(bmk,b->index))&&
	     (!n||(g->end&&g->end->code==BRA)||BTST(bmk,n->index))){
	    for(p=g->end;p;p=p->prev){
	      if(!ignorecall&&p->code==CALL) break;
	      if(p==g->start){
		if(g==start) return 1;
		changed=1; BSET(bmk,g->index);
		break;
	      }
	    }
	  }
	}
      }
      if(g==end) break;
    }
  }while(changed);
  return 0;
}

/*  Ermittelt, ob Variable vindex schleifeninvariant unter den Bedingungen  */
/*  rd_defs, inloop und invariant ist.                                      */
/*  Definition ignore wird nicht beachtet. Wenn ignore auf eine gueltige    */
/*  Definition gesetzt wird, kann man somit auf Induktionsvariablen testen  */
/*  (das Ergebnis sagt dann, ob das die einzige Definition in der Schleife  */
/*  ist).                                                                   */
int def_invariant(int vindex,int ignore)
{
  int i,k,j,d=0;
  /*printf("def_inv(%d)=%s(%ld)\n",vindex,vilist[vindex]->identifier,zm2l(vilist[vindex]->offset));*/
  for(j=1;j<=dcount;j++){
    if(j!=ignore&&BTST(rd_defs,j)){
      if(BTST(var_defs[vindex],j)&&BTST(inloop,j)){
	/*  Mehr als eine moegliche Def. innerhalb der Schleife oder    */
	/*  eine invariante Def. in der Schleife => nicht invariant.    */
	if(d) return 0;
	if(!BTST(moved_completely,j)) return 0;
	d=1;
      }
    }  
    if(BTST(rd_defs,UNDEF(j))){
      if(BTST(var_defs[vindex],UNDEF(j))&&BTST(inloop,UNDEF(j))){
	/*  Mehr als eine moegliche Def. innerhalb der Schleife oder    */
	/*  eine invariante Def. in der Schleife => nicht invariant.    */
	if(d) return 0;
	if(!BTST(moved_completely,UNDEF(j))) return 0;
	d=1;
      }
    }      
  }
  return 1;
#if 0
  if(!BTST(rd_defs,vindex+dcount+1)){
    memcpy(rd_tmp,rd_defs,dsize);
    bvintersect(rd_tmp,var_defs[vindex],dsize);
    for(j=1;j<=dcount;j++){
      if(j!=ignore&&BTST(rd_tmp,j)&&BTST(inloop,j)){
	/*  Mehr als eine moegliche Def. innerhalb der Schleife oder    */
	/*  eine invariante Def. in der Schleife => nicht invariant.    */
	if(d) return 0;
	if(!BTST(moved_completely,j)) return 0;
	d=1;
      }
    }
  }else{
    for(j=1;j<=dcount;j++){
      if(j!=ignore&&BTST(rd_defs,j)&&BTST(inloop,j)){
	struct IC *p=dlist[j];
	for(k=0;k<p->change_cnt;k++){
	  i=p->change_list[k].v->index;
	  if(p->change_list[k].flags&DREFOBJ) i+=vcount-rcount;
	  if(i==vindex) break;
	}
	if(k>=p->change_cnt) continue;
	/*  Mehr als eine moegliche Def. innerhalb der Schleife oder    */
	/*  eine invariante Def. in der Schleife => nicht invariant.    */
	if(d) return 0;
	if(!BTST(moved_completely,j)) return 0;
	d=1;
      }
    }
  }
  return 1;
#endif
}

void frequency_reduction(struct flowgraph *start,struct flowgraph *end,struct flowgraph *head)
/*  Schleifeninvariante ICs finden und in eine Liste eintragen, falls   */
/*  sie vor die Schleife gezogen werden koennen.                        */
{
  struct IC *p;struct flowgraph *g;
  int i,changed;

  if(head&&start->loopend){
    end=start->loopend;

    if(DEBUG&1024){
      printf("searching for loop-invariant code in loop from block %d to %d\n",start->index,end->index);
      printf("head_fg=%d\n",head->index);
    }
    calc_movable(start,end);
    /*  erstmal kein IC invariant   */
    memset(invariant,0,dsize);

    /*  kennzeichnen, welche ICs in der Schleife liegen */
    memset(inloop,0,dsize);
    for(g=start;g;g=g->normalout){
      for(p=g->start;p;p=p->next){
	if(p->defindex) BSET(inloop,p->defindex);
	if(p==g->end) break;
      }
      if(g==end) break;
    }

    do{
      changed=0;
      if(DEBUG&1024) printf("loop-invariant pass\n");

      /*  Schleifeninvariante ICs suchen  */

      for(g=start;g;g=g->normalout){
	memcpy(rd_defs,g->rd_in,dsize);
	for(p=g->start;p;p=p->next){
	  int k1,k2;
	  /*  testen, ob IC neu als invariant markiert werden kann    */
	  if(p->defindex&&p->code!=CALL&&p->code!=GETRETURN&&!BTST(invariant,p->defindex)){
	    if(!BTST(inloop,p->defindex)) ierror(0);
	    if(p->code==ADDRESS||!p->q1.flags||(p->q1.flags&(KONST|DREFOBJ))==KONST||(p->q1.flags&VARADR)){
	      k1=1;
	    }else{
	      if(!(p->q1.flags&VAR)){
		k1=0;
	      }else{
		i=p->q1.v->index;
		if(p->q1.flags&DREFOBJ) i+=vcount-rcount;
		k1=def_invariant(i,-1);
	      }
	    }
	    if(k1){
	      if(!p->q2.flags||(p->q2.flags&(KONST|DREFOBJ))==KONST||(p->q2.flags&VARADR)){
		k2=1;
	      }else{
		if(!(p->q2.flags&VAR)){
		  k2=0;
		}else{
		  i=p->q2.v->index;
		  if(p->q2.flags&DREFOBJ) i+=vcount-rcount;
		  k2=def_invariant(i,-1);
		}
	      }
	    }
	    if(k1&&k2){
	      if(DEBUG&1024){ printf("found loop-invariant IC:\n");pric2(stdout,p);}
	      if(!BTST(moved,p->defindex)&&(always_reached(start,end,g,p,0)||(!dangerous_IC(p)&&used_in_loop_only(start,end,&p->z)))){
		if(p->z.flags&DREFOBJ){
		  if(p->z.flags&KONST)
		    k1=0;
		  else
		    k1=def_invariant(p->z.v->index,-1);
		}else
		  k1=1;
		/*if(DEBUG&1024) printf("always reached or used only in loop\n");*/
		if(k1&&!BTST(not_movable,p->defindex)&&!(ztyp(p)&VOLATILE)&&(!(p->z.flags&VAR)||!p->z.v->reg)){
		  /*if(DEBUG&1024) printf("movable\n");*/
		  add_movable(p,head,MOVE_IC);
		}else{
		  if(p->code==ADDRESS||(ISSCALAR(p->typf)&&(p->q2.flags||(p->q1.flags&DREFOBJ)))){
		    /*if(DEBUG&1024) printf("move computation out of loop\n");*/
		    if(!(disable&256))
		      add_movable(p,head,MOVE_COMP);
		  }
		}
	      }else{
		/*  Wenn IC immer erreicht wird oder ungefaehrlich  */
		/*  ist, kann man zumindest die Operation           */
		/*  rausziehen, falls das lohnt.                    */
		if(!BTST(moved,p->defindex)&&(!dangerous_IC(p)&&ISSCALAR(p->typf)&&(p->q2.flags||(p->q1.flags&DREFOBJ)||p->code==ADDRESS))){
		  /*if(DEBUG&1024) printf("move computation out of loop\n");*/
		  if(!(disable&256))
		    add_movable(p,head,MOVE_COMP);
		}
	      }
	      BSET(invariant,p->defindex);
	      changed=1;
	    }
	  }

	  /*  Das hier, um rd_defs zu aktualisieren.  */
	  rd_change(p);

	  if(p==g->end) break;
	}
	if(g==end) break;
      }
    }while(changed);

  }
  return;
}
void add_sr(struct IC *p,struct flowgraph *fg,int i_var)
/*  Fuegt IC p, das aus der Schleife in Block fg lineare Fkt. der   */
/*  Induktionsvariable i_var ist, in Liste ein.                     */
/*  Funktioniert als Stack. Da mit aeusseren Schleifen angefangen   */
/*  wird, werden ICs zuerst in inneren Schleifen reduziert. Da ein  */
/*  IC nur einmal reduziert wird, sollte dadurch das Problem eines  */
/*  ICs, das potentiell in mehreren Schleifen reduziert werden      */
/*  koennte, geloest werden.                                        */
{
  struct srlist *new=mymalloc(sizeof(*new));
  if(DEBUG&1024) printf("all:%p\n",(void*)new);
  new->IC=p;
  new->target_fg=fg;
  new->ind_var=ind_vars[i_var];
  new->next=first_sr;
  new->hv=0;
  first_sr=new;
#if 0
  if(last_sr){
    last_sr->next=new;
    last_sr=new;
  }else{
    first_sr=last_sr=new;
  }
#endif
}
int do_sr(void)
/*  Durchlaufe die Liste aller strength-reduction-Kandidaten und    */
/*  ersetze sie durch neue Induktionsvariablen. Dabei aufpassen,    */
/*  falls ein IC schon von frequency-reduction bearbeitet wurde.    */
/*  Ausserdem wird die Liste freigegeben.                           */
{
  struct IC **fglist; /* Letztes IC vor jedem Block   */
  struct IC *p;
  struct flowgraph *g;
  struct srlist *mf;
  int changed=0;

  if(!first_sr) return 0;

  if(DEBUG&1024) printf("performing strength-reductions\n");

  fglist=mymalloc((basic_blocks+1)*sizeof(*fglist));
  p=0;
  for(g=first_fg;g;g=g->normalout){
    if(g->index>basic_blocks) ierror(0);
    if(g->end) p=g->end;
    fglist[g->index]=p;
  }

  while(first_sr){
    struct Var *niv,*nstep;
    struct Typ *t1,*t2;
    struct IC *iv_ic,*new,*m;
    int i,c;
    p=first_sr->IC;
    i=p->defindex;
    /*  Falls IC noch nicht verschoben und noch nicht reduziert wurde.  */
    if(!BTST(moved,i)&&p->code!=ASSIGN){
      if(first_sr->hv){
	/*  Es wurde schon eine aequivalente Operation reduziert, wir   */
	/*  koennen also dieselbe Hilfsvariable benutzen.               */
	p->code=ASSIGN;
	p->q1.flags=VAR;
	p->q1.v=first_sr->hv;
	p->q1.val.vmax=l2zm(0L);
	p->q2.flags=0;
	p->q2.val.vmax=szof(p->z.v->vtyp);
        p->typf=p->z.v->vtyp->flags;
	/*  Hilfsvariable wird jetzt auch benutzt.  */
	if(have_alias){
	  void *m=p->use_list;
	  p->use_cnt++;
	  p->use_list=mymalloc(p->use_cnt*VLS);
	  memcpy(&p->use_list[1],m,(p->use_cnt-1)*VLS);
	  free(m);
	  p->use_list[0].v=first_sr->hv;
	  p->use_list[0].flags=0;
	}
      }else{
	int minus=0;
	if(DEBUG&1024){ printf("performing strength-reduction on IC:\n");pric2(stdout,p);}
	c=p->code;
	g=first_sr->target_fg;
	iv_ic=first_sr->ind_var;
	/*  Merken, wenn IC von der Form SUB x,ind_var->z   */
	if((c==SUB||c==SUBIFP)&&!compare_objs(&p->q2,&iv_ic->z,iv_ic->typf))
	  minus=1;
	t1=new_typ();
	t1->flags=p->typf;
	if(c==ADDI2P||c==SUBIFP){
	  t1->flags=p->typf2;
	  t1->next=new_typ();
	  t1->next->flags=VOID;
	}
	niv=add_tmp_var(t1);
	/*  Suchen, ob es noch aequivalente Operationen gibt.   */
	/*  Noch sehr ineffizient...                            */
	for(mf=first_sr->next;mf;mf=mf->next){
	  if(mf->target_fg==g&&mf->ind_var==iv_ic){
	    m=mf->IC;
	    if(c==m->code&&p->typf==m->typf&&
	       !compare_objs(&p->q1,&m->q1,p->typf)&&
	       !compare_objs(&p->q2,&m->q2,p->typf)){
	      if(mf->hv) ierror(0);
	      mf->hv=niv;
	      if(DEBUG&1024){ printf("equivalent operation\n");pric2(stdout,m);}
	    }
	  }
	}
	/*  Initialisierung der Hilfsinduktionsvariablen    */
	new=new_IC();
	*new=*p;
	new->z.flags=VAR;
	new->z.v=niv;
	new->z.val.vmax=l2zm(0L);
	/*  IC benutzt dasselbe wie p und aendert nur niv.  */
	if(have_alias){
	  new->change_cnt=1;
	  new->change_list=mymalloc(VLS);
	  new->change_list[0].v=niv;
	  new->change_list[0].flags=0;
	  new->use_cnt=p->use_cnt;
	  new->use_list=mymalloc(new->use_cnt*VLS);
	  memcpy(new->use_list,p->use_list,new->use_cnt*VLS);
	}
	insert_IC_fg(g,fglist[g->index],new);
	fglist[g->index]=m=new;
	/*  Ersetzen der Operation durch die Hilfsvariable  */
	p->code=ASSIGN;
	p->typf=t1->flags;
	p->q1=m->z;
	p->q2.flags=0;
	p->q2.val.vmax=szof(t1);
	/*  Benutzt jetzt auch Hilfsvariable.               */
	if(have_alias){
	  void *mr=p->use_list;
	  p->use_cnt++;
	  p->use_list=mymalloc(p->use_cnt*VLS);
	  memcpy(&p->use_list[1],mr,(p->use_cnt-1)*VLS);
	  free(mr);
	  p->use_list[0].v=niv;
	  p->use_list[0].flags=0;
	}
	/*  Berechnen der Schrittweite fuer Hilfsvariable   */
	if(c==MULT){
	  t2=new_typ();
	  t2->flags=iv_ic->typf;
	  nstep=add_tmp_var(t2);
	  new=new_IC();
	  new->line=iv_ic->line;
	  new->file=iv_ic->file;
	  new->code=MULT;
	  new->typf=p->typf;
	  new->z.flags=VAR;
	  new->z.v=nstep;
	  new->z.val.vmax=l2zm(0L);
	  if(!compare_objs(&m->q1,&iv_ic->z,iv_ic->typf)) new->q1=m->q2;
	  else new->q1=m->q1;
	  if(!compare_objs(&iv_ic->q1,&iv_ic->z,iv_ic->typf)) new->q2=iv_ic->q2;
	  else new->q2=iv_ic->q1;
	  /*  Benutzt dasselbe wie iv_ic und m.   */
	  if(have_alias){
	    new->use_cnt=iv_ic->use_cnt+m->use_cnt;
	    new->use_list=mymalloc(new->use_cnt*VLS);
	    memcpy(new->use_list,iv_ic->use_list,iv_ic->use_cnt*VLS);
	    memcpy(&new->use_list[iv_ic->use_cnt],m->use_list,m->use_cnt*VLS);
	    new->change_cnt=1;
	    new->change_list=mymalloc(VLS);
	    new->change_list[0].v=nstep;
	    new->change_list[0].flags=0;
	  }
	  insert_IC_fg(g,fglist[g->index],new);
	  fglist[g->index]=m=new;
	}
	/*  Erhoehen der Hilfsvariable um Schrittweite      */
	new=new_IC();
	new->line=iv_ic->line;
	new->file=iv_ic->file;

	new->code=iv_ic->code;
	if(minus){
	  switch(new->code){
	  case ADD:     new->code=SUB; break;
	  case SUB:     new->code=ADD; break;
	  case ADDI2P:  new->code=SUBIFP; break;
	  case SUBIFP:  new->code=ADDI2P; break;
	  }
	}
	if(ISPOINTER(t1->flags)){
	  if(new->code==ADD) new->code=ADDI2P;
	  if(new->code==SUB) new->code=SUBIFP;
	  new->typf=t1->flags;
	}
	new->typf=iv_ic->typf;
	new->q1.flags=VAR;
	new->q1.v=niv;
	new->typf2=niv->vtyp->flags;
	new->q1.val.vmax=l2zm(0L);
	new->z=new->q1;
	if(c==MULT){
	  new->q2=m->z;
	}else{
	  if(!compare_objs(&iv_ic->q1,&iv_ic->z,iv_ic->typf)) new->q2=iv_ic->q2;
	  else new->q2=iv_ic->q1;
	}
	if(have_alias){
	  new->use_cnt=iv_ic->use_cnt+m->use_cnt;
	  new->use_list=mymalloc(new->use_cnt*VLS);
	  memcpy(new->use_list,iv_ic->use_list,iv_ic->use_cnt*VLS);
	  memcpy(&new->use_list[iv_ic->use_cnt],m->use_list,m->use_cnt*VLS);
	  new->change_cnt=1;
	  new->change_list=mymalloc(VLS);
	  new->change_list[0].v=niv;
	  new->change_list[0].flags=0;
	}
	/*  Flussgraph muss nur bei den Schleifenkoepfen ok sein.   */
	insert_IC(iv_ic,new);
	changed|=2;
      }
    }

    mf=first_sr->next;
    free(first_sr);
    first_sr=mf;
  }
  free(fglist);
  return changed;
}
void strength_reduction(struct flowgraph *start,struct flowgraph *end,struct flowgraph *head)
/*  Ersetzen von Operationen mit einer Induktionsvariablen und einem    */
/*  schleifeninvarianten Operanden durch eine zusaetzliche              */
/*  Hilfsinduktionsvariable.                                            */
{
  struct flowgraph *g;struct IC *p;
  int i;
  if(DEBUG&1024) printf("performing strength_reduction on blocks %d to %d\n",start->index,end->index);
  for(i=0;i<vcount;i++) ind_vars[i]=0;
  /*  Nach Induktionsvariablen suchen.    */
  for(g=start;g;g=g->normalout){
    memcpy(rd_defs,g->rd_in,dsize);
    for(p=g->start;p;p=p->next){
      int c=p->code;
      if(c==ADD||c==ADDI2P||c==SUB||c==SUBIFP){
	if(!compare_objs(&p->q1,&p->z,p->typf)){
	  if(DEBUG&1024){printf("possible induction:\n");pric2(stdout,p);}
	  if(p->q2.flags&VAR){
	    i=p->q2.v->index;
	    if(p->q2.flags&DREFOBJ) i+=vcount-rcount;
	  }
	  if((p->q2.flags&(VAR|VARADR))!=VAR||def_invariant(i,-1)){
	    i=p->z.v->index;
	    if(p->z.flags&DREFOBJ) i+=vcount-rcount;
	    if(def_invariant(i,p->defindex)){
	      if(DEBUG&1024) {printf("found basic induction var:\n");pric2(stdout,p);}
	      ind_vars[i]=p;
	    }
	  }
	}
	if(USEQ2ASZ&&c!=SUB&&c!=SUBIFP&&!compare_objs(&p->q2,&p->z,p->typf)){
	  if(DEBUG&1024){printf("possible induction:\n");pric2(stdout,p);}
	  if(p->q1.flags&VAR){
	    i=p->q1.v->index;
	    if(p->q1.flags&DREFOBJ) i+=vcount-rcount;
	  }
	  if((p->q1.flags&(VAR|VARADR))!=VAR||def_invariant(i,-1)){
	    i=p->z.v->index;
	    if(p->z.flags&DREFOBJ) i+=vcount-rcount;
	    if(def_invariant(i,p->defindex)){
	      if(DEBUG&1024) {printf("found basic induction var:\n");pric2(stdout,p);}
	      ind_vars[i]=p;
	    }
	  }
	}
      }

      /*  Das hier, um rd_defs zu aktualisieren.  */
      rd_change(p);

      if(p==g->end) break;
    }
    if(g==end) break;
  }

  if(disable&1024) return;

  /*  Nach reduzierbaren Operationen suchen   */
  for(g=start;g;g=g->normalout){
    memcpy(rd_defs,g->rd_in,dsize);
    for(p=g->start;p;p=p->next){
      if((p->code==MULT||((p->code==ADD||p->code==SUB||p->code==ADDI2P||p->code==SUBIFP)&&!(disable&512)))&&
	 ((!ISFLOAT(p->typf))||fp_assoc)&&!(p->flags&EFF_IC) ){
	int k1,k2,iv;
	if((p->q1.flags&(VAR|VARADR))==VAR){
	  i=p->q1.v->index;
	  if(p->q1.flags&DREFOBJ) i+=vcount-rcount;
	  if(ind_vars[i]){
	    k1=1;iv=i;
	  }else if(def_invariant(i,-1))
	    k1=2;
	  else
	    k1=0;
	}else
	  k1=2;
	if((p->q2.flags&(VAR|VARADR))==VAR){
	  i=p->q2.v->index;
	  if(p->q2.flags&DREFOBJ) i+=vcount-rcount;
	  if(ind_vars[i]){
	    k2=1;iv=i;
	  }else if(def_invariant(i,-1))
	    k2=2;
	  else
	    k2=0;
	}else
	  k2=2;
	if(p->z.flags&VAR){
	  /*  Aufpassen, dass eine Induktion nicht selbst reduziert   */
	  /*  wird.                                                   */
	  i=p->z.v->index;
	  if(p->z.flags&DREFOBJ) i+=vcount-rcount;
	  if(ind_vars[i]) k1=0;
	}
	if(k1+k2==3){
	  /*                    if(DEBUG&1024) {printf("could perform strength-reduction on:\n");pric2(stdout,p);}*/
	  add_sr(p,head,iv);
	}
      }
      /*  Das hier, um rd_defs zu aktualisieren.  */
      rd_change(p);

      if(p==g->end) break;
    }
    if(g==end) break;
  }
}
void copy_code(struct IC *start,struct IC *end,struct IC *dest,int n,struct IC *ignore)
/*  Kopiert Code von start bis end n-mal hinter dest. Generiert         */
/*  entsprechend neue Labels. Allerdings wird der Flussgraph und        */
/*  aliasing-info nicht angepasst und muss danach neu generiert werden. */
/*  IC ignore wird nicht kopiert.                                       */
{
  int firstl=0,lastl=0,*larray,i,j;
  struct IC *p,*new,*current;
  if(DEBUG&1024) printf("copy_code %d times\n",n);
  /*  Feststellen, welche Labels in der Schleife definiert werden.    */
  for(p=start;p;p=p->next){
    if(p->code==LABEL){
      if(firstl==0||firstl>p->typf) firstl=p->typf;
      if(lastl ==0|| lastl<p->typf) lastl =p->typf;
    }
    if(p==end) break;
  }
  if(DEBUG&1024) printf("firstl=%d, lastl=%d\n",firstl,lastl);
  larray=mymalloc((lastl-firstl+1)*sizeof(*larray));
  for(i=0;i<=lastl-firstl;i++) larray[i]=0;
  for(p=start;p;p=p->next){
    if(p->code==LABEL) larray[p->typf-firstl]=1;
    if(p==end) break;
  }
  current=dest;
  /*  Hauptschleife.  */
  for(i=0;i<n;i++){
    /*  Neue Labels erzeugen.   */
    for(j=0;j<=lastl-firstl;j++)
      if(larray[j]) larray[j]=++label;
    /*  Code kopieren (rueckwaerts).    */
    for(p=start;p;p=p->next){
      if(p!=ignore){
	new=new_IC();
	*new=*p;
	p->copy=new;
	/*  Fuer free_alias.    */
	new->change_cnt=new->use_cnt=0;
	new->change_list=new->use_list=0;
	/*  Evtl. Label anpassen.   */
	if(p->code>=LABEL&&p->code<=BRA){
	  if(p->typf>=firstl&&p->typf<=lastl&&larray[p->typf-firstl])
	    new->typf=larray[p->typf-firstl];
	}
	if(p->code==CALL){
	  int i;
	  new->arg_list=mymalloc(sizeof(*new->arg_list)*new->arg_cnt);
	  for(i=0;i<new->arg_cnt;i++) new->arg_list[i]=p->arg_list[i]->copy;
	  new->call_list=mymalloc(new->call_cnt*sizeof(*new->call_list));
	  memcpy(new->call_list,p->call_list,new->call_cnt*sizeof(*new->call_list));
	}
	insert_IC(current,new);
	current=new;
      }
      if(p==end) break;
    }
  }
  free(larray);
}
void add_ur(int flags,long total,long unroll,struct flowgraph *start,struct flowgraph *head,struct IC *cmp,struct IC *branch,struct IC *ind)
/*  Fuegt Daten fuer loop-unrolling in Stack ein.                       */
{
  struct urlist *new=mymalloc(sizeof(struct urlist));
  if(DEBUG&1024) printf("add_ur, flags=%d\n",flags);
  new->flags=flags;
  new->total=total;
  new->unroll=unroll;
  new->start=start;
  new->head=head;
  new->cmp=cmp;
  new->branch=branch;
  new->ind=ind;
  new->next=first_ur;
  first_ur=new;
}
int do_unroll(int donothing)
/*  Fuehrt loop-unrolling durch. Wenn donothing!=0, wird die Liste nur  */
/*  freigegeben.                                                        */
{
  int changed=0; struct urlist *m;
  struct IC *div;
  while(m=first_ur){
    int flags=m->flags;
    long total=m->total,unroll=m->unroll;
    struct flowgraph *start=m->start,*head=m->head;
    struct IC *cmp=m->cmp,*branch=m->branch,*ind=m->ind;
    if(donothing) flags=0;
    if(flags&UNROLL_COMPLETELY){
      /*  Schleife komplett ausrollen.    */
      if(DEBUG&1024) printf("unroll loop completely\n");
      copy_code(start->start->next,cmp->prev,start->start,total-1,0);
      if(DEBUG&1024) printf("removing loop branch\n");
      remove_IC(branch);
      if(!cmp->z.flags){
	remove_IC(cmp);
	if(DEBUG&1024) printf("removing loop compare\n");
      }
      changed|=1;
    }
    if(flags&UNROLL_MODULO){
      /*  Schleife teilweise ausrollen.   */
      if(DEBUG&1024) printf("unroll loop partially, n=%ld,r=%ld\n",unroll,total%unroll);
      if(unroll>1){
	if((flags&(IND_ONLY_COUNTS|MULTIPLE_EXITS))==IND_ONLY_COUNTS){
	  struct IC *new=new_IC();
	  copy_code(start->start->next,cmp->prev,head->start,total%unroll,ind);
	  *new=*ind;
	  new->use_cnt=new->change_cnt=0;
	  new->use_list=new->change_list=0;
	  if(!(ind->q2.flags&KONST)) ierror(0);
	  eval_const(&ind->q2.val,ind->typf);
	  if(ind->typf&UNSIGNED){
	    gval.vumax=zummult(vumax,ul2zum((unsigned long)(total%unroll)));
	    eval_const(&gval,(UNSIGNED|MAXINT));
	  }else{
	    gval.vumax=zummult(vumax,ul2zum((unsigned long)(total%unroll)));
	    eval_const(&gval,MAXINT);
	  }
	  insert_const(&new->q2.val,ind->typf);
	  insert_IC(head->end,new);
	  copy_code(start->start->next,cmp->prev,start->start,unroll-1,ind);
	  eval_const(&ind->q2.val,ind->typf);
	  if(ind->typf&UNSIGNED){
	    gval.vumax=zummult(vumax,ul2zum((unsigned long)(unroll)));
	    eval_const(&gval,(UNSIGNED|MAXINT));
	  }else{
	    gval.vumax=zummult(vumax,ul2zum((unsigned long)(unroll)));
	    eval_const(&gval,MAXINT);
	  }
	  insert_const(&ind->q2.val,ind->typf);		
	  changed|=1;
	}else{
	  copy_code(start->start->next,cmp->prev,head->start,total%unroll,0);
	  copy_code(start->start->next,cmp->prev,start->start,unroll-1,0);
	  changed|=1;
	}
      }
    }
    if(flags&UNROLL_INVARIANT){
      struct IC *new,*mc,*mn; struct Var *v; int out=++label,code;
      long i; struct Typ *t;static struct Typ tptrdiff={0};
      if(DEBUG&1024) printf("unrolling non-constant loop\n");
      if(ISPOINTER(cmp->typf)){
	tptrdiff.flags=PTRDIFF_T(cmp->typf);
	t=&tptrdiff;
      }else{
	if(cmp->q1.flags&VAR)
	  t=cmp->q1.v->vtyp;
	else
	  t=cmp->q2.v->vtyp;
      }
      v=add_tmp_var(clone_typ(t));
      /*  branch dient hier teilweise als leere Schablone.    */
      /*  Label an Schleifenausgang setzen.   */
      new=new_IC(); 
      *new=*branch;
      new->q1.flags=0;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->code=LABEL;
      new->typf=out;
      insert_IC(branch,new);
      /*  Test vor die unroll-Variante.   */
      new=new_IC(); *new=*branch;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      if(branch->code==BLT) new->code=BGE;
      if(branch->code==BLE) new->code=BGT;
      if(branch->code==BGT) new->code=BLE;
      if(branch->code==BGE) new->code=BLT;
      if(branch->code==BEQ) ierror(0);
      if(branch->code==BNE) ierror(0);
      code=branch->code;
      mc=new;
      new->typf=out;
      insert_IC(head->start,new);
      new=new_IC(); *new=*cmp;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      insert_IC(head->start,new);
      /*  Einsprungpunkte fuer die Modulos.   */
      label+=unroll;
      for(i=1;i<unroll;i++){
	copy_code(start->start->next,cmp->prev,head->start,1,0);
	new=new_IC(); *new=*branch;
	new->q1.flags=0;
	new->change_cnt=new->use_cnt=0;
	new->change_list=new->use_list=0;
	new->code=LABEL;
	new->typf=out+i;
	insert_IC(head->start,new);
      }
      /*  Testen, welches Modulo. */
      for(i=unroll-2;i>=0;i--){
	new=new_IC(); *new=*branch;
	new->change_cnt=new->use_cnt=0;
	new->change_list=new->use_list=0;
	new->code=BEQ;
	if(i>0) new->typf=out+i;
	else new->typf=start->start->typf;
	insert_IC(head->start,new);
	new=new_IC(); *new=*branch;
	new->change_cnt=new->use_cnt=0;
	new->change_list=new->use_list=0;
	if(SWITCHSUBS) gval.vint=zm2zi(l2zm(1L));
	else       gval.vint=zm2zi(l2zm(i));
	eval_const(&gval,INT);
	new->q1.flags=VAR;
	new->q1.v=v;
	new->q1.val.vmax=l2zm(0L);
	new->typf=t->flags;
	if(SWITCHSUBS||i==0){
	  new->code=TEST;
	  insert_IC(head->start,new);
	  if(i>0){
	    new=new_IC();
	    *new=*head->start->next;
	    new->change_cnt=new->use_cnt=0;
	    new->change_list=new->use_list=0;
	    new->code=SUB;
	    new->z=new->q1;
	    new->q2.flags=KONST;
	    insert_const(&new->q2.val,new->typf&NU);
	    insert_IC(head->start,new);
	  }
	}else{
	  new->code=COMPARE;
	  new->q2.flags=KONST;
	  insert_const(&new->q2.val,new->typf&NU);
	  insert_IC(head->start,new);
	}
      }
      /*  Durchlaeufe modulo unroll berechnen.    */
      new=new_IC(); *new=*branch;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->code=AND;
      new->typf=t->flags;
      new->q1.flags=VAR;
      new->q1.v=v;
      new->q1.val.vmax=l2zm(0L);
      new->z=new->q1;
      new->q2.flags=KONST;
      gval.vmax=l2zm(unroll-1);
      eval_const(&gval,MAXINT);
      insert_const(&new->q2.val,new->typf);
      insert_IC(head->start,new);
      new=new_IC();
      *new=*ind;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->code=DIV;
      div=new;
      new->q1=head->start->next->z;
      new->z=new->q1;
      insert_IC(head->start,new);
      new=new_IC();
      *new=*head->start->next;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->code=ADD;
      insert_IC(head->start,new);
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      if(code==BLT||code==BGT){
	new=new_IC();
	*new=*head->start->next;
	new->change_cnt=new->use_cnt=0;
	new->change_list=new->use_list=0;
	new->code=SUB;
	gval.vmax=l2zm(1L);
	eval_const(&gval,MAXINT);
	insert_const(&new->q2.val,new->typf);
	insert_IC(head->start,new);
      }
      new=new_IC();
      *new=*head->start->next;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->code=SUB;
      if(t==&tptrdiff)
	new->code=SUBPFP;

      if(!compare_objs(&ind->z,&cmp->q1,new->typf)){
	if(code==BLT||code==BLE){
	  new->q1=cmp->q2;new->q2=ind->z;
	}else{
	  new->q2=cmp->q2;new->q1=ind->z;
	}
      }else{
	if(code==BLT||code==BLE){
	  new->q1=cmp->q1;new->q2=ind->z;
	}else{
	  new->q2=cmp->q1;new->q1=ind->z;
	}
      }
#if 0
      if(ind->code==SUB){
	struct obj o;
	o=new->q1;new->q1=new->q2;new->q2=o;
      }
#endif
      if(!new->q1.flags){
	if(!new->q2.flags) ierror(0);
	new->q1.flags=KONST;
	gval.vmax=l2zm(0L);
	eval_const(&gval,new->typf);
	insert_const(&new->q1.val,new->typf);
      }
      if(!new->q2.flags){
	if(!new->q1.flags) ierror(0);
	new->q2.flags=KONST;
	gval.vmax=l2zm(0L);
	eval_const(&gval,new->typf);

	insert_const(&new->q2.val,new->typf);
      }
      insert_IC(head->start,new);
      new=new_IC();
      *new=*mc;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->typf=out+1;
      insert_IC(head->start,new);
      new=new_IC();
      *new=*cmp; new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      insert_IC(head->start,new);
      if((flags&(IND_ONLY_COUNTS|MULTIPLE_EXITS))==IND_ONLY_COUNTS){
	if(!(ind->q2.flags&KONST)) ierror(0);
	copy_code(start->start->next,cmp->prev,start->start,unroll-1,ind);
	eval_const(&ind->q2.val,ind->typf);
	if(ind->typf&UNSIGNED){
	  gval.vumax=zummult(vumax,ul2zum((unsigned long)(unroll)));
	  eval_const(&gval,(UNSIGNED|MAXINT));
	}else{
	  gval.vumax=zummult(vumax,ul2zum((unsigned long)(unroll)));
	  eval_const(&gval,MAXINT);
	}
	insert_const(&ind->q2.val,ind->typf);		
	changed|=2;
      }else{
	copy_code(start->start->next,cmp->prev,start->start,unroll-1,0);
	changed|=2;
      }
      div->typf|=UNSIGNED;
    }
    if(flags&UNROLL_REVERSE){
      struct IC *new,*mc; struct Var *v; int out=++label,code;
      long i; struct Typ *t;static struct Typ tptrdiff={0};
      if(DEBUG&1024) printf("reversing loop\n");
      if(ISPOINTER(cmp->typf)){
	tptrdiff.flags=PTRDIFF_T(cmp->typf);
	t=&tptrdiff;
      }else{
	if(cmp->q1.flags&VAR)
	  t=cmp->q1.v->vtyp;
	else
	  t=cmp->q2.v->vtyp;
      }
      v=add_tmp_var(clone_typ(t));
      new=new_IC();
      *new=*ind;
      new->code=SUB;
      new->typf=t->flags;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->q1.flags=VAR;
      new->q1.v=v;
      new->q1.val.vmax=l2zm(0L);
      new->z=new->q1;
      new->q2.flags=KONST;
      gval.vmax=l2zm(1L);
      eval_const(&gval,MAXINT);
      insert_const(&new->q2.val,new->typf);
      insert_IC(cmp->prev,new);
      code=branch->code;
#if HAVE_WANTBNE
      branch->code=BNE;
#else
      branch->code=BGT;
#endif
      /*  Durchlaeufe berechnen.    */
      new=new_IC();
      *new=*ind;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->code=DIV;
      div=new;
      new->q1.flags=VAR;
      new->q1.v=v;
      new->q1.val.vmax=l2zm(0L);
      new->z=new->q1;
      insert_IC(head->start,new);
      new=new_IC();
      *new=*head->start->next;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->code=ADD;
      insert_IC(head->start,new);
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      if(code==BLT||code==BGT||code==BNE){
	struct IC *a=head->start->next;
	gval.vmax=l2zm(1L);
	eval_const(&gval,MAXINT);
	insert_const(&gval,a->typf);
	calc(SUB,a->typf,&a->q2.val,&gval,&a->q2.val,0);
      }
      new=new_IC();
      *new=*head->start->next;
      new->change_cnt=new->use_cnt=0;
      new->change_list=new->use_list=0;
      new->code=SUB;
      if(t==&tptrdiff)
	new->code=SUBPFP;

      if(!compare_objs(&ind->z,&cmp->q1,new->typf)){
	if(code==BLT||code==BLE||(code==BNE&&(ind->code==ADD||ind->code==ADDI2P))){
	  new->q1=cmp->q2;new->q2=ind->z;
	}else{
	  new->q2=cmp->q2;new->q1=ind->z;
	}
      }else{
	if(code==BLT||code==BLE||(code==BNE&&(ind->code==ADD||ind->code==ADDI2P))){
	  new->q1=cmp->q1;new->q2=ind->z;
	}else{
	  new->q2=cmp->q1;new->q1=ind->z;
	}
      }
#if 0
      if(ind->code==SUB){
	struct obj o;
	o=new->q1;new->q1=new->q2;new->q2=o;
      }
#endif
      if(!new->q1.flags||!new->q2.flags) ierror(0);
      insert_IC(head->start,new);
      cmp->code=TEST;
      cmp->q1.flags=VAR;
      cmp->q1.v=v;
      cmp->q1.val.vmax=l2zm(0L);
      cmp->q2.flags=0;
      div->typf|=UNSIGNED;
      changed|=2;
    }
    first_ur=m->next;
    free(m);
  }
  return changed;
}

void unroll(struct flowgraph *start,struct flowgraph *head)
/*  Versucht loop-unrolling.                                            */
{
  struct flowlist *lp;struct flowgraph *end,*g;struct IC *p,*m,*branch,*cmp;
  struct obj *o,*e,*cc; union atyps init_val,end_val,step_val;
  bvtype *tmp;
  long dist,step,ic_cnt,n;
  int bflag=0,t=0,i,flags=0; /* 1: sub, 2: init_val gefunden  */
  int ind_only_counts,multiple_exits,cfl;
  end=start->loopend;
  if(DEBUG&1024) printf("checking for possible unrolling from %d to %d\n",start->index,end->index);
  if(end->end->code==BRA){
    if(DEBUG&1024) printf("loop ends with BRA\n");
    return;
  }
  for(lp=start->in;lp;lp=lp->next)
    if(lp->graph->index>start->index&&lp->graph->index<=end->index&&lp->graph!=end) return;
  if(DEBUG&1024) printf("only one backward-branch\n");
  e=0; p=end->end; cfl=0;
  do{
    if(p->code>=BEQ&&p->code<BRA){ branch=p;bflag=p->code;cc=&p->q1; cfl=1;}
    if(p->code==TEST){
      if(!cfl) return;
      if(compare_objs(cc,&p->z,p->typf)) return;
      o=&p->q1;t=p->typf;cmp=p;
      end_val.vmax=l2zm(0L); eval_const(&end_val,MAXINT);
      insert_const(&end_val,t);
      break;
    }
    if(p->code==COMPARE){
      if(!cfl) return;
      if(compare_objs(cc,&p->z,p->typf)) return;
      cmp=p;
      if(p->q1.flags&VAR){
	if(ind_vars[p->q1.v->index]){
	  o=&p->q1;t=p->typf;
	  e=&p->q2;
	  break;
	}
      }
      if(p->q2.flags&VAR){
	if(ind_vars[p->q2.v->index]){
	  o=&p->q2;t=p->typf;
	  e=&p->q1;
	  if(bflag==BLT) bflag=BGT;
	  if(bflag==BLE) bflag=BGE;
	  if(bflag==BGT) bflag=BLT;
	  if(bflag==BGE) bflag=BLE;
	  break;
	}
      }
      return;
    }
    if(p==end->start) return;
    p=p->prev;
  }while(p);
  if(!e||(e->flags&KONST)){
    if(e) end_val=e->val;
    if(DEBUG&1024) printf("end condition is constant\n");
  }else{
    if(!(e->flags&VAR)) return;
    i=e->v->index;
    if(e->flags&DREFOBJ) i+=vcount-rcount;
    if(DEBUG&1024) printf("testing end-condition\n");
    memcpy(rd_defs,end->rd_in,dsize);
    for(m=end->start;m;m=m->next){
      if(m==cmp){
	if(DEBUG&1024) pric2(stdout,m);
	if(!def_invariant(i,-1)) return;
	if(DEBUG&1024) printf("end condition loop-invariant\n");
	break;
      }
      rd_change(m);
      if(m==end->end) ierror(0);
    }
  }
  p=ind_vars[o->v->index];
  if(!p) return;
  if(compare_objs(o,&p->z,t)) return;
  if(DEBUG&1024) printf("loop condition only dependant on induction var\n");
  if(!(p->q2.flags&KONST)) return;
  if(DEBUG&1024) printf("induction is constant\n");
  ind_only_counts=IND_ONLY_COUNTS;
  multiple_exits=0;
  for(ic_cnt=0,g=start;g;g=g->normalout){
    for(m=g->start;m;m=m->next){
      if(m!=p&&m!=cmp&&ind_only_counts){
	int i;
	for(i=0;i<m->use_cnt;i++){
	  if(m->use_list[i].v==p->q1.v){
	    if(DEBUG&1024){printf("use of ind_var:");pric2(stdout,m);}
	    ind_only_counts=0;break;
	  }
	}
      }
      if(m==p&&!always_reached(start,end,g,p,1)) return;
      ic_cnt++;
      if(m==g->end) break;
    }
    if(g==end) break;
    if(!multiple_exits&&g->branchout&&(g->branchout->index<start->index||g->branchout->index>end->index))
      multiple_exits=MULTIPLE_EXITS;
  }
  ic_cnt-=2;  /*  Branch und Test */
  if(DEBUG&1024) printf("induction always reached\n");
  if(ind_only_counts&&(DEBUG&1024)) printf("induction variable only used as counter\n");
  if(multiple_exits&&(DEBUG&1024)) printf("loop has multiple exits\n");
  if(DEBUG&1024) printf("ICs in loop: %ld\n",ic_cnt);
  step_val=p->q2.val;
  if(p->code==SUB) flags|=1;
  if(start->start->code==LABEL&&(start->start->flags&LOOP_COND_TRUE)&&e&&ind_only_counts){
    if(e->flags&KONST)
      eval_const(&e->val,t);
    if(!(e->flags&KONST)||!zmeqto(vmax,l2zm(0L))||!zumeqto(vumax,ul2zum(0UL))||!zldeqto(vldouble,d2zld(0.0))){
      int bc=branch->code;
      eval_const(&step_val,t);
      if((!(flags&1)&&(bc==BLT||bc==BLE))
	 ||((flags&1)&&(bc==BGT||bc==BGE))
	 ||(bc==BNE&&zmeqto(vmax,l2zm(1L))&&zumeqto(vumax,ul2zum(1UL)))){
	if((t&UNSIGNED)||zmleq(l2zm(0L),vmax)){
	  if(optspeed){
	    add_ur(UNROLL_REVERSE|ind_only_counts|multiple_exits,0,1,start,head,cmp,branch,p);
	    return;
	  }else{
	    if(zmeqto(vmax,l2zm(1L))&&zumeqto(vumax,ul2zum(1UL))&&zldeqto(vldouble,d2zld(1.0))){
	      add_ur(UNROLL_REVERSE|ind_only_counts|multiple_exits,0,1,start,head,cmp,branch,p);
	      return;
	    }
	  }
	}
      }
    }
  }
  if(!e||(e->flags&KONST)){
    struct IC tmpic;
    i=p->z.v->index;
    if(p->z.flags&DREFOBJ) i+=vcount-rcount;
    memcpy(rd_defs,head->rd_out,dsize);
    tmpic=*p;
    if(propagate(&tmpic,&tmpic.z,1,1)){
      init_val=tmpic.z.val;
    }else{
      goto inv;
    }
    if(DEBUG&1024){
      printf("loop number determinable\n");
      printf("init_val: ");printval(stdout,&init_val,t);
      printf("\nend_val: ");printval(stdout,&end_val,t);
      printf("\nstep_val: ");printval(stdout,&step_val,t);
      printf("\nflags=%d bflag=%d\n",flags,bflag);
    }
    /*  Nur integers als Induktionsvariablen.   */
    if(!ISINT(t)) goto inv;
    /*  Distanz und Step werden als long behandelt, deshalb pruefen, ob */
    /*  alles im Bereich des garantierten Mindestwerte fuer long.       */
    /*  Wenn man hier die Arithmetik der Zielmaschine benutzen wuerde,  */
    /*  koennte man theoretisch mehr Faelle erkennen, aber das waere    */
    /*  recht popelig und man muss sehr aufpassen.                      */
    if(t&UNSIGNED){
      eval_const(&step_val,t);
      if(!zumleq(vumax,l2zm(2147483647))) return;
      step=zum2ul(vumax);
      if(flags&1) step=-step;
      eval_const(&end_val,t);
      if(!zumleq(vumax,l2zm(2147483647))) goto inv;
      dist=zum2ul(vumax);
      eval_const(&init_val,t);
      if(!zumleq(vumax,l2zm(2147483647))) goto inv;
      dist-=zum2ul(vumax);
    }else{
      eval_const(&step_val,t);
      if(!zmleq(vmax,l2zm(2147483647))) return;
      if(zmleq(vmax,l2zm(-2147483647))) return; /*  eins weniger als moeglich waere */
      step=zm2l(vmax);
      if(flags&1) step=-step;
      eval_const(&end_val,t);
      if(!zmleq(vmax,l2zm(2147483647/2))) goto inv;
      if(zmleq(vmax,l2zm(-2147483647/2))) goto inv; /*  eins weniger als moeglich waere */
      dist=zm2l(vmax);
      eval_const(&init_val,t);
      if(!zmleq(vmax,l2zm(2147483647/2))) goto inv;
      if(zmleq(vmax,l2zm(-2147483647/2))) goto inv; /*  eins weniger als moeglich waere */
      dist-=zm2l(vmax);
    }
    if(DEBUG&1024) printf("dist=%ld, step=%ld\n",dist,step);
    if(step==0) ierror(0);
    /*  Die Faelle kann man noch genauer untersuchen, ob die Schleife   */
    /*  evtl. nur einmal durchlaufen wird o.ae.                         */
    if(step<0&&dist>=0){
      if(report_suspicious_loops){ error(208);report_suspicious_loops=0;}
      return;
    }
    if(step>0&&dist<=0){
      if(report_suspicious_loops){ error(208);report_suspicious_loops=0;}
      return;
    }
    if(bflag==BEQ){
      if(report_suspicious_loops){ error(208);report_suspicious_loops=0;}
      return;
    }
    /*  Aufpassen, ob das Schleifenende bei BNE auch getroffen wird.    */
    if(bflag==BNE){
      if(dist%step){
	if(report_suspicious_loops){ error(208);report_suspicious_loops=0;}
	return;
      }
    }
    if(bflag==BLT||bflag==BGT||bflag==BNE){
      if(step>0) dist--; else dist++;
    }
    if(dist/step<0) ierror(0);
    if(DEBUG&1024) printf("loop is executed %ld times\n",dist/step+1);
    if(start->start->code!=LABEL) ierror(0);
    if(dist/step+1==1||ic_cnt*(dist/step+1)<=unroll_size){
      /*  Schleife komplett ausrollen.    */
      add_ur(UNROLL_COMPLETELY|ind_only_counts|multiple_exits,dist/step+1,dist/step+1,start,head,cmp,branch,p);
    }else{
      /*  Schleife teilweise ausrollen.   */
      n=(unroll_size-ic_cnt-2)/(2*ic_cnt);
      if(n>0)
	add_ur(UNROLL_MODULO|ind_only_counts|multiple_exits,dist/step+1,n,start,head,cmp,branch,p);
    }
    return;
  }
 inv:
  /*  Anzahl der Schleifendurchlaeufe kann beim Eintritt in die   */
  /*  Schleife zur Laufzeit berechnet werden.                     */
  if(!(p->q2.flags&KONST)) return;
  if(t&UNSIGNED){
    eval_const(&step_val,t);
    if(!zumleq(vumax,l2zm(2147483647))) return;
    step=zum2ul(vumax);
    if(flags&1) step=-step;
  }else{
    eval_const(&step_val,t);
    if(!zmleq(vmax,l2zm(2147483647))) return;
    if(zmleq(vmax,l2zm(0))) return; /* gibt Probleme */
    step=zm2l(vmax);
    if(flags&1) step=-step;
  }
  if(!compare_objs(&cmp->q1,&p->z,t)){
    if(step>0&&(branch->code==BGT||branch->code==BGE)){
      if(report_suspicious_loops){ error(208);report_suspicious_loops=0;}
      return;
    }
    if(step<0&&(branch->code==BLT||branch->code==BLE)){
      if(report_suspicious_loops){ error(208);report_suspicious_loops=0;}
      return;
    }
  }else{
    if(step<0&&(branch->code==BGT||branch->code==BGE)){
      if(report_suspicious_loops){ error(208);report_suspicious_loops=0;}
      return;
    }
    if(step>0&&(branch->code==BLT||branch->code==BLE)){
      if(report_suspicious_loops){ error(208);report_suspicious_loops=0;}
      return;
    }
  }
  if(!unroll_all) return;
  if(bflag!=BEQ&&bflag!=BNE){
    if(unroll_size>=8*ic_cnt+8)
      add_ur(UNROLL_INVARIANT|ind_only_counts|multiple_exits,0,8,start,head,cmp,branch,p);
    else if(unroll_size>=4*ic_cnt+4)
      add_ur(UNROLL_INVARIANT|ind_only_counts|multiple_exits,0,4,start,head,cmp,branch,p);
    else if(unroll_size>=4*ic_cnt+2)
      add_ur(UNROLL_INVARIANT|ind_only_counts|multiple_exits,0,2,start,head,cmp,branch,p);
    return;
  }
}

int loop_optimizations(struct flowgraph *fg)
/*  steuert Optimierungen in Schleifen  */
{
  int changed=0,i;
  struct flowgraph *g,*last;
  if(DEBUG&1024) print_flowgraph(fg);
  if(loops(fg,0)==0) return 0;
  if(DEBUG&1024) print_flowgraph(fg);
  first_fg=fg=create_loop_headers(fg,0);
  if(DEBUG&1024) print_flowgraph(fg);
  num_defs();

  /*bsize=(basic_blocks+CHAR_BIT)/CHAR_BIT;*/
  bsize=BVSIZE(basic_blocks+1);
  fg_tmp=mymalloc(bsize);
  ind_vars=mymalloc(vcount*sizeof(*ind_vars));
  invariant=mymalloc(dsize);
  inloop=mymalloc(dsize);
  rd_defs=mymalloc(dsize);
  reaching_definitions(fg);
  if(DEBUG&1024) print_flowgraph(fg);
  moved=mymalloc(dsize);
  memset(moved,0,dsize);
  moved_completely=mymalloc(dsize);
  memset(moved_completely,0,dsize);
  not_movable=mymalloc(dsize);

  first_mov=last_mov=0;
  first_sr=last_sr=0;

  for(last=0,g=fg;g;g=g->normalout){
    if(g->loopend){
      frequency_reduction(g,g->loopend,last);
      strength_reduction(g,g->loopend,last);
      if(optflags&2048) unroll(g,last);
    }
    last=g;
  }

  free(rd_matrix);
  free(var_defs);
  free(defs_kill);
  free(defs_gen);
  free(dlist);
  free(rd_defs);
  free(invariant);
  free(inloop);
  changed|=move_to_head();
  if(DEBUG&1024) puts("done");
  changed|=do_sr();
  if(DEBUG&1024) puts("done");
  changed|=do_unroll(changed);
  if(DEBUG&1024) puts("done");
  free(moved);
  free(not_movable);
  free(moved_completely);
  if(DEBUG&1024) puts("4");
  if(changed&2){
    if(DEBUG&1024) printf("must repeat num_vars\n");
    free(vilist);
    free(av_globals);free(av_statics);
    free(av_drefs);free(av_address);
    num_vars();
  }

  free(ind_vars);
  free(fg_tmp);

  return changed;
}

