PreviousUpNext

15.4.482  src/lib/compiler/back/top/improve-nextcode/do-nextcode-inlining-g.pkg

## do-nextcode-inlining-g.pkg 

# Compiled by:
#     src/lib/compiler/core.sublib



# This file implements one of the nextcode transforms.
# For context, see the comments in
#
#     src/lib/compiler/back/top/highcode/highcode-form.api



#     "General inlining, which decides whether or not
#      to inline functions called more than once based
#      on a budget and on estimates of code size and
#      optimization opportunities that inlining will create.
#
#     "This also does loop unrolling, and introduction of
#      loop pre-headers [1] which allow loops to be inlined.
#
#          -- Principled Compilation and Scavenging
#             Stefan Monnier, 2003 [PhD Thesis, U Montreal]
#             http://www.iro.umontreal.ca/~monnier/master.ps.gz 
#
#      [1] Loop Headers in lambda-calculus or nextcode
#          Andrew W Appel
#          1994, 6p
#          http://citeseer.ist.psu.edu/appel94loop.html



#DO set_control "compiler::trap_int_overflow" "TRUE";

stipulate
    package ncf =  nextcode_form;                               # nextcode_form                 is from   src/lib/compiler/back/top/nextcode/nextcode-form.pkg
    package hct =  highcode_type;                               # highcode_type                 is from   src/lib/compiler/back/top/highcode/highcode-type.pkg
    package hut =  highcode_uniq_types;                         # highcode_uniq_types           is from   src/lib/compiler/back/top/highcode/highcode-uniq-types.pkg
    package iht =  int_hashtable;                               # int_hashtable                 is from   src/lib/src/int-hashtable.pkg
herein

    api Do_Nextcode_Inlining {
        #
        do_nextcode_inlining
           :
           { function:       ncf::Function,
             bodysize:       Int,
             unroll:         Bool,
             table:          iht::Hashtable( hut::Uniqtypoid ),
             after_closure:  Bool,
             do_headers:     Bool,
             click:          String -> Void
           }
           ->
           ncf::Function;

    };
end;




                                                                # Machine_Properties    is from   src/lib/compiler/back/low/main/main/machine-properties.api
stipulate
    package coc =  global_controls::compiler;                   # global_controls       is from   src/lib/compiler/toplevel/main/global-controls.pkg
    package hcf =  highcode_form;                               # highcode_form         is from   src/lib/compiler/back/top/highcode/highcode-form.pkg
    package iht =  int_hashtable;                               # int_hashtable         is from   src/lib/src/int-hashtable.pkg
    package ncf =  nextcode_form;                               # nextcode_form         is from   src/lib/compiler/back/top/nextcode/nextcode-form.pkg
    package tmp =  highcode_codetemp;                           # highcode_codetemp     is from   src/lib/compiler/back/top/highcode/highcode-codetemp.pkg
