Patch title: Release 91 bulk changes
Abstract:
File: /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 and/or
// modify it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// version 2 along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

/*
abstract
  [The basic built in code optimizer and code generator] ; eol
  [It's main function is to decide which arguments can be shared, and assign some to processor registers optimally.] ; eol
*/
 

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


static Void remove_unused_args(struct GeneratorContext *gc) {
  Arrow *c; struct Argument *arg;
  for(c=List_first(&gc->arguments); c!=G.null_constant; ) {
    arg=(struct Argument *)*c;
    if(arg->first_instruction!=null)
      c=List_next(c);
    else
      c=List_remove(&gc->arguments,c); } }


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

/*
doc
  [This is the very much time consuming 'share_test_general' function that will determine if argument 'aux' can be safely shared with argument 'master'.] ; eol
  ['share_test_simple' is a faster version of 'share_test_general' for simple arguments that is expected to always produce the same result.] ; eol
  ['share_test_instruction' will perform the test on a single instruction. We know which of 'master' and 'ok' are defined at the beginning of the instruction (flags argument) and on return, we provide the new flags set at the end of the instruction.] ; eol
  ['share_test_sequence' will perform the test on a set of instructions, and recursively calls itself for each conditional jump in the program, so we mark (set 'share_marked' flag) all tested instructions in order to avoid infinite looping.]
*/


#define aux_ok        0x01
#define master_ok     0x02
#define share_failed  0x10
#define share_marked  0x20


INLINE struct Argument *boundto(struct Argument *arg) {
  return (struct Argument *)arg->user_field; }

INLINE struct Argument *BARG(struct Instruction *instr,Int index) {
  return boundto(IARG(instr,index)); }

INLINE Bool function_is_external(struct Function *f) {
  return f->generate_binary==null; }
  

static Int share_test_instruction(struct Instruction *instr,Int flags,struct Argument *aux,struct Argument *master) {
  Int nextflags,test;
  struct Function *f;
  Int i,j; struct Argument *a,*a2; Int m;
  nextflags=flags;
  f=instr->function;
  if(master->where==Argument_register && function_is_external(f)) {
    if(bit_test(f->modify_registers,master->u.cpu_register))
      nextflags=0;
    for(i=0; i<f->nb_argres; i++)
      if(f->arguments[i].cpu_register==master->u.cpu_register) {
        m=f->arguments[i].access;
        if(((m&Access_read) || !(m&Access_byvalue)) && !(flags&master_ok))
          return share_failed;
        if((m&Access_write) && (m&(Access_byvalue|Access_mapped)))
          nextflags=master_ok; } }
  for(i=0; i<instr->arguments.nb; i++) {
    a=BARG(instr,i);
    if(a==aux) {
      m=f->arguments[i].access;
      if((m&Access_read) && !(flags&aux_ok))
        return share_failed;
      if(m&Access_write)
        nextflags=aux_ok; }
    if(a==master) {
      m=f->arguments[i].access;
      if((m&Access_read) && !(flags&master_ok))
        return share_failed;
      if(m&Access_write)
        nextflags=master_ok; }
    while(a->where==Argument_indirect) {
      a=boundto(a->u.indirect.pointer);
      if(a==aux && !(flags&aux_ok))
        return share_failed;
      if(a==master && !(flags&master_ok))
        return share_failed; } }
  if(instr->arguments.nb>f->nb_arg && !(f->flags&Function_flag_allow_shared_result)) {
    a=BARG(instr,f->nb_arg);
    if(a==aux || a==master)
      for(i=0; i<instr->arguments.nb; i++) {
        a2=BARG(instr,i);
        if((a2==aux || a2==master) && a2!=a)
          return share_failed; } }
  if((f->flags&Function_flag_copy) && instr->arguments.nb>=2) {
    a=BARG(instr,0),a2=BARG(instr,1);
    if((a==aux && a2==master && (flags&aux_ok)) || (a==master && a2==aux && (flags&master_ok)))
      nextflags=aux_ok|master_ok; }
  return nextflags; }


static Bool share_test_sequence(struct Instruction *instr,Int flags,struct Argument *aux,struct Argument *master) {
  Int oldflags;
  Int f;
  while(instr!=null) {
    oldflags=*(Int *)&instr->user_field;
    if(oldflags&share_marked) {
      if((oldflags&master_ok)<=(flags&master_ok) && (oldflags&aux_ok)<=(flags&aux_ok))
        return true;
      flags&=oldflags; }
    *(Int *)&instr->user_field=flags|share_marked;
    flags=share_test_instruction(instr,flags,aux,master);
    if(flags==share_failed)
      return false;
    if(instr->function==G.function_i386_jump) {
      instr=instr->jump;
      continue; }
    if(instr->jump!=null) {
      if(!share_test_sequence(instr->jump,flags,aux,master))
        return false; }
    instr=instr->next_instruction; }
  return true; }


static Bool share_test_general(struct Argument *aux,struct Argument *master,struct GeneratorContext *gc) {
  Bool result; struct Instruction *instr;
  #ifdef _CHECK_
    for(instr=gc->first_instruction; instr!=null; instr=instr->next_instruction)
      check(instr->user_field==null);
  #endif
  result=share_test_sequence(gc->first_instruction,aux_ok|master_ok,aux,master);
  for(instr=gc->first_instruction; instr!=null; instr=instr->next_instruction)
    instr->user_field=null;
  return result; }


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


/*
doc
  ['argument_is_byvalue' tests if the argument is always used by value (never by address).]
*/

static Bool argument_is_byvalue(struct Argument *arg) {
  struct Instruction *instr; Int i; struct Argument *a;
  for(instr=arg->first_instruction;; instr=instr->next_instruction) {
    for(i=0; i<instr->arguments.nb; i++) {
      a=IARG(instr,i);
      if(a==arg)
        #ifdef _EXPERIMENTAL_
          if(!(instr->function->arguments[i].access&(Access_byvalue|Access_mapped)))
        #else
          if(!(instr->function->arguments[i].access&Access_byvalue))
        #endif
          return false; }
    if(instr==arg->last_instruction)
      break; }
  return true; }


