Patch title: i386 optimizer v24
Abstract:
Enhancements to the core optimizer for i386.
File: /pliant/language/optimizer/optimize.c
Key:
    Removed line
    Added line
   
// Copyright  Hubert Tonneau  hubert.tonneau@pliant.cx
//
// This program is free software; you can redistribute it an
// modify it under the terms of the GNU General Public Licen
// as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be u
// but WITHOUT ANY WARRANTY; without even the implied warran
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// version 2 along with this program; if not, write to the F
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA


#ifdef _LISTING_
  FUNCTION Bool please_trace(struct GeneratorContext *gc) {
    struct Str temp;
// Copyright  Hubert Tonneau  hubert.tonneau@pliant.cx
//
// This program is free software; you can redistribute it an
// modify it under the terms of the GNU General Public Licen
// as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be u
// but WITHOUT ANY WARRANTY; without even the implied warran
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// version 2 along with this program; if not, write to the F
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA


#ifdef _LISTING_
  FUNCTION Bool please_trace(struct GeneratorContext *gc) {
    struct Str temp;
    return compare_str(&gc->function->name,Str_map_string(&t
    return G.verbose_level>=3 || compare_str(&gc->function->name,Str_map_string(&temp,"sample function"))==compare_equal; }
#endif



static Void optimize_declare(struct GeneratorContext *gc) {
  Arrow pushsize,localssize; struct Str temp;
#endif



static Void optimize_declare(struct GeneratorContext *gc) {
  Arrow pushsize,localssize; struct Str temp;
  #ifdef _LISTING_
  if(please_trace(gc))
    optimize_listing(gc);
  #endif
  pushsize=entry(G.type_Int,G.debugging_level ? 4 : 0,END);
  Dictionary_insert(&gc->locals,Str_map_string(&temp,"pliant
  localssize=entry(G.type_Int,0,END);
  Dictionary_insert(&gc->locals,Str_map_string(&temp,"pliant



  pushsize=entry(G.type_Int,G.debugging_level ? 4 : 0,END);
  Dictionary_insert(&gc->locals,Str_map_string(&temp,"pliant
  localssize=entry(G.type_Int,0,END);
  Dictionary_insert(&gc->locals,Str_map_string(&temp,"pliant



#ifdef _LISTING_
static Void optimize_listing_pair(struct GeneratorContext *gc, struct Instruction *instr1,
                    struct Instruction *instr2, Char *name) {
  if (G.verbose_level>=4) {
    consolen(Z,"optimize_remove_nouse_instructions(gc,",Z,name,Z,")",EOL);
    if (instr1 != null) {
      optimize_listing_instruction(instr1);
      if (instr2==null && instr1->next_instruction != null)
        instr2=instr1->next_instruction; }
    if (instr2 != null)
      optimize_listing_instruction(instr2); } }
#else
#define optimize_listing_pair(gc, instr1, instr2, name) do { } while (0)
#endif

/*
doc
  [Removes some instructions that do nothing (such as copyin
*/

/*
doc
  [Removes some instructions that do nothing (such as copyin
*/

static Void optimize_remove_nouse_instructions(struct Genera
  struct Instruction *instr,*instr2;
  for(instr=gc->first_instruction; instr!=null; instr=instr2
    instr2=instr->next_instruction;
    if(instr->function==G.function_do_nothing) {
    orif(instr->function->flags&Function_flag_copy)
      if(instr->arguments.nb<2 || !Argument_is_shared(IARG(i
/*
doc
  [Removes some instructions that do nothing (such as copying a variable into itself).]
*/

#define Optimize_do_nothing               0x1
#define Optimize_copy_to_self             0x2
#define Optimize_copy_loop                0x4
#define Optimize_copy_over                0x8
#define Optimize_copy_to_unused           0x10
#define Optimize_copy_through_temp        0x20
#define Optimize_copy_through_addr        0x40
#define Optimize_copy_and_call            0x80
#define Optimize_call_and_copy            0x100
#define Optimize_math                     0x200
#define Optimize_addr                     0x400

/*
 * flag to say that ->first_instruction and ->last instruction are meaningful
 * also for true registers (this is true only at the very beginning of optimization,
 * since later assembler function call args will become implicit).
 */
#define Optimize_registers_explicit       0x1000
/*
 * flag to say that the stack (ESP) will not be adjusted anymore.
 * this is true only at the very end of optimization, since before the stack
 * is adjusted due to variables in progress of getting allocated on the stack
 */
#define Optimize_stack_frozen             0x2000


#define Optimize_ultrasafe ( \
  Optimize_do_nothing|Optimize_copy_to_unused|Optimize_copy_to_self| \
  Optimize_copy_through_temp|Optimize_copy_through_addr| \
  Optimize_copy_loop|Optimize_copy_over|Optimize_math \
)
#define Optimize_common                   (Optimize_ultrasafe|Optimize_addr)

#define Optimize_pass_first               (Optimize_common|Optimize_registers_explicit|Optimize_copy_and_call|Optimize_call_and_copy)
#define Optimize_pass_before_registrables (Optimize_ultrasafe)
#define Optimize_pass_middle              (Optimize_common)
#define Optimize_pass_last                (Optimize_common|Optimize_stack_frozen)


static Void optimize_remove_nouse_instructions_f(struct GeneratorContext *gc, Int flags) {
  struct Instruction *instr,*instr2,*next,*prev=null;
  struct Argument *arg1, *arg2, *narg1, *narg2, *arg;
  struct Function *f, *nf;
  int nb, nnb, i, j, nojump1, nojump2;

  for(instr=gc->first_instruction; instr!=null; instr=next) {
    prev=instr->previous_instruction;
    
    f = instr->function;
    nb = instr->arguments.nb;
    if (nb>=1)
      arg1 = IARG(instr,0);
    if (nb>=2)
      arg2 = IARG(instr,1);

    if ((instr2=next=instr->next_instruction)!=null) {
      nf = next->function;
      nnb=next->arguments.nb;
      if (nnb>=1)
        narg1 = IARG(next,0);
      if (nnb>=2)
        narg2 = IARG(next,1);
    }
    arg=null;
    
    // Max: each time an instruction is removed or changed,
    // restart optimizing from the PREVIOUS one
    if ((flags & Optimize_do_nothing) && f==G.function_do_nothing) {
      instr=GeneratorContext_remove(gc,instr);
      if (instr!=null && prev!=null && f!=G.function_do_nothing)
        next=prev;
      continue;
    }
    
    if ((f->flags&Function_flag_copy) && nb>=2) {
      
      // various optimizations on single <copy> instructions
      
      if ((flags & Optimize_copy_to_self) && Argument_is_shared(arg1,arg2)) {
        // remove redundant instructions like
        // i386_MOV EAX EAX
 
        optimize_listing_pair(gc, prev, instr, "0self,BEFORE");
        if (prev!=null)
          next=prev;
        instr2=GeneratorContext_remove(gc,instr);
        optimize_listing_pair(gc, prev, instr2, "0self,AFTER");
        continue;
        continue;
    other
      continue; }
    GeneratorContext_remove(gc,instr); } }
      }
      
      if ((flags & Optimize_copy_to_unused) &&
          ((j=arg2->where)==Argument_a_register || j==Argument_local ||
           (j==Argument_register && (flags & Optimize_registers_explicit))) &&
          arg2->first_instruction == instr && 
          arg2->last_instruction == instr) {
        // remove redundant instructions like
        // i386_MOV EAX EBX
        // 
        // where EBX is never used again.
        // 
        // for true registers, this is possible *ONLY* at the very beginning of optimization,
        // since later function call arguments will be removed
        // (they are implicit in machine language) so they will appear as not used.
        // 
        // also, only registers and locals can be thought as 'never used again'
        
        optimize_listing_pair(gc, prev, instr, "0write,BEFORE");
        instr2=GeneratorContext_remove(gc,instr);
        optimize_listing_pair(gc, prev, instr2, "0write,AFTER");
        if (prev!=null)
          next=prev;
        continue;
      }


      if ((flags & Optimize_copy_through_addr) &&
          next!=null &&
          (nf==G.function_i386_lea) && nnb>=2 &&
          List_first(&next->backward_jumps) == G.null_constant &&
          ((i=arg2->where) == Argument_a_register || i == Argument_local ||
           (i == Argument_register && (flags & Optimize_registers_explicit))) &&
          arg2->first_instruction == instr &&
          arg2->last_instruction == next &&
          narg1->where == Argument_indirect &&
          Argument_is_shared(arg2,arg=narg1->u.indirect.pointer) &&
          ((flags & Optimize_stack_frozen) || !Argument_is_dependent_on_ESP(arg)) &&
          !Argument_is_shared_or_dependent(arg1,arg) &&
          !Argument_is_shared_or_dependent(arg,narg2)) {
          
        // Max: optimize
        // <copy>      src      temp                 (instr)
        // i386_LEA   [temp+0]  dest                 (next)
        // to
        // <copy> src   dest
        // 
        // by merging temp with dest (restrictive, but safe)
        // this is possible _only_ if temp is not indirect and is not used anywhere else
        // 
        // in case instr->function is i386_mov, we must check that either src or dest
        // are registers (i386 constraint)
        // 
        // this is a specialized version of the combination
        // (Optimize_addr|Optimize_copy_through_temp)
        
        i=arg1->where;
        j=narg2->where;
        
        if (f==G.function_i386_mov &&
            i!=Argument_register && i!=Argument_a_register &&
            j!=Argument_register && j!=Argument_a_register)
          ;
        else {
          optimize_listing_pair(gc, instr, next, "1addr,BEFORE");
          GeneratorContext_suckup(gc,arg,narg2);
          instr2=GeneratorContext_remove(gc,next);
          optimize_listing_pair(gc, instr, instr2, "1addr,AFTER");
          next=instr;
          continue;
        }
      }
      


      if (next!=null && (nf->flags&Function_flag_copy) && nnb>=2) {
      
        // various optimizations on pairs of <copy> instructions

        if ((flags & Optimize_copy_through_temp) &&
            ((nojump1 = List_first(&instr->backward_jumps) == G.null_constant) ||
             (nojump2 = List_first(&next->backward_jumps) == G.null_constant)) &&
            ((i=arg2->where) == Argument_a_register || i == Argument_local ||
             (i == Argument_register && (flags & Optimize_registers_explicit))) &&
            arg2->first_instruction == instr &&
            arg2->last_instruction == next &&
            Argument_is_shared(arg2,narg1) &&
            !Argument_is_shared_or_dependent(arg1,arg2) &&
            !Argument_is_shared_or_dependent(narg1,narg2)) {
          
          // Max: optimize
          // <copy> src   temp                 (instr)
          // <copy> temp  dest                 (next)
          // to
          // <copy> src   dest
          // 
          // by merging temp with either src or dest
          // this is possible _only_ if temp is not indirect and is not used anywhere else
          // 
          // in case instr->function is i386_mov, we must check that either src or dest
          // are registers (i386 constraint)
          // 

          i=arg1->where;
          j=narg2->where;
          
          if (f==G.function_i386_mov &&
              i!=Argument_register && i!=Argument_a_register &&
              j!=Argument_register && j!=Argument_a_register)
            ;
          else {
            optimize_listing_pair(gc, instr, next, "1temp,BEFORE");
          
            /* prefer merging with a true register, not with something that *may* become a register */
            if (nojump1 && i != Argument_indirect &&
                (i == Argument_register || !nojump2 || j != Argument_register)) {
              
              GeneratorContext_suckup(gc,arg2,arg1);
              instr2=GeneratorContext_remove(gc,instr);
              optimize_listing_pair(gc, prev, instr2, "1temp,AFTER");
              if (prev)
                next=prev;
              continue;
            }
            if (nojump2 && j != Argument_indirect) {
              
              GeneratorContext_suckup(gc,narg1,narg2);
              instr2=GeneratorContext_remove(gc,next);
              optimize_listing_pair(gc, instr, instr2, "1temp,AFTER");
              next=instr;
              continue;
            }
            
            optimize_listing_pair(gc, null, null, "1temp,AFTER:FAILED!");
          }
        }

        if ((flags & Optimize_copy_loop) &&
            List_first(&next->backward_jumps) == G.null_constant &&
            Argument_is_shared(arg1,narg2) &&
            Argument_is_shared(narg1,arg2)) {
          
          // Max: optimize redundant instructions like
          // i386_MOV EAX EBX                 (instr)
          // i386_MOV EBX EAX                 (next)
          // to
          // i386_MOV EAX EBX                 (instr)
          // then restart optimizing the CURRENT instruction

          optimize_listing_pair(gc, instr, next, "1loop,BEFORE");
          instr2=GeneratorContext_remove(gc,next);
          optimize_listing_pair(gc, instr, instr2, "1loop,AFTER");
          next=instr;
          continue;
        }
        
        if ((flags & Optimize_copy_over) &&
            Argument_is_shared(arg2,narg2) &&
            !Argument_is_shared_or_dependent(arg1,arg2) &&
            !Argument_is_shared_or_dependent(narg1,narg2)) {
          
          // Max: optimize redundant instructions like
          // i386_MOV EAX EBX  (instr)
          // i386_MOV ECX EBX  (next)
          // to
          // i386_MOV ECX EBX  (next)
          // then restart optimizing the PREVIOUS instruction
          // 
          // this can be used only if both instructions do not
          // read the argument to be set, i.e.
          // i386_MOV EAX   EBX  (instr)
          // i386_MOV [EBX] EBX  (next)
          // cannot be optimized in this way.

          optimize_listing_pair(gc, instr, next, "1over,BEFORE");
          GeneratorContext_remove(gc,instr);
          optimize_listing_pair(gc, prev, next, "1over,AFTER");
          if (prev)
            next=prev;
          continue;
        }
      
        // end of optimizations on pairs of <copy> instructions
      }
      
      // end of optimizations on single <copy> instructions

    }

    if ((flags & Optimize_registers_explicit) && (flags & Optimize_call_and_copy) &&
        next!=null &&
        f->nb_arg < (j=f->nb_argres) && j==nb &&
        (nf->flags&Function_flag_copy) && nnb>=2 &&
        List_first(&next->backward_jumps) == G.null_constant &&
        (arg=IARG(instr,--j))->first_instruction == instr &&
        arg->last_instruction == next &&
        ((i=arg->where) == Argument_register || i == Argument_a_register || i == Argument_local) &&
        Argument_is_shared(arg,narg1) &&
        !Argument_is_shared_or_dependent(narg1,narg2) &&
        !Argument_is_shared_or_dependent_with_instruction(narg2,instr)) {

      // Max: Code equivalent to this optimization exists in pliant PDEE too,
      //      but this allows using optimization even before such code is compiled.
      //      
      //      This is the most important optimization.
      //      the reason it is so important is stated in pliant docs:
      //      
      //      given a non-atomic type (say, a big Matrix or a Str) with
      //      operators like '+' defined for it, the code
      //      
      //      Str a b c
      //      a := "foo"
      //      b := "bar"
      //      c := a + b
      //      
      //      should *NOT* perform costly copying of the non-atomic type.
      //      While most languages would compile `c := a+b' as:
      //      
      //      Str temp                     # probably on the stack
      //      (operator'+' a b -> temp)    # everything is passed by reference, temp may be implicit if it's on the stack
      //      ('copy Str' temp c)          # everything is passed by reference, but this performs an EXPENSIVE bit-by-bit copying of the objects
      //      
      //      Pliant instead, thanks to this optimization, tries really hard to compile `c := a+b' as:
      //      
      //      (operator'+' a b -> c)      # everything is passed by reference, no bit-by-bit copying of the objects
      // 
      // 
      // Max: optimize redundant instructions like
      // 
      // <function call> arg1 ... argnb -> result (current)
      // <copy> result dest                       (next)
      // to
      // <function call> arg1 ... argnb -> dest   (current)
      // 
      // this is possible _only_ if result is a local or register,
      // is not used anywhere else, and dest is not one of arg1 ... argnb
      // 
      // restart optimizing at the current instruction.
      // 
      // this is possible *ONLY* at the very beginning of optimization,
      // since later function call arguments will be removed
      // (they are implicit in machine language) so we cannot access them easily
        
      optimize_listing_pair(gc, instr, next, "2call,BEFORE");
      GeneratorContext_suckup(gc,narg1,narg2);
      instr2=GeneratorContext_remove(gc,next);
      optimize_listing_pair(gc, instr, instr2, "2call,AFTER");
      next=instr;
      continue;
    }

    
    if ((flags & Optimize_registers_explicit) && (flags & Optimize_copy_and_call) &&
        next!=null &&
        (f->flags&Function_flag_copy) && nb>=2 &&
        nf->nb_argres == nnb &&
        List_first(&instr->backward_jumps) == G.null_constant &&
        arg2->first_instruction == instr &&
        arg2->last_instruction == next &&
        ((i=arg2->where) == Argument_register || i == Argument_a_register || i == Argument_local) &&
        !Argument_is_shared_or_dependent(arg1,arg2) &&
        Argument_is_shared_with_instruction(arg2,next) &&
        !Argument_is_dependent_with_instruction(arg2,next) &&
        !Argument_is_shared_or_dependent_with_instruction(arg1,next)) {
      
      // Max: this is the dual version of the previous optimization:
      //      optimize redundant instructions like
      // 
      // <copy>  src temp                                        (instr)
      // <function call> arg1 ... temp ... argnb [-> result]     (next)
      // to
      // <function call> arg1 ... src  ... argnb [-> result]     (next)
      // 
      // the constraints are the same as above, plus:
      // temp must be passed as read-only to the function

      for (j=0; j<nnb; j++) {
        arg=IARG(next,j);
        if (Argument_is_shared(arg2,arg) &&
            (nf->arguments[j].access & (Access_write|Access_mapped|Access_result_write|Access_result_consistent)))
          break;
      }
      if (j==nnb) {
        optimize_listing_pair(gc, instr, next, "2call_in,BEFORE");
        GeneratorContext_suckup(gc,arg2,arg1);
        GeneratorContext_remove(gc,instr);
        optimize_listing_pair(gc, prev, next, "2call_in,AFTER");
        if (prev)
          next=prev;
        continue;
      }
    }

    if ((flags & Optimize_addr) &&
        f==G.function_i386_lea &&
        arg1->where==Argument_indirect && arg1->u.indirect.offset==0 &&
        ((flags & Optimize_stack_frozen) || !Argument_is_dependent_on_ESP(arg1))) {

      // optimize instructions like
      // i386_lea [EBX+0] EAX
      // to
      // i386_mov  EBX    EAX
      // 
      // if the first argument refers to register ESP, this can be performed only at the very
      // end of optimization since before we still have to invoke optimize_adjust_stacked_offsets(),
      // which sometimes changes offsets of i386_lea [ESP+?] <...> due to data pushed/popped on the stack.
      // 
      // such a trick *ONLY* works if variables are *NEVER* aliased to ESP register!

      optimize_listing_pair(gc, prev, instr, "3lea,BEFORE");
      if(!Argument_is_shared(arg1->u.indirect.pointer,arg2))
        GeneratorContext_insert_after_instruction(gc,instr,instruction(G.function_i386_mov,arg1->u.indirect.pointer,arg2,END));
      instr2=GeneratorContext_remove(gc,instr);
      optimize_listing_pair(gc, prev, instr2, "3lea,AFTER");
      if (prev!=null)
        next=prev;
      continue;
    }
    
    if ((flags & Optimize_math) &&
        nb>=2 && (f==G.function_i386_add || f==G.function_i386_sub)) {
      
      if (arg1->where==Argument_constant && arg1->type==G.type_Int &&
          *(Int *)arg1->u.constant==0) {
        // Max: remove
        // i386_add 0 <arg2>
        // and
        // i386_sub 0 <arg2>
        optimize_listing_pair(gc, prev, instr, "4math,BEFORE");
        instr2=GeneratorContext_remove(gc,instr);
        optimize_listing_pair(gc, prev, instr2, "4math,AFTER");
        if (prev)
          next=prev;
        continue;
      }
    }
  }
}


static Void optimize_remove_nouse_instructions(struct GeneratorContext *gc) {
  optimize_remove_nouse_instructions_f(gc,Optimize_pass_first);
}

/*
doc
  [Removes jump instructions where the target instruction is
*/


static Void optimize_rewrite_assembly(struct GeneratorContex
/*
doc
  [Removes jump instructions where the target instruction is
*/


static Void optimize_rewrite_assembly(struct GeneratorContex
  struct Instruction *instr,*instr2;
  struct Argument *src,*dest;
  for(instr=gc->first_instruction; instr!=null; instr=instr2
    instr2=instr->next_instruction;
    if(instr->function==G.function_i386_mov) {
      src=IARG(instr,0),dest=IARG(instr,1);
      if(Argument_is_shared(src,dest))
        GeneratorContext_remove(gc,instr);
    orif(instr->function==G.function_i386_lea)
      src=IARG(instr,0),dest=IARG(instr,1);
      if(src->where==Argument_indirect && src->u.indirect.of
        if(!Argument_is_shared(src->u.indirect.pointer,dest)
          GeneratorContext_insert_after_instruction(gc,instr
        GeneratorContext_remove(gc,instr); } } } }
  optimize_remove_nouse_instructions_f(gc,Optimize_pass_last);
}



static Void optimize_allocate_registrables(struct GeneratorC
  Int conservative;
  Int lap,nextlap,r;
  struct List simples,generals,locals;
  Arrow *c,*c2; struct Argument *arg,*master;
  #ifdef _LISTING_
    static Char *registers[8]={"EAX","ECX","EDX","EBX","ESP"
    struct Str temp; Char buffer[16];
  #endif
  GeneratorContext_renumber(gc);
  GeneratorContext_rebuild(gc);



static Void optimize_allocate_registrables(struct GeneratorC
  Int conservative;
  Int lap,nextlap,r;
  struct List simples,generals,locals;
  Arrow *c,*c2; struct Argument *arg,*master;
  #ifdef _LISTING_
    static Char *registers[8]={"EAX","ECX","EDX","EBX","ESP"
    struct Str temp; Char buffer[16];
  #endif
  GeneratorContext_renumber(gc);
  GeneratorContext_rebuild(gc);
#if 0  
  optimize_listing_pair(gc, null, null, "allocate_registrables,BEFORE");
  optimize_listing_pair(gc, null, null, "allocate_registrables,AFTER");
#endif
  optimize_remove_nouse_instructions_f(gc,Optimize_pass_before_registrables);
  GeneratorContext_renumber(gc);
  
  #ifdef _LISTING_
    if(please_trace(gc)) {
      optimize_listing(gc);
      for(r=0; r<nb_register; r++)
        string_Str(registers[r],&gc->registers[r]->name); }
  #endif
  share_begin(gc);
  List_build(&simples),List_build(&generals);
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=Li
    arg=(struct Argument *)*c; if(arg->first_instruction==nu
    if(arg->where==Argument_local) {
      if(!argument_is_byvalue(arg)) {
      orif(argument_is_simple(arg))
        List_append(&simples,arg);
      other
        List_append(&generals,arg); }
    orif(arg->where==Argument_a_register)
      check(argument_is_byvalue(arg));
      check(argument_is_simple(arg)); } }
  conservative=0;
  restart:
  List_build(&locals);
  for(r=0; r<nb_register; r++)
    List_append(&locals,gc->registers[r]);
  if(G.generator_level>0)
    for(lap=0; lap<4; lap=nextlap) {
      #ifdef _LISTING_
        if(please_trace(gc))
          consolen(Z,"lap ",S,Str_map_area(&temp,buffer,Int_
      #endif
      nextlap=lap+1;
      for(c=List_first(lap==0 || lap==2 ? &gc->arguments : &
        arg=(struct Argument *)*c;
        if(arg->first_instruction==null || boundto(arg)!=arg
        if((lap==0 || lap==2) && arg->where!=Argument_a_regi
        if(lap<2 && arg->last_instruction->order-arg->first_
        master=argument_prefered(arg);
        if(master==null) continue;
        if(arg->where==Argument_a_register && !(arg->u.possi
        if(lap==1 || lap==3) {
          if(conservative==2) continue;
          if(conservative==1 && master->u.cpu_register==Regi
        if(!share_try(gc,arg,master,true,false,true)) contin
        nextlap=0; } }
  for(lap=4; lap<(G.generator_level>=2 ? 7 : G.generator_lev
    #ifdef _LISTING_
      if(please_trace(gc))
        consolen(Z,"lap ",S,Str_map_area(&temp,buffer,Int_st
    #endif
    for(c=List_first(lap==4 ? &gc->arguments : lap==5 ? &sim
      arg=(struct Argument *)*c;
      if(arg->first_instruction==null || boundto(arg)!=arg) 
      if(lap==4 && arg->where!=Argument_a_register) continue
      for(c2=List_first(&locals); c2!=G.null_constant; c2=Li
        master=(struct Argument *)*c2;
        if(master->where==Argument_register) {
          if(arg->where==Argument_a_register && !(arg->u.pos
            continue;
        other
          if(arg->where==Argument_a_register)
            continue; }
        if(share_try(gc,arg,master,lap<6,false,true))
          goto done; }
      if(arg->where==Argument_a_register && conservative<2) 
        List_destroy(&locals);
        share_begin(gc);
        conservative++;
        goto restart; }
  #ifdef _LISTING_
    if(please_trace(gc)) {
      optimize_listing(gc);
      for(r=0; r<nb_register; r++)
        string_Str(registers[r],&gc->registers[r]->name); }
  #endif
  share_begin(gc);
  List_build(&simples),List_build(&generals);
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=Li
    arg=(struct Argument *)*c; if(arg->first_instruction==nu
    if(arg->where==Argument_local) {
      if(!argument_is_byvalue(arg)) {
      orif(argument_is_simple(arg))
        List_append(&simples,arg);
      other
        List_append(&generals,arg); }
    orif(arg->where==Argument_a_register)
      check(argument_is_byvalue(arg));
      check(argument_is_simple(arg)); } }
  conservative=0;
  restart:
  List_build(&locals);
  for(r=0; r<nb_register; r++)
    List_append(&locals,gc->registers[r]);
  if(G.generator_level>0)
    for(lap=0; lap<4; lap=nextlap) {
      #ifdef _LISTING_
        if(please_trace(gc))
          consolen(Z,"lap ",S,Str_map_area(&temp,buffer,Int_
      #endif
      nextlap=lap+1;
      for(c=List_first(lap==0 || lap==2 ? &gc->arguments : &
        arg=(struct Argument *)*c;
        if(arg->first_instruction==null || boundto(arg)!=arg
        if((lap==0 || lap==2) && arg->where!=Argument_a_regi
        if(lap<2 && arg->last_instruction->order-arg->first_
        master=argument_prefered(arg);
        if(master==null) continue;
        if(arg->where==Argument_a_register && !(arg->u.possi
        if(lap==1 || lap==3) {
          if(conservative==2) continue;
          if(conservative==1 && master->u.cpu_register==Regi
        if(!share_try(gc,arg,master,true,false,true)) contin
        nextlap=0; } }
  for(lap=4; lap<(G.generator_level>=2 ? 7 : G.generator_lev
    #ifdef _LISTING_
      if(please_trace(gc))
        consolen(Z,"lap ",S,Str_map_area(&temp,buffer,Int_st
    #endif
    for(c=List_first(lap==4 ? &gc->arguments : lap==5 ? &sim
      arg=(struct Argument *)*c;
      if(arg->first_instruction==null || boundto(arg)!=arg) 
      if(lap==4 && arg->where!=Argument_a_register) continue
      for(c2=List_first(&locals); c2!=G.null_constant; c2=Li
        master=(struct Argument *)*c2;
        if(master->where==Argument_register) {
          if(arg->where==Argument_a_register && !(arg->u.pos
            continue;
        other
          if(arg->where==Argument_a_register)
            continue; }
        if(share_try(gc,arg,master,lap<6,false,true))
          goto done; }
      if(arg->where==Argument_a_register && conservative<2) 
        List_destroy(&locals);
        share_begin(gc);
        conservative++;
        goto restart; }
      #if 1
      if(arg->where==Argument_a_register)
        consolen(Z,"pliant warning 1: failed to allocate a register for ",S,&arg->name,EOL);
      #else
      check(arg->where!=Argument_a_register);
      check(arg->where!=Argument_a_register);
      #endif
      List_append(&locals,arg);
      done:; } }
  List_destroy(&locals);
  List_destroy(&simples),List_destroy(&generals);
  GeneratorContext_rebuild(gc);
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=Li
    arg=(struct Argument *)*c;
    if(boundto(arg)!=arg)
      GeneratorContext_suckup(gc,arg,boundto(arg)); }
  share_end(gc);
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=Li
    arg=(struct Argument *)*c; if(arg->first_instruction==nu
      List_append(&locals,arg);
      done:; } }
  List_destroy(&locals);
  List_destroy(&simples),List_destroy(&generals);
  GeneratorContext_rebuild(gc);
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=Li
    arg=(struct Argument *)*c;
    if(boundto(arg)!=arg)
      GeneratorContext_suckup(gc,arg,boundto(arg)); }
  share_end(gc);
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=Li
    arg=(struct Argument *)*c; if(arg->first_instruction==nu
    #if 1
    if(arg->where==Argument_a_register)
      consolen(Z,"pliant warning 2: failed to allocate a register for ",S,&arg->name,EOL);
    #else
    check(arg->where!=Argument_a_register);
    check(arg->where!=Argument_a_register);
    if(arg->where==Argument_local)
    #endif
    if(arg->where==Argument_local || arg->where==Argument_a_register)
      allocate_on_stack(arg,gc); }
  remove_unused_args(gc);
      allocate_on_stack(arg,gc); }
  remove_unused_args(gc);
  optimize_remove_nouse_instructions(gc); }
  optimize_remove_nouse_instructions_f(gc,Optimize_pass_middle); }