herein

    # This generic is invoked (only) from:
    #
    #     src/lib/compiler/back/top/improve-nextcode/run-optional-nextcode-improvers-g.pkg

    generic package   do_nextcode_inlining_g   (
        #             ======================
        #
        machine_properties:  Machine_Properties                 # Typically                       src/lib/compiler/back/low/main/intel32/machine-properties-intel32.pkg
    )
    : (weak)  Do_Nextcode_Inlining                              # Do_Nextcode_Inlining  is from   src/lib/compiler/back/top/improve-nextcode/do-nextcode-inlining-g.pkg
    {

        #
        fun inc (ri as REF i) =   ri := i + 1;
        fun dec (ri as REF i) =   ri := i - 1;
        #
        fun map1 f (a, b)
            =
            (f a, b);
        #
        fun sum f
            =
            h
            where
               fun h (a ! r) =>  f a  +  h r;
                   h []      =>  0; 
               end;
            end;
        #
        fun muldiv (a, b, c)   #  A*b/c, approximately, but guaranteed no overflow 
            =
            (a*b) / c
            except
                OVERFLOW
                    =
                    if (a > b)   muldiv (a / 2, b, c / 2);
                    else         muldiv (a, b / 2, c / 2);
                    fi;
        #
        fun share_name (x, ncf::CODETEMP   y) =>  tmp::share_name (x, y); 
            share_name (x, ncf::LABEL y) =>  tmp::share_name (x, y); 
            share_name _ => ();
        end;

        Mode = ALL | NO_UNROLL | UNROLL Int | HEADERS;
        #
        fun do_nextcode_inlining
            {
              function => (fkind, fvar, fargs, ctyl, cexp),
              unroll,
              bodysize,
              click,
              after_closure,
              table=>typetable,
              do_headers
            }
            =
            {
                clicked_any =   REF FALSE;

                click =   \\ z =   { click z; clicked_any := TRUE;};

                debug = *coc::debugnextcode;            # FALSE 

                debugprint = if debug  global_controls::print::say;   else \\ _ = (); fi;
                debugflush = if debug  global_controls::print::flush; else \\ _ = (); fi;

                cginvariant = *coc::invariant;
                #
                fun label v
                    =
                    after_closure   ??   ncf::LABEL v
                                    ::   ncf::CODETEMP   v;

                Data = FUN  { escape: Ref( Int ),
                              call:   Ref( Int ),
                              size:   Ref( Int ),
                              args:   List( ncf::Codetemp ),
                              body:   ncf::Instruction,
                              # 
                              invariant:        Ref(  List(  Bool ) ),          #  one for each arg 
                              unroll_call:      Ref( Int ),
                              level:            Int,
                              within:           Ref( Bool )
                            }
                     | ARG  { escape: Ref( Int ), savings: Ref( Int ),
                               record: Ref( List( (Int, ncf::Codetemp) ) ) }
                     | SEL  { savings: Ref( Int ) }
                     | REC  { escape: Ref( Int ), size: Int,
                               vars:  List( (ncf::Value, ncf::Fieldpath) ) }
                     | FLOAT
                     | CONST
                     | OTHER
                     ;

                rep_flag = machine_properties::representations;

                type_flag =   *global_controls::compiler::checknextcode1
                          and *global_controls::compiler::checknextcode2
                          and  rep_flag;

                stipulate   

                    exception NEXPAND;
                    #
                    fun getty v
                        = 
                        if type_flag
                            #
                            (iht::get  typetable  v)
                            except
                                _ =
                                  {   global_controls::print::say ("NEXPAND: Can't find the variable " +
                                            (int::to_string v) + " in the typetable ***** \n");
                                      raise exception NEXPAND;
                                  };
                        else
                            hcf::truevoid_uniqtypoid;
                        fi;
                    #
                    fun addty (f, t)
                        =
                        iht::set typetable (f, t);

                herein
                    #
                    fun make_var (t)
                        =
                        {   v = tmp::issue_highcode_codetemp();

                            if type_flag  addty (v, t); fi;

                            v;
                        };
                    #
                    fun copy_lvar v
                        =
                        {   x = tmp::clone_highcode_codetemp (v);

                            if type_flag  addty (x, getty v); fi;

                            x;
                        };

                end; #  with


                stipulate

                    exception EXPAND;

                    m = iht::make_hashtable  { size_hint => 128,  not_found_exception => EXPAND }
                      : iht::Hashtable( Data );

                    get' =  iht::get  m;

                herein

                    note =  iht::set  m;
                    #
                    fun get i
                        =
                        get' i
                        except
                            EXPAND = OTHER;
                    #
                    fun discard_pass1_info ()
                        =
                        iht::clear m;
                end;
                #
                fun getval (ncf::CODETEMP v) =>  get v;
                    getval (ncf::LABEL    v) =>  get v;
                    getval (ncf::INT      _) =>  CONST;
             #      getval (ncf::REAL     _) =>  FLOAT;
                    getval _                 =>  OTHER;
                end;
                #
                fun call (v, args)
                    =
                    case (getval v)
                        #
                        FUN { call, within=>REF FALSE, ... }
                            =>
                            inc call;

                        FUN { call, within=>REF TRUE, unroll_call, args=>vl, invariant, ... }
                            => 
                            { fun g (ncf::CODETEMP x ! args, x' ! vl, i ! inv) =>
                                      (i and x==x') ! g (args, vl, inv);
                                   g( _ ! args, _ ! vl, i ! inv) =>
                                      FALSE ! g (args, vl, inv);
                                   g _ => NIL; end;
                              inc call; inc unroll_call;
                                invariant := g (args, vl,*invariant);
                            };

                        ARG { savings, ... } => inc savings;

                        SEL { savings } => inc savings;

                        _ => ();
                    esac;
                #
                fun escape v
                    =
                    case (getval v)
                        FUN { escape, ... } => inc escape;
                        ARG { escape, ... } => inc escape;
                        REC { escape, ... } => inc escape;
                        _ => ();
                    esac;
                #
                fun escapeargs v
                    =
                    case (getval v)
                        FUN { escape, ... }          =>   inc escape;
                        ARG { escape, savings, ... } => { inc escape;  inc savings; };
                        SEL { savings   }            =>   inc savings;
                        REC { escape, ... }          =>   inc escape;
                        _ => ();
                    esac;
                #
                fun unescapeargs v
                    =
                    case (getval v)
                        FUN { escape,          ... } =>   dec escape;
                        ARG { escape, savings, ... } => { dec escape;  dec savings; };
                        SEL { savings              } =>   dec savings;
                        REC { escape,          ... } =>   dec escape;
                        _ => ();
                    esac;
                #
                fun notearg   v = (note (v, ARG { escape=>REF 0, savings=>REF 0, record=>REF [] } ));
                fun noteother v = ();                                                                   # note (v, OTHER) 
                fun notefloat  v = noteother v;                                                         # note (v, FLOAT) 
                #
                fun enter level (_, f, vl, _, e)
                    = 
                    {   note ( f,
                               FUN { escape => REF 0,
                                     call   => REF 0,
                                     size   => REF 0,
                                     args => vl,
                                     body => e,
                                     within      => REF FALSE,
                                     unroll_call => REF 0, 
                                     invariant   => REF (map (\\ _ = cginvariant) vl),
                                     level
                                   }
                             );

                        apply notearg vl;
                    };

                #
                fun noterec (w, vl, size)
                    =
                    note (w, REC { size, escape=>REF 0, vars=>vl } );

                #
                fun notesel (i, v, w)
                    =
                    {   note (w, SEL { savings=>REF 0 } );

                        case (getval v)

                            ARG { savings, record, ... }
                                =>
                                {   inc savings;
                                    record := (i, w) ! *record;
                                };

                           _ => ();
                        esac;
                    };

                #
                fun setsize (f, n)
                    =
                    case (get f)   
                        FUN { size, ... } => {  size := n;  n;  };
                       _ => raise exception DIE "Expand: setsize: not a FUN";
                    esac;

                #
                fun increase_savings (v, k)
                    =
                    case (getval v)
                        ARG { savings, ... } => savings := *savings + k;
                        SEL { savings      } => savings := *savings + k;
                        _ => ();
                    esac;

                #
                fun setsave (v, k)
                    =
                    case (getval v)
                        ARG { savings, ... } => savings := k;
                        SEL { savings      } => savings := k;
                        _ => ();
                    esac;

                #
                fun savesofar v
                    =
                    case (getval v)
                        ARG { savings, ... } => *savings;
                        SEL { savings      } => *savings;
                        _ => 0;
                    esac;

                #
                fun within f fn arg
                    =
                    case (get f)   
                         FUN { within => w, ... }
                             =>
                             {   w := TRUE;

                                 fn arg
                                 then
                                     (w := FALSE);
                             };
                        _ => raise exception DIE "Expand: within: f is not a FUN";
                    esac;


                recursive my prim
                    =
                    \\ (level, vl, e)
                        =
                        {   fun vbl (ncf::CODETEMP v)
                                    =>
                                    case (get v)
                                        REC _ => 0;
                                        _     => 1;
                                    esac;

                                vbl _ => 0;
                            end;

                            nonconst = sum vbl vl;
                            sl = map savesofar vl;
                            afterwards = pass1 level e;
                            zl = map savesofar vl;
                            overhead = length vl + 1;
                            potential = overhead;

                            savings = case nonconst   
                                          1 => potential;
                                          2 => potential / 4;
                                          _ => 0;
                                      esac;
                            #
                            fun app3 f
                                =
                                loop
                                where
                                    fun loop (a ! b, c ! d, e ! r)
                                            =>
                                            {   f (a, c, e);
                                                loop (b, d, r);
                                            };

                                        loop _ => ();
                                    end;
                                end;

                            app3 (\\ (v, s, z) =  setsave (v, s + savings + (z-s)))
                                 (vl, sl, zl);

                            overhead+afterwards;
                        }

                also
                primfloat
                    =
                    \\ (level, { op => _, args => vl, to_temp => w, type => _, next => e })
                        =
                        {   notefloat w;

                            apply (\\ v = increase_savings (v, 1))  vl;

                            2*(length vl + 1) + pass1 level e;
                        }

                # *****************************************************************
                #  pass1: gather info on code.                                     
                # *****************************************************************
                also
                pass1:  Int -> ncf::Instruction -> Int
                    =
                    (\\ level
                        =
                        \\  ncf::DEFINE_RECORD { fields, to_temp, next, ... }
                                =>
                                {   len = length fields;
                                    apply (escape o #1) fields;
                                    noterec (to_temp, fields, len);
                                    2 + len + pass1 level next;
                                };

                            ncf::GET_FIELD_I            { i, record, to_temp, next, ... } =>  { notesel (i, record, to_temp);  1 + pass1 level next;};
                            ncf::GET_ADDRESS_OF_FIELD_I {            to_temp, next, ... } =>  { noteother to_temp;             1 + pass1 level next;};

                            ncf::TAIL_CALL { fn, args }
                                =>
                                {   call (fn, args); 
                                    apply escapeargs args; 
                                    1 + ((length args + 1) / 2);
                                };

                            ncf::DEFINE_FUNS { funs, next }
                                => 
                                {   apply  (enter level)  funs; 

                                    sum   (\\ (_, f, _, _, e) = setsize (f, within f (pass1 (level+1)) e))   funs 
                                    + length funs
                                    + pass1 level next;
                                };

                            ncf::JUMPTABLE { i, nexts, ... }
                                =>
                                {   len = length nexts;
                                    jumps = 4 + len;                                    # 64-bit issue XXX BUGGO FIXME. Does the '4' need to be '8' on 64-bit machines...?
                                    branches = sum (pass1 level) nexts;
                                    increase_savings (i, muldiv (branches, len - 1, len) + jumps);
                                    jumps+branches;
                                };

                            ncf::IF_THEN_ELSE { args => vl, xvar => c, then_next => e1, else_next => e2, ... }
                                =>
                                {   fun vbl (ncf::CODETEMP v)
                                            =>
                                            case (get v)
                                                REC _ => 0;
                                                _     => 1;
                                            esac;

                                        vbl _ => 0;
                                    end;

                                    nonconst = sum vbl vl;
                                    sl = map savesofar vl;

                                    branches = pass1 level e1 + pass1 level e2;
                                    zl = map savesofar vl;

                                    overhead = length vl;
                                    potential = overhead + branches / 2;

                                    savings
                                        =
                                        case nonconst   
                                            1 => potential;
                                            2 => potential / 4;
                                            _ => 0;
                                        esac;
                                    #
                                    fun app3 f
                                        =
                                        loop
                                        where
                                            fun loop (a ! b, c ! d, e ! r)
                                                    =>
                                                    {   f (a, c, e);
                                                        loop (b, d, r);
                                                    };

                                                loop _ => ();
                                            end;
                                        end;

                                    app3 (\\ (v, s, z) =  setsave (v, s + savings + (z-s) / 2))
                                         (vl, sl, zl);

                                    overhead+branches;
                                };

                            ncf::FETCH_FROM_RAM { args, to_temp, next, ... }
                                =>
                                {   noteother to_temp;
                                    prim (level, args, next);
                                };

                            ncf::STORE_TO_RAM { args, next, ... }
                                =>
                                prim (level, args, next);

                            ncf::ARITH ( args as { op => ncf::p::ARITH { kind_and_size=>ncf::p::FLOAT 64, ... }, ... })
                                =>
                                primfloat (level, args);

                            ncf::ARITH ( args as { op => ncf::p::ROUND _, ... })
                                =>
                                primfloat (level, args);

                            ncf::ARITH { args, to_temp, next, ... }
                                =>
                                {   noteother to_temp;
                                    prim (level, args, next);
                                };

                            ncf::PURE { op => ncf::p::PURE_ARITH { kind_and_size=>ncf::p::FLOAT 64, ... }, args => [v], to_temp, next, ... }
                                => 
                                {   notefloat to_temp;
                                    increase_savings (v, 1);
                                    4+(pass1 level next);
                                };

                            ncf::PURE { op => ncf::p::CONVERT_FLOAT { to=>ncf::p::FLOAT 64, ... }, args, to_temp, next, ... }
                                =>
                                {   notefloat to_temp;
                                    prim (level, args, next);
                                };

                            ncf::PURE { args, to_temp, next, ... }
                                =>
                                {   noteother to_temp;
                                    prim (level, args, next);
                                };

                            ncf::RAW_C_CALL { args, to_ttemps, next, ... }
                                =>
                                {   apply  (noteother o #1)  to_ttemps;
                                    #
                                    prim (level, args, next);
                                };
                        end
                    );                          # fn pass1


                # *******************************************************************
                #  substitute (args, wl, e, alpha) : substitute args for wl in e.        
                #  If alpha=TRUE, also rename all namings.                          
                # *******************************************************************
                fun substitute (args, wl, e, alpha)
                    =
                    {   exception ALPHA;

                        my vm: iht::Hashtable( ncf::Value )
                            =
                            iht::make_hashtable  { size_hint => 16,  not_found_exception => ALPHA };
                        #
                        fun get (v, default)
                            =
                            the_else (iht::find vm v, default);

                        enter = iht::set vm;
                        #
                        fun use (v0 as ncf::CODETEMP   v) => get (v, v0);
                            use (v0 as ncf::LABEL v) => get (v, v0);
                            use x => x;
                        end;

                        #
                        fun def v
                            =
                            if alpha
                                 w = copy_lvar v; 
                                 enter (v, ncf::CODETEMP w);
                                 w;
                            else
                                 v;
                            fi; 

                        #
                        fun defl v
                            =
                            if alpha
                                 w = copy_lvar v; 
                                 enter (v, label w);
                                 w;
                            else
                                 v;
                            fi;
                        #
                        fun bind (a ! args, w ! wl)
                                => 
                                {   share_name (w, a);
                                    enter     (w, a);
                                    bind (args, wl);
                                };

                            bind _ => ();
                        end;

                        recursive my g
                            =
                            \\  ncf::DEFINE_RECORD { kind, fields, to_temp, next }
                                    =>
                                    ncf::DEFINE_RECORD    { kind,
                                                            fields =>  map (map1 use) fields,
                                                            to_temp   =>  def to_temp,
                                                            next   =>  g next
                                                          };

                                ncf::GET_FIELD_I { i, record,               to_temp,                type, next           }
                             => ncf::GET_FIELD_I { i, record => use record, to_temp => def to_temp, type, next => g next };

                                ncf::GET_ADDRESS_OF_FIELD_I { i, record,               to_temp,                next           }
                             => ncf::GET_ADDRESS_OF_FIELD_I { i, record => use record, to_temp => def to_temp, next => g next };

                                ncf::TAIL_CALL { fn, args }
                                    =>
                                    ncf::TAIL_CALL { fn =>  use fn,
                                                 args =>  map use args
                                               };

                                ncf::DEFINE_FUNS { funs, next }
                                    => 
                                    ncf::DEFINE_FUNS { funs =>  map h2 (map h1 funs),
                                                       next =>  g next
                                                     }
                                    where
                                        # Careful: order of evaluation
                                        # is important here:
                                        #       
                                        fun h1 (fk, f, vl, cl, e)
                                            =
                                            (fk, defl f, vl, cl, e);
                                        #
                                        fun h2 (fk, f', vl, cl, e)
                                            =
                                            {   vl' = map def vl;
                                                e'= g e;
                                                (fk, f', vl', cl, e');
                                            };
                                    end;

                                ncf::JUMPTABLE      { i, xvar,                 nexts } =>   ncf::JUMPTABLE      { i => use i,   xvar => def xvar,                         nexts => map g nexts   };

                                ncf::STORE_TO_RAM   { op, args,                next  } =>   ncf::STORE_TO_RAM   { op, args => map use args,                               next => g next };
                                ncf::FETCH_FROM_RAM { op, args, to_temp, type, next  } =>   ncf::FETCH_FROM_RAM { op, args => map use args, to_temp => def to_temp, type, next => g next };

                                ncf::ARITH   { op, args, to_temp, type, next } =>   ncf::ARITH  { op,  args => map use args,  to_temp => def to_temp,  type, next => g next };
                                ncf::PURE    { op, args, to_temp, type, next } =>   ncf::PURE   { op,  args => map use args,  to_temp => def to_temp,  type, next => g next };

                                ncf::RAW_C_CALL { kind, cfun_name, cfun_type,  args,                  to_ttemps,                                            next           }
                             => ncf::RAW_C_CALL { kind, cfun_name, cfun_type,  args => map use args,  to_ttemps => map (\\ (w, t) = (def w, t)) to_ttemps,  next => g next };

                                ncf::IF_THEN_ELSE { op, args, xvar, then_next, else_next }
                                    =>
                                    ncf::IF_THEN_ELSE { op,
                                                        args =>  map use args,
                                                        xvar =>  def xvar,
                                                        then_next =>  g then_next,
                                                        else_next =>  g else_next
                                                      };
                          end ;

                        bind (args, wl);

                        g e;
                    };
                #
                fun whatsave (acc, size, (v: ncf::Value) ! vl, a ! al)
                        =>
                        if (acc >= size)
                            acc;
                        else
                            case (get a)   
                                #
                                ARG { escape=>REF esc, savings=>REF save, record=>REF rl }
                                    =>
                                    whatsave (acc+this - muldiv (acc, this, size), size, nvl, nal)
                                    where
                                        my (this, nvl: List( ncf::Value ), nal)
                                            =
                                            case (getval v)   
                                                #
                                                FUN { escape=>REF 1, ... }
                                                    =>
                                                    ( esc > 0   ??  save   ::  6+save,
                                                      vl,
                                                      al
                                                    );

                                                FUN _ => (save, vl, al);

                                                REC { escape=>REF ex, vars, size }
                                                    =>
                                                    {   exception CHASE;
                                                        #
                                                        fun chasepath (v, ncf::SLOT 0)
                                                                =>
                                                                v;

                                                            chasepath (v, ncf::VIA_SLOT (i, p))
                                                                =>
                                                                case (getval v)   
                                                                    #
                                                                    REC { vars, ... }
                                                                        =>
                                                                        chasepath (chasepath (list::nth (vars, i)), p);

                                                                   _ => raise exception CHASE;
                                                               esac;

                                                            chasepath _ => raise exception CHASE;
                                                        end;
                                                        #
                                                        fun loop ([], nvl, nal)
                                                                => 
                                                                ( (ex>1 or esc>0)  ??  save  ::  save+size+2,
                                                                  nvl,
                                                                  nal
                                                                );

                                                            loop((i, w) ! rl, nvl, nal)
                                                                =>
                                                                loop ( rl,
                                                                       chasepath (list::nth (vars, i)) ! nvl,
                                                                       w ! nal
                                                                     );
                                                        end;

                                                        loop (rl, vl, al)
                                                        except
                                                            CHASE => (0, vl, al);
                                                            INDEX_OUT_OF_BOUNDS => (0, vl, al);
                                                        end ;
                                                    }; 

                                        #       FLOAT  => (save, vl, al)

                                                CONST => (save, vl, al);

                                                _ => (0, vl, al);

                                            esac;
                                    end;

                               SEL { savings=>REF save }
                                   =>
                                   whatsave (acc + this - muldiv (acc, this, size), size, vl, al)
                                   where
                                       this = case v
                                                  ncf::CODETEMP v' => case (get v')   
                                                                FUN _  => save;
                                                                REC _  => save;
                                                                _      => 0;
                                                            esac;

                                                  _      => save;
                                              esac;
                                   end;

                                 _ => raise exception DIE "Expand: whatsave: not ARG nor SEL";
                             esac;
                        fi;

                    whatsave (acc, size, _, _)
                        =>
                        acc;
                end;


                ################################################################
                # Should a function application be inlined?
                ################################################################
                #
                fun should_expand
                        ( d,                                                                                                    # Path length from entry to current function 
                          u,                                                                                                    # Unroll level 
                          e as ncf::TAIL_CALL { fn => v, args => vl }, 
                          FUN { escape, call, unroll_call, size=>REF size, args, body,
                                          level, within=>REF within, ... }
                        )
                        =>
                        if (*call + *escape == 1)
                            #
                            FALSE;
                        else
                            stupidloop                                                                                          # Prevent infinite loops  at compile time
                                = 
                                case (v, body)
                                    #
                                    (ncf::CODETEMP vv,  ncf::TAIL_CALL { fn => ncf::CODETEMP v', ... }) =>   vv==v'; 
                                    (ncf::LABEL    vv,  ncf::TAIL_CALL { fn => ncf::LABEL    v', ... }) =>   vv==v'; 
                                    _ => FALSE;
                                esac;

                            calls = case u
                                        UNROLL _ => *unroll_call;
                                        _        => *call;
                                    esac;

                            small_fun_size
                                =
                                case u
                                    UNROLL _ =>  0;
                                    _        => 50;
                                esac;

                            savings =   whatsave (0, size, vl, args);

                            predicted
                                = 
                                {   real_increase = size-savings-(1+length vl);

                                    real_increase * calls - 
                                        # Don't subtract off the original body if
                                        # the original body is huge (because we might
                                        # have guessed wrong and the consequences are
                                        # too nasty for big functions); or if we're
                                        # in unroll mode
                                        #       
                                        if (size < small_fun_size)  size;
                                        else                        0;
                                        fi;
                                };

                            depth = 2;
                            max   = 2;

                            if (FALSE and debug)
                                 prettyprint_nextcode::print_nextcode_expression e;
                                 debugprint (int::to_string predicted); debugprint "   "; 
                                 debugprint (int::to_string bodysize); debugprint "\n";
                            fi;

                            not stupidloop
                            and case u
                                    UNROLL lev
                                        => 
                                        # Unroll if: the loop body doesn't make function
                                        # calls or "unroll_recursion" is turned on; and 
                                        # we are within the definition of the function; 
                                        # and it looks like things won't grow too much.
                                        #
                                        (*coc::unroll_recursion or level >= lev)
                                        and within and predicted <= bodysize;

                                    NO_UNROLL
                                        =>
                                        *unroll_call == 0 and
                                        not within and
                                        (predicted <= bodysize  
                                          or (*escape==0 and calls == 1));

                                    HEADERS => FALSE;                                           # Shouldn't get here 

                                    ALL =>
                                        (predicted <= bodysize  
                                          or (*escape==0 and calls == 1));
                                esac;

                        fi;

                    should_expand _
                        =>
                        raise exception DIE "Expand: should_expand: unexpected argument";
                end;

                Decision = YES  { formals: List( ncf::Codetemp ),
                                  body:    ncf::Instruction
                                } 
                         | NO  Int      # How many no's in a row.
                         ;
                # There is really no point in
                # making decisions a REF.
                #  This should be changed one day.                              # XXX SUCKO FIXME
                #       
                decisions =  REF NIL
                          :  Ref( List( Decision ) )
                          ;
                #
                fun decide_yes x
                    =
                    decisions := YES x ! *decisions;
                #
                fun decide_no ()
                    =
                    decisions :=  case *decisions
                                      NO n ! rest =>  NO (n+1) ! rest;
                                      d           =>  NO 1 ! d;
                                  esac;


                # *******************************************************************
                #  pass2: mark function applications to be inlined.                  
                # *******************************************************************
                #
                fun pass2
                    ( d,        #  path length from start of current function 
                      u,        #  unroll-info 
                      e         #  expression to traverse 
                    )
                    =
                    case e
                        #
                        ncf::DEFINE_RECORD  { fields, next, ... } => pass2 (d+2+length fields, u, next);
                        #
                        ncf::GET_FIELD_I            { next, ... } => pass2 (d+1, u, next);
                        ncf::GET_ADDRESS_OF_FIELD_I { next, ... } => pass2 (d+1, u, next);
                        #
                        ncf::TAIL_CALL { fn, ... }
                            => 
                            case (getval fn)
                                #
                                info as FUN { args, body, ... }
                                    =>
                                    (should_expand (d, u, e, info))
                                        ??   decide_yes { formals=>args, body }
                                        ::   decide_no ();

                                _ => decide_no ();
                           esac;

                        ncf::DEFINE_FUNS { funs, next }
                            => 
                            {   apply fundef funs;
                                #
                                pass2 (d + length(funs), u, next);
                            }
                            where
                                fun fundef (ncf::NO_INLINE_INTO, _, _, _, _)
                                        =>
                                        ();

                                    fundef (fk, f, vl, cl, e)
                                        =>
                                        case (get f)
                                            #
                                            FUN { level, within, escape=>REF escape, ... }
                                                =>
                                                {   u' = case u
                                                             #
                                                             UNROLL _ => UNROLL level;
                                                             _        => u;
                                                         esac;
                                                    #
                                                    fun conform ((ncf::CODETEMP x) ! r, z ! l)
                                                            =>
                                                            (x==z) and conform (r, l);

                                                        conform (_ ! r, _ ! l) => FALSE;
                                                        conform (   [],    []) => TRUE;
                                                        conform _              => FALSE;
                                                    end;

                                                    within := TRUE; 

                                                    pass2 (0, u', e)
                                                    then
                                                        within := FALSE;
                                               };

                                           _ => ();     #  Cannot happen 
                                        esac;
                                end;
                            end;

                        ncf::JUMPTABLE { nexts, ... }
                            =>
                            apply  (\\ e = pass2 (d+2, u, e))  nexts;

                        ( ncf::FETCH_FROM_RAM { next, ... }
                        | ncf::STORE_TO_RAM   { next, ... }
                        | ncf::ARITH          { next, ... }
                        | ncf::PURE           { next, ... }
                        | ncf::RAW_C_CALL     { next, ... }
                        )   =>
                            pass2 (d+2, u, next);

                        ncf::IF_THEN_ELSE { then_next, else_next, ... }
                            =>
                            {   pass2 (d+2, u, then_next); 
                                pass2 (d+2, u, else_next);
                            };
                    esac;


                recursive my gamma
                    =
                    \\  ncf::DEFINE_RECORD { kind, fields, to_temp, next               }
                     => ncf::DEFINE_RECORD { kind, fields, to_temp, next => gamma next };

                        ncf::GET_FIELD_I   { i, record, to_temp, type, next               }
                     => ncf::GET_FIELD_I   { i, record, to_temp, type, next => gamma next };

                        ncf::GET_ADDRESS_OF_FIELD_I { i, record, to_temp,       next               }
                     => ncf::GET_ADDRESS_OF_FIELD_I { i, record, to_temp,       next => gamma next };
                        #
                        e as ncf::TAIL_CALL _ => e;
                        #
                        ncf::DEFINE_FUNS { funs, next }
                            =>
                            ncf::DEFINE_FUNS  { funs => map fundef funs,  next => gamma next }
                            where
                                fun fundef (z as (ncf::NO_INLINE_INTO, _, _, _, _))
                                        =>
                                        z;

                                    fundef (z as (fk, f, vl, cl, e))
                                        =>
                                        case (get f)
                                            #
                                            FUN { escape=>REF escape,
                                                  call,
                                                  unroll_call,
                                                  invariant=>REF inv,
                                                  ...
                                                }
                                                =>
                                                if (escape == 0 and *unroll_call > 0
                                                    and (*call - *unroll_call > 1 
                                                            or list::exists (\\ t=t) inv)
                                                )
                                                    f' = copy_lvar f;
                                                    vl' = map copy_lvar vl;
                                                    #
                                                    fun drop (FALSE ! r, a ! s) =>  a ! drop (r, s);
                                                        drop (TRUE  ! r, _ ! s) =>      drop (r, s);
                                                        drop _ => NIL;
                                                    end;

                                                    newformals
                                                        =
                                                        label f' ! map ncf::CODETEMP (drop (inv, vl'));

                                                    e' = substitute (newformals,
                                                                       f ! drop (inv, vl),
                                                                       gamma e,
                                                                       FALSE); 

                                                    click "!"; debugprint (int::to_string f);

                                                    enter 0 (fk, f', vl', cl, e');

                                                    ( fk,
                                                      f,
                                                      vl,
                                                      cl,
                                                      ncf::DEFINE_FUNS
                                                        {
                                                          funs => [ (fk, f', vl', cl, e') ],
                                                          #
                                                          next => ncf::TAIL_CALL { fn =>  label f',
                                                                                   args =>  map  ncf::CODETEMP  vl
                                                                                 }
                                                        }
                                                    );

                                                else
                                                    (fk, f, vl, cl, gamma e);
                                                fi;

                                          _ => z;               #  Cannot happen 
                                      esac;
                                 end;
                            end;

                        ncf::JUMPTABLE { i, xvar, nexts }  =>  ncf::JUMPTABLE { i, xvar, nexts => map gamma nexts };

                        ncf::ARITH  { op, args, to_temp, type, next } =>  ncf::ARITH  { op, args, to_temp, type,  next => gamma next  };
                        ncf::PURE   { op, args, to_temp, type, next } =>  ncf::PURE  { op, args, to_temp, type,  next => gamma next  };

                        ncf::FETCH_FROM_RAM { op, args, to_temp, type, next } =>  ncf::FETCH_FROM_RAM { op, args, to_temp, type, next => gamma next };
                        ncf::STORE_TO_RAM   { op, args,                next } =>  ncf::STORE_TO_RAM   { op, args,                next => gamma next };

                        ncf::RAW_C_CALL { kind, cfun_name, cfun_type, args, to_ttemps,  next               }
                     => ncf::RAW_C_CALL { kind, cfun_name, cfun_type, args, to_ttemps,  next => gamma next };
                        #
                        ncf::IF_THEN_ELSE { op, args, xvar, then_next, else_next }
                     => ncf::IF_THEN_ELSE { op, args, xvar, then_next => gamma then_next,
                                                            else_next => gamma else_next
                                          };
                end ;


                recursive my beta
                    =
                    \\  ncf::DEFINE_RECORD { kind, fields, to_temp, next               }
                     => ncf::DEFINE_RECORD { kind, fields, to_temp, next =>  beta next };
                        #
                        ncf::GET_FIELD_I   { i, record, to_temp, type, next              }
                     => ncf::GET_FIELD_I   { i, record, to_temp, type, next => beta next };

                        ncf::GET_ADDRESS_OF_FIELD_I { i, record, to_temp,       next }
                     => ncf::GET_ADDRESS_OF_FIELD_I { i, record, to_temp,       next => beta next };

                        e as ncf::TAIL_CALL { fn, args }
                            => 
                            case *decisions
                                #
                                YES { formals, body } ! rest
                                    =>
                                    {   click "^";

                                        case fn
                                            #
                                            ncf::CODETEMP vv =>  debugprint (int::to_string vv);
                                            _           =>  ();
                                        esac;

                                        debugflush();
                                        decisions := rest;
                                        substitute (args, formals, body, TRUE);
                                    };

                                NO 1 ! rest => { decisions := rest; e;};
                                NO n ! rest => { decisions := NO (n - 1) ! rest; e;};
                                [] => e;        # Cannot happen.
                            esac;

                        ncf::DEFINE_FUNS { funs, next }
                            => 
                            ncf::DEFINE_FUNS  { funs =>  map fundef funs,
                                                next =>  beta next
                                              }
                            where
                                fun fundef (z as (ncf::NO_INLINE_INTO, _, _, _, _)) =>  z;
                                    fundef (fk, f, vl, cl, e)                       =>  (fk, f, vl, cl, beta e);
                                end;
                            end;

                        ncf::JUMPTABLE { i, xvar, nexts }  =>  ncf::JUMPTABLE { i, xvar, nexts => map beta nexts };

                        ncf::ARITH { op, args, to_temp, type, next }   =>  ncf::ARITH { op, args, to_temp, type,  next => beta next  };
                        ncf::PURE  { op, args, to_temp, type, next }   =>  ncf::PURE  { op, args, to_temp, type,  next => beta next  };

                        ncf::FETCH_FROM_RAM { op, args, to_temp, type, next } =>  ncf::FETCH_FROM_RAM { op, args, to_temp, type, next => beta next };
                        ncf::STORE_TO_RAM   { op, args,                next } =>  ncf::STORE_TO_RAM   { op, args,                next => beta next };

                        ncf::RAW_C_CALL { kind, cfun_name, cfun_type, args, to_ttemps, next              }
                     => ncf::RAW_C_CALL { kind, cfun_name, cfun_type, args, to_ttemps, next => beta next };

                        ncf::IF_THEN_ELSE { op, args, xvar, then_next, else_next }
                     => ncf::IF_THEN_ELSE { op, args, xvar, then_next => beta then_next,
                                                            else_next => beta else_next
                                          };
                end;


                #
                fun pass2_beta (mode, e)
                    =
                    {   pass2 (0, mode, e);
                        discard_pass1_info();
                        debugprint "Expand: finishing pass2\n"; debugflush();

                        case *decisions
                            #
                            [NO _] => {   debugprint "No expansions to do.\n";
                                          debugflush();
                                          e;
                                      };

                           _ => {   decisions := reverse *decisions;
                                    debugprint "Beta: ";

                                    beta e
                                    then
                                        {   debugprint "\n";
                                            debugflush();
                                        };
                                };
                        esac;
                    };

                gamma = \\ c =  {   debugprint "Gamma: ";
                                    gamma c
                                    then
                                        {   debugprint "\n";
                                            debugflush();
                                        };
                                };

                #  Body of expand 
                notearg fvar;
                apply notearg fargs;

        #       if *coc::printit then prettyprint_nextcode::print_nextcode_expression cexp


                debugprint("Expand: pass1: ");
                debugprint (int::to_string (pass1 0 cexp));
                debugprint "\n";
                debugflush();

                if unroll
                    #
                    debugprint(" (unroll)\n");
                    debugflush();

                    e' = pass2_beta (UNROLL 0, cexp);

                    if *clicked_any 
                        #
                        do_nextcode_inlining
                          {
                            function =>  (fkind, fvar, fargs, ctyl, e'),
                            table    =>  typetable,
                            bodysize, click, unroll,
                            after_closure,
                            do_headers
                          };
                    else
                        # debugprint("\nExpand\n"); 
                        # debugflush();
                        # (fkind, fvar, fargs, ctyl, pass2_beta (ALL, cexp));

                        (fkind, fvar, fargs, ctyl, e');
                    fi;

                else

                   if *coc::unroll
                        #
                        debugprint(" (headers)\n");
                        debugflush();

                        e' = if do_headers  gamma cexp;
                             else                 cexp;
                             fi;

                        if *clicked_any
                            #
                            do_nextcode_inlining
                              {
                                function => (fkind, fvar, fargs, ctyl, e'),
                                table    => typetable,
                                bodysize,
                                click,
                                unroll,
                                after_closure, 
                                do_headers  => FALSE
                              };
                        else
                            debugprint(" (non-unroll 1)\n");
                            debugflush();
                            (fkind, fvar, fargs, ctyl, pass2_beta (NO_UNROLL, e'));
                        fi;

                  else
                      debugprint(" (non-unroll 2)\n");
                      debugflush();
                      (fkind, fvar, fargs, ctyl, pass2_beta (ALL, cexp));
                  fi;
            fi;
        };                      # fun             do_nextcode_inlining
    };                          # generic package do_nextcode_inlining_g
end;




Comments and suggestions to: bugs@mythryl.org

PreviousUpNext