/*
doc
  ['argument_is_mapped' tests if one of the instructions may return a pointer to a part of the argument. In this case, it's safer not to share or discard the argument value.] 
*/


static Bool argument_is_mapped(struct Argument *arg) {
  struct Instruction *instr; struct Function *f; Int i,j;
  for(instr=arg->first_instruction;; instr=instr->next_instruction) {
    f=instr->function; check(instr->arguments.nb==f->nb_argres);
    for(i=0; i<instr->arguments.nb; i++)
      if(IARG(instr,i)==arg && !(f->arguments[i].access&Access_byvalue))
       for(j=0; j<instr->arguments.nb; j++) {
          if(!(f->arguments[j].access&Access_write))
            continue;
          if(!(f->arguments[j].access&Access_mapped) && !(f->arguments[j].type->flags&Type_flag_mapper) && !(f->arguments[j].maps&(1<<minimum(i,30))))
            continue;
          if(!(f->arguments[j].maps&((1<<minimum(i,30))|int_bad)))
            continue;
          if(j!=i)
            return true; }
    if(instr==arg->last_instruction)
      break; }
  return false; }
  

/*
doc
  [A simple argument is written first, then red, possibly several times, in each sequence of the code where it appears. ]
  [It means that it can be discared at the end of each sequence where it appears. ]
  [I call a sequence a subset of consecutive instructions that are always entered by the first one (there are no jumps to one of them in the middle).]
*/

static Int test_instruction(struct Instruction *instr,Int flags,struct Argument *aux) {
  Int nextflags;
  struct Function *f;
  Int i,j; struct Argument *a,*a2; Int m;
  nextflags=flags;
  f=instr->function;
  if(aux->where==Argument_register && function_is_external(f)) {
    if(bit_test(f->modify_registers,aux->u.cpu_register))
      nextflags=0;
    for(i=0; i<f->nb_argres; i++)
      if(f->arguments[i].cpu_register==aux->u.cpu_register) {
        m=f->arguments[i].access;
        if(((m&Access_read) || !(m&Access_byvalue)) && !(flags&master_ok))
          return share_failed;
        if((m&Access_write) && (m&(Access_byvalue|Access_mapped)))
          nextflags=aux_ok; } }
  for(i=0; i<instr->arguments.nb; i++) {
    a=BARG(instr,i);
    if(a==aux) {
      m=f->arguments[i].access;
      if((m&Access_read) && !(flags&aux_ok))
        return share_failed;
      if(m&Access_write)
        nextflags=aux_ok; }
    while(a->where==Argument_indirect) {
      a=boundto(a->u.indirect.pointer);
      if(a==aux && !(flags&aux_ok))
        return share_failed; } }
  if(instr->arguments.nb>f->nb_arg && !(f->flags&Function_flag_allow_shared_result)) {
    a=BARG(instr,f->nb_arg);
    if(a==aux)
      for(i=0; i<instr->arguments.nb; i++) {
        a2=BARG(instr,i);
        if(a2==aux && a2!=a)
          return share_failed; } }
  return nextflags; }


static Bool argument_is_simple(struct Argument *arg) {
  struct Instruction *instr; Int flags;
  if(arg->first_instruction==null) return true;
  flags=0;
  for(instr=arg->first_instruction;; instr=instr->next_instruction) {
    if(List_first(&instr->backward_jumps)!=G.null_constant)
      flags=0;
    flags=test_instruction(instr,flags,arg);
    if(flags==share_failed)
      return false;
    if(instr==arg->last_instruction) break; }
  return true; }


static Bool share_test_simple(struct Argument *aux,struct Argument *master) {
  struct Instruction *instr; Int flags;
  flags=aux_ok|master_ok;
  instr=aux->first_instruction;
  while(instr!=null) {
    flags=share_test_instruction(instr,flags,aux,master);
    if(flags==share_failed)
      return false;
    if(instr->order>=aux->last_instruction->order) {
      if((flags&master_ok) || List_first(&instr->backward_jumps)!=G.null_constant)
        break;
      if(function_is_external(instr->function) && (master->last_instruction==null || instr->order>=master->last_instruction->order))
        break; }
    instr=instr->next_instruction; }
  return true; }



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


/*
doc
  ['argument_is_used' tests if the instruction is using the argument.] ; eol
  ['argument_clash' tests if we'd better note share the two arguments because in an instruction in the program, we use both of them.] ; eol
*/

static Int argument_is_used(struct Argument *arg,struct Instruction *instr) {
  Int i; struct Argument *a;
  for(i=0; i<instr->arguments.nb; i++)
    for(a=BARG(instr,i); a!=null; a=(a->where!=Argument_indirect ? null : boundto(a->u.indirect.pointer)))
      if(a==arg)
        return true;
  return false; }
  
static Bool share_clash(struct GeneratorContext *gc,struct Argument *arg1,struct Argument *arg2) {
  struct Instruction *first,*last,*instr;
  if(arg1->first_instruction==null || arg2->first_instruction==null)
    return false;
  first=(arg1->first_instruction->order>arg2->first_instruction->order ? arg1->first_instruction : arg2->first_instruction);
  last=(arg1->last_instruction->order<arg2->last_instruction->order ? arg1->last_instruction : arg2->last_instruction);
  if(first->order<=last->order)
    for(instr=first;; instr=instr->next_instruction) {
      if(!(instr->function->flags&Function_flag_copy) && argument_is_used(arg1,instr) && argument_is_used(arg2,instr))
        return true;
      if(instr==last)
        break; }
  return false; }


/*
doc
  ['share_try' is the top most function that will determine wether it's safe or not to share the two arguments.] ; eol
*/

static Void share_begin(struct GeneratorContext *gc) {
  Arrow *c; struct Argument *arg; Int r;
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=List_next(c)) {
    arg=(struct Argument *)*c;
    arg->user_field=(Address)arg; }
  for(r=0; r<nb_register; r++)
    gc->registers[r]->user_field=(Address)gc->registers[r]; }


static Void share_end(struct GeneratorContext *gc) {
  Arrow *c; struct Argument *arg; Int r;
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=List_next(c)) {
    arg=(struct Argument *)*c;
    arg->user_field=null; }
  for(r=0; r<nb_register; r++)
    gc->registers[r]->user_field=null; }


static Bool share_try(struct GeneratorContext *gc,struct Argument *arg,struct Argument *master,Bool simple,Bool suckup,Bool bound) {
  struct Instruction *i; struct Function *f; Int j; struct Argument *src,*dest;
  Arrow *c; struct Argument *a; struct Instruction *instr;
  if( ( !(arg->type->flags&Type_flag_atomic) || !(master->type->flags&Type_flag_atomic) ) && arg->type!=master->type )
    return false;
  if( ( Str_len(&arg->name)!=0 || Str_len(&master->name)!=0 ) && G.debugging_level>=2)
    return false;
  if(master->where==Argument_register)
    if(master->u.cpu_register==Register_ESP || (G.debugging_level>0 && master->u.cpu_register==Register_EBP))
      return false;
  check(master!=arg);
  check(master->where==Argument_register || master->where==Argument_a_register || master->where==Argument_local || master->where==Argument_constant);
  check(arg->where!=Argument_a_register || master->where==Argument_register);
  if(share_clash(gc,arg,master))
    return false;
  if(simple ? share_test_simple(arg,master) : share_test_general(arg,master,gc)) {
    #ifdef _LISTING_
      if(please_trace(gc))
        consolen(Z,"  ",S,&arg->name,Z," -> ",S,&master->name,EOL);
    #endif
    if(suckup) {
      GeneratorContext_suckup(gc,arg,master);
    orif(bound)
      arg->user_field=(Address)master;
      if(master->first_instruction==null || arg->first_instruction->order<master->first_instruction->order)
        master->first_instruction=arg->first_instruction;
      if(master->last_instruction==null || arg->last_instruction->order>master->last_instruction->order)
        master->last_instruction=arg->last_instruction; }
    return true; }
  return false; }

static Bool share_try2(struct GeneratorContext *gc,struct Argument *arg,struct Argument *master) {
  struct Instruction *i; struct Function *f; Int j; struct Argument *src,*dest;
  return share_try(gc,arg,master,argument_is_simple(arg) && argument_is_simple(master),true,false); }


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


/*
doc
  [Below are the various code generator functions that will slowly turn a set of Pliant instructions to an set of assembly instructions, and finally binary code.]
  [The order in which these functions are called is defined in ] ; link "startup.c" "../startup/startup.c" section "optimize" ; [.]
*/

/*
doc
  [Adds 'pliant push size' and 'pliant locals size' in the generator context dictionary. These variables will be updated when local variables are assigned a position on the stack, so that at the end of the code generating process we can add the stack pointer adjustment instructions.]
*/

static Void optimize_declare(struct GeneratorContext *gc) {
  Arrow pushsize,localssize; struct Str temp;
  pushsize=entry(G.type_Int,G.debugging_level ? 4 : 0,END);
  Dictionary_insert(&gc->locals,Str_map_string(&temp,"pliant push size"),true,pushsize);
  localssize=entry(G.type_Int,0,END);
  Dictionary_insert(&gc->locals,Str_map_string(&temp,"pliant locals size"),true,localssize); }


/*
doc
  [Adds the instructions that will copy arguments passed by registers to the local variable that will store the value of the parameter while the function is running. The same appends the other way round for the result. ]
  [For arguments that are passed on the stack, we set them as indirect pointed by the stack register.]
*/

static Void optimize_add_copy_instructions(struct GeneratorContext *gc) {
  Arrow *c1,*c2; struct Str temp;
  struct Function *function; struct Array *arguments;
  Int i; struct FunctionPrototype *parg; struct Argument *barg;
  struct Argument *arg; struct Instruction *instr;
  c1=Module_first(gc->module,Str_map_string(&temp,"pliant function")); if(c1==G.null_constant) return;
  function=(struct Function *)*c1;
  c2=Module_first(gc->module,Str_map_string(&temp,"pliant arguments")); check(c2!=G.null_constant);
  arguments=(struct Array *)*c2;
  for(i=0; i<function->nb_argres; i++) {
    parg=function->arguments+i;
    barg=(struct Argument *)Array_get_index(arguments,i);
    if(parg->access&Access_byvalue) {
      arg=barg;
    orif(parg->access&Access_mapped)
      arg=barg;
    other
      arg=(struct Argument *)entry_new(G.type_Argument);
      Argument_locate(barg,parg->type,Argument_indirect);
      Arrow_set((Arrow *)&barg->u.indirect.pointer,arg);
      barg->u.indirect.offset=0; }
    if(parg->cpu_register<0) {
      Argument_locate(arg,G.type_Int,Argument_indirect);
      Arrow_set((Arrow *)&arg->u.indirect.pointer,gc->registers[Register_ESP]);
      arg->u.indirect.offset=-parg->cpu_register*sizeof(Int)+int_max/2;
    other
      Argument_locate(arg,G.type_Int,Argument_local);
      if( (parg->access&Access_read) || !(parg->access&(Access_byvalue|Access_mapped)) ) {
        instr=instruction(G.function_i386_mov,gc->registers[parg->cpu_register],arg,END);
        GeneratorContext_insert_at_section_bottom(gc,Section_header,instr); }
      if( (parg->access&Access_write) && (parg->access&(Access_byvalue|Access_mapped)) ) {
        instr=instruction(G.function_i386_mov,arg,gc->registers[parg->cpu_register],END);
        GeneratorContext_insert_at_section_top(gc,Section_tail,instr); } } }
  GeneratorContext_rebuild(gc); }


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


/*
doc
  [Checks if the function may have side effects or generate errors.]
*/

static Void optimize_check_side_effects(struct GeneratorContext *gc) {
  struct Function *function; Arrow *c1;  struct Str temp;
  struct Instruction *instr;
  c1=Module_first(gc->module,Str_map_string(&temp,"pliant function")); if(c1==G.null_constant) return;
  function=(struct Function *)*c1;
  if(function->flags&Function_flag_has_no_side_effect)
    return;
  for(instr=gc->first_instruction; instr!=null; instr=instr->next_instruction) {
    if(instr->function->flags&Function_flag_has_side_effects)
      function->flags|=Function_flag_has_side_effects;
    if(instr->function->flags&Function_flag_may_generate_error)
      function->flags|=Function_flag_may_generate_error; } }


/*
doc
  [Replaces inline functions call by the inline instructions set.]
*/

static Void optimize_inline_functions(struct GeneratorContext *gc) {
  struct Instruction *instr,*instr2; struct Function *fun;
  struct Relation relation; struct List instructions; Arrow *r; struct Instruction *current;
  struct Instruction *i; Int u; struct Argument *a;
  struct ListingPositions p;
  for(instr=gc->first_instruction; instr!=null; instr=instr2) {
    instr2=instr->next_instruction;
    fun=instr->function;
    if(fun->flags&Function_flag_inline_instructions) {
      if(fun==gc->function) {
       fun->flags&=~Function_flag_inline_instructions;
       continue; }
      if(fun->flags&Function_flag_under_construction)
        continue;
      Relation_build(&relation); Relation_set_flags(&relation,4);
      check(fun->nb_argres==instr->arguments.nb);
      for(u=0; u<fun->nb_argres; u++) {
        a=Array_get_index(&instr->arguments,u);
        if(fun->arguments[u].access&Access_mapped) {
          check(a->where==Argument_indirect && a->u.indirect.offset==0);
          a=a->u.indirect.pointer; }
        Relation_define(&relation,fun->arguments[u].inline_argument,null,a); }
      List_build(&instructions);
      for(r=List_first(&fun->inline_instructions); r!=G.null_constant; r=List_next(r)) {
       i=Instruction_copy((struct Instruction *)*r,&relation);
        ListingPositions_build(&p);
        ListingPositions_copy(&i->position,&p);
        ListingPositions_concat(&instr->position,&p,&i->position);
        ListingPositions_destroy(&p);
        List_append(&instructions,i); }
      for(r=List_first(&instructions); r!=G.null_constant; r=List_next(r)) {
        i=(struct Instruction *)*r;
        if(i->jump==null) continue;
        i->jump=Relation_query(&relation,i->jump,null); check(i->jump!=null); }
      current=instr;
      for(r=List_first(&instructions); r!=G.null_constant; r=List_next(r)) 
        current=GeneratorContext_insert_after_instruction(gc,current,(struct Instruction *)*r);
      List_destroy(&instructions);
      Relation_destroy(&relation);
      instr2=instr->next_instruction;
      GeneratorContext_remove(gc,instr); } }
  GeneratorContext_rebuild(gc); }


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


/*
doc
  [Removes some instructions that do nothing (such as copying a variable into itself).]
*/

static Void optimize_remove_nouse_instructions(struct GeneratorContext *gc) {
  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(instr,0),IARG(instr,1)))
        continue;
    other
      continue; }
    GeneratorContext_remove(gc,instr); } }


/*
doc
  [Removes jump instructions where the target instruction is the next one. (Mainly append when the last expression of the function was a 'return' instruction)]
*/

static Void optimize_remove_next_instruction_jumps(struct GeneratorContext *gc) {
  struct Instruction *instr;
  for(instr=gc->first_instruction; instr!=null; )
    if(instr->function==G.function_i386_jump && instr->jump==instr->next_instruction)
      instr=GeneratorContext_remove(gc,instr);
    else
      instr=instr->next_instruction; }


/*
doc
  [Replace some instruction by a faster one that does the same.]
*/

static Void optimize_rewrite_instructions(struct GeneratorContext *gc) {
  struct Instruction *instr,*instr2;
  struct Argument *targ,*size; struct Type *type;
  struct Function *direct;
  for(instr=gc->first_instruction; instr!=null; instr=instr2) {
    instr2=instr->next_instruction;
    if(instr->function==G.function_copy_universal) {
      targ=IARG(instr,2); check(targ->where==Argument_constant && entry_type(targ->u.constant)==G.type_Type);
      type=(struct Type *)targ->u.constant;
      if(Argument_is_shared(IARG(instr,0),IARG(instr,1))) {
        GeneratorContext_remove(gc,instr);
      orif((type->flags&Type_flag_atomic) && (type->flags&Type_flag_scalar))
        GeneratorContext_insert_after_instruction(gc,instr,instruction(G.function_copy_atomic,IARG(instr,0),IARG(instr,1),END));
        GeneratorContext_remove(gc,instr);
      orif(type->flags&Type_flag_scalar)
        size=argument(G.type_Int,Argument_constant,entry(G.type_Int,type->size,END));
        GeneratorContext_insert_after_instruction(gc,instr,instruction(G.function_copy_scalar,IARG(instr,0),IARG(instr,1),size,END));
        GeneratorContext_remove(gc,instr);
      orif((type->flags&Type_flag_direct_copy) || type->nb_field==0)
        direct=Type_get_generic_method(type,G.generic_method_copy);
        GeneratorContext_insert_after_instruction(gc,instr,instruction(direct,IARG(instr,0),IARG(instr,1),END));
        GeneratorContext_remove(gc,instr); }
    orif(instr->function==G.function_build_universal)
      targ=IARG(instr,0); check(targ->where==Argument_constant && entry_type(targ->u.constant)==G.type_Type);
      type=(struct Type *)targ->u.constant;
      if((type->flags&Type_flag_direct_build) || type->nb_field==0) {
        direct=Type_get_generic_method(type,G.generic_method_build);
        GeneratorContext_insert_after_instruction(gc,instr,instruction(direct,IARG(instr,1),END));
        GeneratorContext_remove(gc,instr); }
    orif(instr->function==G.function_destroy_universal)
      targ=IARG(instr,0); check(targ->where==Argument_constant && entry_type(targ->u.constant)==G.type_Type);
      type=(struct Type *)targ->u.constant;
      if((type->flags&Type_flag_direct_destroy) || type->nb_field==0) {
        direct=Type_get_generic_method(type,G.generic_method_destroy);
        GeneratorContext_insert_after_instruction(gc,instr,instruction(direct,IARG(instr,1),END));
        GeneratorContext_remove(gc,instr); } } } }


/*
doc
  [Replaces 'if integer comparison then jump' by a shorter and faster equivalent method.]
*/

static Void optimize_int_compare_ifs(struct GeneratorContext *gc) {
  struct Instruction *instr,*instr2,*instr3;
  struct Argument *carg,*marg,*barg,*arg; Int mode;
  struct Instruction *current;
  struct Instruction *i;
  for(instr=gc->first_instruction; instr!=null; instr=instr->next_instruction) {
    if(instr->function!=G.function_compare_int && instr->function!=G.function_compare_address) continue;
    instr2=instr->next_instruction; if(instr2==null) continue;
    if(instr2->function!=G.function_compare_apply_mode) continue;
    instr3=instr2->next_instruction; if(instr3==null) continue;
    if(instr3->function!=G.function_jump_if && instr3->function!=G.function_jump_if_not) continue;
    carg=IARG(instr,2); if(carg->first_instruction!=instr || carg->last_instruction!=instr2) continue;
    if(IARG(instr2,0)!=carg) continue;
    marg=IARG(instr2,1); if(marg->where!=Argument_constant) return; check(entry_type(marg->u.constant)==G.type_Int);
    barg=IARG(instr2,2); if(barg->first_instruction!=instr2 || barg->last_instruction!=instr3) continue;
    if(IARG(instr3,0)!=barg) continue;
    mode = *(Int *)marg->u.constant & (compare_inferior+compare_equal+compare_superior);
    if(instr3->function==G.function_jump_if_not) mode^=compare_inferior+compare_equal+compare_superior;
    current=instr;
    arg=argument(G.type_Int,Argument_a_register);
    marg=argument(G.type_Int,Argument_constant,entry(G.type_Int,mode,END));
    current=GeneratorContext_insert_after_instruction(gc,current,instruction(G.function_i386_mov,IARG(instr,0),arg,END));
    current=GeneratorContext_insert_after_instruction(gc,current,instruction(G.function_i386_cmp,IARG(instr,1),arg,END));
    i=instruction(G.function_i386_jump_if,marg,END); i->jump=instr3->jump;
    current=GeneratorContext_insert_after_instruction(gc,current,i);
    GeneratorContext_remove(gc,instr);
    GeneratorContext_remove(gc,instr2);
    GeneratorContext_remove(gc,instr3);
    instr=current; } }


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


/*
doc
  [tries to share some complex local datas in order to save some stack space and call less construction/destructor functions. (temporary strings are good samples of such datas)]
*/

static Void optimize_share_complex_locals(struct GeneratorContext *gc) {
  struct List locals,generals;
  Int lap; Arrow *c,*c2; struct Argument *arg,*master;
  #ifdef _LISTING_
    static Char *registers[8]={"EAX","ECX","EDX","EBX","ESP","EBP","ESI","EDI"};
    Int r;
    if(please_trace(gc)) {
      optimize_listing(gc);
      for(r=0; r<nb_register; r++)
        string_Str(registers[r],&gc->registers[r]->name); }
  #endif
  GeneratorContext_rebuild(gc);
  share_begin(gc);
  List_build(&locals),List_build(&generals);
  for(lap=0; lap<minimum(G.generator_level,2); lap++)
    for(c=List_first(lap==0 ? &gc->arguments : &generals); c!=G.null_constant; c=List_next(c)) {
      arg=(struct Argument *)*c;
      if(lap==0) {
        if(arg->first_instruction==null) continue;
        if(arg->where!=Argument_local) continue;
        if((arg->type->flags&Type_flag_atomic) && !argument_is_mapped(arg) && argument_is_byvalue(arg)) continue;
        if(argument_is_mapped(arg))
          continue;
        if(!argument_is_simple(arg)) {
          List_append(&generals,arg);
          continue; } }
      for(c2=List_first(&locals); c2!=G.null_constant; c2=List_next(c2)) {
        master=(struct Argument *)*c2;
        if(share_try(gc,arg,master,lap==0,true,false))
          goto done; }
      List_append(&locals,arg);
      done:; }
  List_destroy(&locals),List_destroy(&generals);
  share_end(gc);
  remove_unused_args(gc);
  optimize_remove_nouse_instructions(gc); }


/*
doc
  [Allocates the complex local datas on the stack, and add the constructor/destructor functions call at the beginning/end of the function.]
*/

static struct Argument *allocate_on_stack(struct Argument *arg,struct GeneratorContext *gc) {
  Int *localssize; struct Str temp;
  struct Argument *allocated;
  struct Argument *targ; struct Instruction *instr;
  #ifdef _LISTING_
    if(please_trace(gc))
      consolen(Z,"  ",S,&arg->name,Z," (",S,&arg->type->name,Z," ",Z,argument_is_mapped(arg) ? "m" : "",Z,argument_is_byvalue(arg) ? "v" : "",Z,") on stack",EOL);
  #endif
  localssize=(Int *)*Dictionary_first(&gc->locals,Str_map_string(&temp,"pliant locals size"));
  allocated=argument(arg->type,Argument_indirect,gc->registers[Register_ESP],*localssize);
  *localssize+=round_up(allocated->type->size,sizeof(Int));
  GeneratorContext_suckup(gc,arg,allocated);
  if(!(allocated->type->flags&Type_flag_scalar)) {
    targ=argument(G.type_Type,Argument_constant,allocated->type);
    instr=instruction(G.function_build_universal,targ,allocated,END);
    GeneratorContext_insert_at_section_bottom(gc,Section_build,instr);
    instr=instruction(G.function_destroy_universal,targ,allocated,END);
    GeneratorContext_insert_at_section_top(gc,Section_destroy,instr); }
 return allocated; }

static Void optimize_allocate_complex_locals(struct GeneratorContext *gc) {
  Arrow *c; struct Argument *arg;
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=List_next(c)) {
    arg=(struct Argument *)*c; if(arg->first_instruction==null) continue;
    #ifdef _CHECK_
      if((arg->type->flags&Type_flag_atomic) && !(arg->type->flags&Type_flag_scalar))
        error_notifyn(error_id_mismatch,Z,"An atomic type should be scalar, ",S,&arg->type->name,Z," is not",END);
    #endif
    if(arg->where!=Argument_local) continue;
    if((arg->type->flags&Type_flag_atomic) && !argument_is_mapped(arg) && argument_is_byvalue(arg)) continue;
    allocate_on_stack(arg,gc); } }


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

/*
doc
  section "turn_to_assembly"
  [Replaces all functions call by the assembly call sequence. When this is done, all instructions of the function are assembly instructions.]
*/

static Void optimize_turn_to_assembly(struct GeneratorContext *gc) {
  struct Instruction *instr,*instr2;
  for(instr=gc->first_instruction; instr!=null; instr=instr2) {
    instr2=instr->next_instruction;
    Instruction_generate_assembly(instr,gc); } }


/*
doc
  [Improves the assembly listing through suppressing nouse instructions.]
*/

#ifdef _EXPERIMENTAL_
  static Void optimize_rewrite_lea(struct GeneratorContext *gc) {
    struct Instruction *instr,*instr2;
    struct Argument *src,*dest,*p;
    for(instr=gc->first_instruction; instr!=null; instr=instr2) {
      instr2=instr->next_instruction;
      if(instr->function==G.function_i386_lea) {
        src=IARG(instr,0),dest=IARG(instr,1);
        if(src->where==Argument_indirect && src->u.indirect.offset==0) {
          p=src->u.indirect.pointer;
          if(p->where!=Argument_register || p->u.cpu_register!=Register_ESP) {
            GeneratorContext_insert_after_instruction(gc,instr,instruction(G.function_i386_mov,p,dest,END));
            if(p->where==Argument_constant)
              GeneratorContext_insert_after_instruction(gc,instr,instruction(G.function_do_nothing1,src,END));
            GeneratorContext_remove(gc,instr); } } } } }
#endif

static Void optimize_rewrite_assembly(struct GeneratorContext *gc) {
  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.offset==0) {
        if(!Argument_is_shared(src->u.indirect.pointer,dest))
          GeneratorContext_insert_after_instruction(gc,instr,instruction(G.function_i386_mov,src->u.indirect.pointer,dest,END));
        GeneratorContext_remove(gc,instr); } } } }


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


/*
doc
  [Double indirect variables are variables that are know by their address which is given by another variable which is also know by it's address given by a third variable. ]
  [Since fetching directly the value of such a variable is not possible using any assembly instruction, we insert an instruction that will first fetch it's address and store it in a register.]
*/

static struct Argument *suppress_double_indirects_using(struct Argument *arg,struct Argument *reg,struct Instruction *instr,struct GeneratorContext *gc) {
  struct Argument *ptr; struct Argument *arg2;
  if(arg->where!=Argument_indirect || arg->u.indirect.pointer->where==Argument_constant || arg->u.indirect.pointer->where==Argument_register || arg->u.indirect.pointer->where==Argument_a_register)
    return arg;
  ptr=suppress_double_indirects_using(arg->u.indirect.pointer,reg,instr,gc);
  GeneratorContext_insert_before_instruction(gc,instr,instruction(G.function_i386_mov,ptr,reg,END));
  arg2=argument(arg->type,Argument_indirect,reg,arg->u.indirect.offset);
  Str_copy(&arg->name,&arg2->name);
  return arg2; }

static Void optimize_suppress_double_indirects(struct GeneratorContext *gc) {
  struct Instruction *instr; Int i; struct Argument *arg;
  struct Argument *reg,*arg2;
  Char buffer[16]; struct Str temp;
  for(instr=gc->first_instruction; instr!=null; instr=instr->next_instruction)
    for(i=0; i<instr->arguments.nb; i++) {
      arg=IARG(instr,i);
      if(arg->where!=Argument_indirect || arg->u.indirect.pointer->where==Argument_constant || arg->u.indirect.pointer->where==Argument_register || arg->u.indirect.pointer->where==Argument_a_register)
        continue;
      reg=argument(G.type_Address,Argument_a_register);
      arg2=suppress_double_indirects_using(arg,reg,instr,gc);
      Instruction_set_argument(instr,i,arg2); }
  GeneratorContext_rebuild(gc); }


/*
doc
  [Allocates all atomic local variables. We try to do it cleverly so that most local variables be allocated to processor registers, and that the chosen registers will lead to many nouse move instruction (moving the data from the register to itself), but we currently clearly fail to be optimal, probably because we try to allocate local variables one after the other using a naive algorithm whereas we should use global optimization algorithms taken from scientific publications. ] ; highlight "help wanted" ; eol
  [lap 0: a_register tiny arguments to prefered register] ; eol
  [lap 1: local simple tiny arguments to prefered register] ; eol
  [lap 2: a_register arguments to prefered] ; eol
  [lap 3: local simple arguments to prefered] ; eol
  [lap 4: a_register arguments to any] ; eol
  [lap 5: local simple arguments to any] ; eol
  [lap 6: local complex arguments to any] ; eol
  italic [tiny] ; [ means that the argument appears only in a very short sequence.]
*/

static struct Argument *argument_prefered(struct Argument *arg) {
  struct Instruction *instr; struct Argument *master;
  for(instr=arg->first_instruction;; instr=instr->next_instruction) {
    master=(!(instr->function->flags&Function_flag_copy) || instr->arguments.nb<2 ? null : BARG(instr,0)==arg ? BARG(instr,1) : BARG(instr,1)==arg ? BARG(instr,0) : null);
    if(master!=null && master->where==Argument_register)
      return master;
    if(instr==arg->last_instruction)
      break; }
  return null; }

static Void optimize_allocate_registrables(struct GeneratorContext *gc) {
  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","EBP","ESI","EDI"};
    struct Str temp; Char buffer[16];
  #endif
  GeneratorContext_renumber(gc);
  GeneratorContext_rebuild(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=List_next(c)) {
    arg=(struct Argument *)*c; if(arg->first_instruction==null) continue;
    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_str2(lap,10,buffer)),Z,":",EOL); 
      #endif
      nextlap=lap+1;
      for(c=List_first(lap==0 || lap==2 ? &gc->arguments : &simples); c!=G.null_constant; c=List_next(c)) {
        arg=(struct Argument *)*c;
        if(arg->first_instruction==null || boundto(arg)!=arg) continue;
        if((lap==0 || lap==2) && arg->where!=Argument_a_register) continue;
        if(lap<2 && arg->last_instruction->order-arg->first_instruction->order>512) continue;
        master=argument_prefered(arg);
        if(master==null) continue;
        if(arg->where==Argument_a_register && !(arg->u.possible_registers&(1<<minimum(master->u.cpu_register,31)))) continue;
        if(lap==1 || lap==3) {
          if(conservative==2) continue;
          if(conservative==1 && master->u.cpu_register==Register_EDI) continue; }
        if(!share_try(gc,arg,master,true,false,true)) continue;
        nextlap=0; } }
  for(lap=4; lap<(G.generator_level>=2 ? 7 : G.generator_level==1 ? 6 : 5); lap++) {
    #ifdef _LISTING_
      if(please_trace(gc))
        consolen(Z,"lap ",S,Str_map_area(&temp,buffer,Int_str2(lap,10,buffer)),Z,":",EOL); 
    #endif
    for(c=List_first(lap==4 ? &gc->arguments : lap==5 ? &simples : &generals); c!=G.null_constant; c=List_next(c)) {
      arg=(struct Argument *)*c;
      if(arg->first_instruction==null || boundto(arg)!=arg) continue;
      if(lap==4 && arg->where!=Argument_a_register) continue;
      for(c2=List_first(&locals); c2!=G.null_constant; c2=List_next(c2)) {
        master=(struct Argument *)*c2;
        if(master->where==Argument_register) {
          if(arg->where==Argument_a_register && !(arg->u.possible_registers&(1<<minimum(master->u.cpu_register,31))))
            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; }
      check(arg->where!=Argument_a_register);
      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=List_next(c)) {
    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=List_next(c)) {
    arg=(struct Argument *)*c; if(arg->first_instruction==null) continue;
    check(arg->where!=Argument_a_register);
    if(arg->where==Argument_local)
      allocate_on_stack(arg,gc); }
  remove_unused_args(gc);
  optimize_remove_nouse_instructions(gc); }


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


/*
doc
  [Adds the stack pointer adjustment instructions at the beginning end the end of the function.]
*/

static Void optimize_add_grow_stack_instructions(struct GeneratorContext *gc) {
  Int *localssize; struct Str temp;
  struct Argument *arg;
  localssize=(Int *)*Dictionary_first(&gc->locals,Str_map_string(&temp,"pliant locals size"));
  if(*localssize==0) return;
  arg=argument(G.type_Int,Argument_constant,localssize);
  GeneratorContext_insert_at_section_top(gc,Section_header,instruction(G.function_i386_sub,arg,gc->registers[Register_ESP],END));
  GeneratorContext_insert_at_section_bottom(gc,Section_tail,instruction(G.function_i386_add,arg,gc->registers[Register_ESP],END)); }


/*
doc
  [Adds the register saving/restoring instructions at the beginning/end of the function, and computes the list of registers that the function truly modify.]
*/

static Void optimize_add_push_pop_instructions(struct GeneratorContext *gc) {
  Int *pushsize;
  struct Function *fun; struct Str temp; Arrow *c;
  struct FunctionPrototype *p;
  Byte used[nb_8_register_sets];
  struct Instruction *instr; Int i; struct Argument *arg;
  pushsize=(Int *)*Dictionary_first(&gc->locals,Str_map_string(&temp,"pliant push size"));
  c=Module_first(gc->module,Str_map_string(&temp,"pliant function"));
  if(c!=G.null_constant)
    fun=(struct Function *)*c;
  else
    fun=null;
  memory_clear(used,bit_size(nb_register));
  for(instr=gc->first_instruction; instr!=null; instr=instr->next_instruction) {
    for(i=0; i<instr->arguments.nb; i++) {
      arg=IARG(instr,i); if(arg->where!=Argument_register) continue;
      if(instr->function->arguments[i].access&Access_write)
        bit_set(used,arg->u.cpu_register); }
    for(i=0; i<bit_size(nb_register); i++)
      used[i]|=instr->function->modify_registers[i]; }
  if(fun!=null)
    if(G.generator_level>=2) {
      for(i=0; i<bit_size(nb_register); i++) {
        if(!(fun->flags&(Function_flag_generic|Function_flag_indirect|Function_flag_later)))
          fun->modify_registers[i]&=used[i];
        used[i]&=~fun->modify_registers[i]; }
    other
      for(i=0; i<fun->nb_argres; i++) {
        p=fun->arguments+i;
        if(p->cpu_register>=0)
          if(p->access&Access_write)
            if(p->access&(Access_byvalue|Access_mapped))
              bit_clear(used,p->cpu_register); } }
  for(i=0; i<nb_register; i++)
    if(bit_test(used,i) && i!=Register_ESP) {
      GeneratorContext_insert_at_section_top(gc,Section_header,instruction(G.function_i386_push,gc->registers[i],END));
      GeneratorContext_insert_at_section_bottom(gc,Section_tail,instruction(G.function_i386_pop,gc->registers[i],END));
      *pushsize+=sizeof(Int); } }


/*
doc
  [Adds the return instruction at the end of the function.]
*/

static Void optimize_add_return_instruction(struct GeneratorContext *gc) {
  Int pop;
  struct Str temp; Arrow *c; struct Function *function;
  Int i; struct FunctionPrototype *parg;
  struct Argument *arg;
  c=Module_first(gc->module,Str_map_string(&temp,"pliant function"));
  pop=0;
  if(c!=G.null_constant) {
    function=(struct Function *)*c;
    if(!(function->flags&Function_flag_not_popped))
      for(i=0; i<function->nb_argres; i++) {
        parg=function->arguments+i;
        if(parg->cpu_register<0)
          pop+=sizeof(Int); } }
  if(G.debugging_level) {
    GeneratorContext_insert_at_section_top(gc,Section_header,instruction(G.function_i386_mov,gc->registers[Register_ESP],gc->registers[Register_EBP],END));
    GeneratorContext_insert_at_section_top(gc,Section_header,instruction(G.function_i386_push,gc->registers[Register_EBP],END));
    GeneratorContext_insert_at_section_bottom(gc,Section_tail,instruction(G.function_i386_pop,gc->registers[Register_EBP],END)); }
  arg=argument(G.type_Int,Argument_constant,entry(G.type_Int,pop,END));
  GeneratorContext_insert_at_section_bottom(gc,Section_tail,instruction(G.function_i386_return,arg,END)); }


/*
doc
  [Computes the exact position of each parameter passed on the stack through adding the amount of stack space reserved for local variables.]
*/

static Void optimize_adjust_parameters_offsets(struct GeneratorContext *gc) {
  Arrow *c1,*c2; struct Str temp;
  struct Function *function; struct Array *arguments;
  Int pushsize,localssize;
  Int i; struct FunctionPrototype *parg; struct Argument *barg,*arg;
  c1=Module_first(gc->module,Str_map_string(&temp,"pliant function")); if(c1==G.null_constant) return;
  function=(struct Function *)*c1;
  c2=Module_first(gc->module,Str_map_string(&temp,"pliant arguments")); check(c2!=G.null_constant);
  arguments=(struct Array *)*c2;
  pushsize=*(Int *)*Dictionary_first(&gc->locals,Str_map_string(&temp,"pliant push size"));
  localssize=*(Int *)*Dictionary_first(&gc->locals,Str_map_string(&temp,"pliant locals size"));
  for(i=0; i<function->nb_argres; i++) {
    parg=function->arguments+i;
    if(parg->cpu_register<0) {
      barg=(struct Argument *)Array_get_index(arguments,i);
      if(function->arguments[i].access&Access_byvalue) {
        arg=barg;
      other
        check(barg->where==Argument_indirect);
        arg=barg->u.indirect.pointer; }
      check(arg->where==Argument_indirect);
      arg->u.indirect.offset+=localssize+pushsize-int_max/2; } } }


/*
doc
  [When calling a function, some arguments may have to be pushed on the stack. As a result, the stack pointer is moving, so if we want to use local variables or arguments allocated on the stack in the middle of the calling sequence, we have to adjust their offset.]
*/

static Void optimize_adjust_stacked_offsets(struct GeneratorContext *gc) {
  struct Instruction *instr,*instr2;
  Int i; struct Argument *a,*a2;
  for(instr=gc->first_instruction; instr!=null; instr=instr->next_instruction) {
    if(instr->stackdelta==0) continue;
    for(i=0; i<instr->arguments.nb; i++) {
      a=IARG(instr,i);
      if(a->where==Argument_indirect && a->u.indirect.pointer==gc->registers[Register_ESP]) break; }
    if(i==instr->arguments.nb) continue;
    instr2=(struct Instruction *)entry_new(G.type_Instruction);
    Arrow_set((Arrow *)&instr2->function,instr->function);
    Array_resize(&instr2->arguments,instr->arguments.nb);
    for(i=0; i<instr2->arguments.nb; i++) {
      a=IARG(instr,i);
      if(a->where==Argument_indirect && a->u.indirect.pointer==gc->registers[Register_ESP]) {
        a2=argument(a->type,Argument_indirect,gc->registers[Register_ESP],a->u.indirect.offset+instr->stackdelta);
        List_copy(&a->requires,&a2->requires);
      other
        a2=a; }
      Array_set_index(&instr2->arguments,i,a2); }
    instr2->jump=instr->jump;
    GeneratorContext_insert_after_instruction(gc,instr,instr2);
    GeneratorContext_remove(gc,instr);
    instr=instr2; } }


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


/*
doc
  [Builds the binary version of the function.]
*/

static Void optimize_build_binary(struct GeneratorContext *gc) {
  struct Function *function;
  struct Instruction *instr;
  Arrow *c,*c2; struct Argument *arg;
  struct ActionRecord ca; Int i;
  struct Str s;
  function=gc->function;
  for(instr=gc->first_instruction; instr!=null; instr=instr->next_instruction) {
    instr->order=function->exe_size;
    ActionRecord_build(&ca);
    Str_build(&s);
    ListingPositions_get(&instr->position,&s);
    action_push_recordn(&ca,Z,"generate ",S,&s,END);
    Str_destroy(&s);
    Instruction_generate_binary(instr,function);
    action_pull_record(&ca);
    ActionRecord_destroy(&ca);
    if(error_notified())
      break; } 
  for(c=List_first(&gc->arguments); c!=G.null_constant; c=List_next(c)) {
    arg=(struct Argument *)*c;
    for(c2=List_first(&arg->requires); c2!=G.null_constant; c2=List_next(c2))
      Function_record_external(function,*c2,false); }
  if(G.debugging_level>=2) {
    Str_build(&s);
    ListingPosition_get(&function->position,&s);
    for(i=0; i<Str_len(&s); i++)
      Function_code_immediat(function,s.chars[i],1);
    Function_code_immediat(function,0,1);
    Str_destroy(&s); } }

static Void optimize_conclude(struct GeneratorContext *gc) {
  struct Function *function;
  function=gc->function;
  Function_terminate_code(function);
  #ifdef _LISTING_
    { struct Str temp; Char buffer[16];
      if(please_trace(gc)) {
        optimize_listing(gc);
        consolen(Z,"function executable size is ",S,Str_map_area(&temp,buffer,Int_str2(function->exe_size,10,buffer)),Z," bytes",EOL); } }
  #endif
  }


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

/*
doc
  [The following functions are used in ] ; link "startup.c" "../startup/startup.c" ; [ in order to record the various optimizing functions above.]
*/


static Void C_optimizer_section(Char *section) {
  struct Str *s;
  s=(struct Str *)entry_new(G.type_Str);
  string_Str(section,s);
  List_append(G.optimizer_sections,s); }


static Void C_optimizer2(Char *section,Char *funname,Address cfun) {
  struct Str temp;
  #ifdef _CHECK_
    Arrow *c;
    for(c=List_first(G.optimizer_sections); c!=G.null_constant; c=List_next(c))
      if(compare_str((struct Str *)*c,Str_map_string(&temp,section))==compare_equal) break;
    if(c==null)
      error_notifyn(error_id_missing,Z,section,Z," is missing",END);
  #endif
  Dictionary_insert2(G.general_dictionary,Str_map_string(&temp,section),false,C_function2(funname,cfun),G.boptimizer_module); }

#define C_optimizer(section,cfun) C_optimizer2(section,#cfun,cfun)