PreviousUpNext

15.4.487  src/lib/compiler/back/top/improve-nextcode/split-nextcode-fns-into-known-vs-escaping-versions-g.pkg

## split-nextcode-fns-int-known-vs-escaping-versions-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




#    "Introduces eta-redexes such that all functions fall
#     into one of two categories:  Either all of their call
#     sites are known or none of their call sites are known.
#
#    "I.e., if a function f is not in one of those two categories,
#     a new function f' = \x.fx is introduced and all places where
#     f "escapes" (i.e., is passed as a higher function argument
#     rather than being directly called) are redirected to use f'."
#
#     [...]
#
#    "The need for etasplit is avoided by using slightly
#     different formulations of the relevant optimizations."
#
#          -- Principled Compilation and Scavenging
#             Stefan Monnier, 2003 [PhD Thesis, U Montreal]
#             http://www.iro.umontreal.ca/~monnier/master.ps.gz 



# Perform the "eta-split" transformation on nextcode expressions.
# Its purpose is to give two entry points to functions which
# both escape and which are called at known points.
#
# The function is split into two functions:
#
#     A known function that is used for calls.
#
#     A strictly escaping function used for all
#     escaping occurrences of the original function.
#
# The new escaping function simply calls the new known function.
#
# I do not bother to split known functions, or functions that only
# escape.  Furthermore, no fates are split.  I expect that
# the majority of fates are escaping, except for a few known
# fates that were created for reasons of space complexity (as
# the join of two branches, for example).  I doubt there are many
# fates which both escape and have known calls.  -- Trevor Jim


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 Split_Nextcode_Fns_Into_Known_Vs_Escaping_Versions {
        #
        split_nextcode_fns_into_known_vs_escaping_versions
          :
          { function:   ncf::Function,
            table:      iht::Hashtable( hut::Uniqtypoid ),
            click:      String -> Void
          }
          ->
          ncf::Function;
    };
end;



# We are invoked from:
#
#     src/lib/compiler/back/top/improve-nextcode/run-optional-nextcode-improvers-g.pkg

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


    generic package   split_nextcode_fns_into_known_vs_escaping_versions_g   (
        #             ====================================================
        #
        machine_properties:  Machine_Properties                 # Typically                                       src/lib/compiler/back/low/main/intel32/machine-properties-intel32.pkg
    )
    : (weak)  Split_Nextcode_Fns_Into_Known_Vs_Escaping_Versions                # Split_Nextcode_Fns_Into_Known_Vs_Escaping_Versions    is from   src/lib/compiler/back/top/improve-nextcode/split-nextcode-fns-into-known-vs-escaping-versions-g.pkg
    {

        fun share_name (x, ncf::CODETEMP y) => tmp::share_name (x, y); 
            share_name _ => ();
        end;

        fun split_nextcode_fns_into_known_vs_escaping_versions
              {
                function => (fkind, fvar, fargs, ctyl, cexp),
                table    => typetable,
                click
              }
            =
            {   debug =   *global_controls::compiler::debugnextcode;                                    # FALSE 

                fun debugprint s  =  if debug  global_controls::print::say s;    fi;
                fun debugflush () =  if debug  global_controls::print::flush();  fi;

                rep_flag  =  machine_properties::representations;
                type_flag = *global_controls::compiler::checknextcode1 and rep_flag;


                exception SPLIT1;

                fun getty v
                    = 
                    if type_flag 
                        #
                        (iht::get  typetable  v)
                        except
                            _ =
                                 {   global_controls::print::say
                                         (  "SPLIT1: Can't find the variable "
                                         +  (int::to_string v)
                                         + " in the typetable ***** \n"
                                         );

                                     raise exception SPLIT1;
                                 };
                    else
                        hcf::truevoid_uniqtypoid;
                    fi;

                fun addty (f, t)
                    =
                    if type_flag  iht::set typetable (f, t); fi;

                fun copy_lvar v
                    =
                    {   x = tmp::clone_highcode_codetemp (v);
                        addty (x, getty v);
                        x;
                    };

                stipulate

                    exception SPLIT2;

                    my m:  iht::Hashtable( ncf::Value )
                        =
                        iht::make_hashtable  { size_hint => 32,  not_found_exception => SPLIT2 };

                herein

                    fun makealias x
                        =
                        {   share_name x;
                            iht::set m x;
                        };

                    fun alias (ncf::CODETEMP v)
                            =>
                            (THE (iht::get  m  v))
                            except
                                SPLIT2 = NULL;

                        alias _ => NULL;
                    end;
                end;

                stipulate

                    exception SPLIT3;

                    my m:  iht::Hashtable { used:  Ref( Int ), called:  Ref( Int ) }
                         =
                         iht::make_hashtable  { size_hint => 32,  not_found_exception => SPLIT3 };

                herein

                    get = iht::get  m;

                    fun enter_fn (_, f, _, _, _)
                        =
                        iht::set m (f,{ used=>REF 0, called=>REF 0 } );
                        #
                        # Perhaps I shouldn't bother to enter_fn fates?

                    fun use (ncf::CODETEMP v)
                            =>
                            {   (get v) ->   { used, ... };
                                used := *used + 1;
                            }
                            except
                                SPLIT3 = ();

                        use _ => ();
                    end;

                    fun call (ncf::CODETEMP v)
                            =>
                            {   (get v) ->   { used, called };
                                used   :=  *used   + 1;
                                called :=  *called + 1;
                             }
                             except
                                 SPLIT3 = ();

                        call _ => ();
                    end;
                end;



                # Get usage information and
                # mark whether or not we will
                # be doing any splits.

                found_split = REF FALSE;

                recursive my pass1
                    = 
                    \\  ncf::DEFINE_RECORD          { fields, next, ... }  =>   {   apply (use o #1) fields;   pass1 next;   };
                        ncf::GET_FIELD_I            { record, next, ... }  =>   {   use record;                pass1 next;   };
                        ncf::GET_ADDRESS_OF_FIELD_I { record, next, ... }  =>   {   use record;                pass1 next;   };
                        #
                        ncf::JUMPTABLE { i, nexts, ... } => { use i; apply pass1 nexts;};
                        #
                        ncf::IF_THEN_ELSE { args, then_next, else_next, ... }
                            =>
                            {   apply use args;
                                pass1 then_next;
                                pass1 else_next;
                            };
                        #
                        ncf::STORE_TO_RAM    { args, next, ... } => { apply use args;  pass1 next; };
                        ncf::FETCH_FROM_RAM  { args, next, ... } => { apply use args;  pass1 next; };
                        #
                        ncf::ARITH       { args, next, ... } => {  apply use args;  pass1 next;  };
                        ncf::PURE       { args, next, ... } => {  apply use args;  pass1 next;  };
                        ncf::RAW_C_CALL { args, next, ... } => {  apply use args;  pass1 next;  };
                        #
                        ncf::TAIL_CALL { fn, args } =>   { call fn;  apply use args; };

                        ncf::DEFINE_FUNS { funs, next }
                            =>
                            {   # Any changes to dosplit() (below)
                                # must be reflected here:
                                #
                                fun checksplit NIL
                                        =>
                                        ();

                                    checksplit ((ncf::FATE_FN, _, _, _, _) ! tl)
                                        =>
                                        checksplit tl;

                                    checksplit ((_, f, _, _, _) ! tl)
                                        =>
                                        {   (get f) ->  { used=>REF u, called=>REF c };

                                            if (u!=c and c!=0)   found_split := TRUE;
                                            else                 checksplit tl;
                                            fi;
                                       };
                                end;

                                apply enter_fn  funs;

                                apply (\\ (_, _, _, _, body) =  pass1 body)
                                     funs;

                                pass1 next;

                                if (not *found_split)  checksplit funs;   fi;
                           };
                    end;

                recursive my reduce
                    = 
                    \\  ncf::DEFINE_RECORD { kind, fields, to_temp, next                }
                     => ncf::DEFINE_RECORD { kind, fields, to_temp, next => reduce next };
                        #
                        ncf::GET_FIELD_I   { i, record, to_temp, type, next                }
                     => ncf::GET_FIELD_I   { i, record, to_temp, type, next => reduce next };
                        #
                        ncf::GET_ADDRESS_OF_FIELD_I { i, record, to_temp,       next } 
                     => ncf::GET_ADDRESS_OF_FIELD_I { i, record, to_temp,       next => reduce next };
                        #
                        ncf::JUMPTABLE { i, xvar, nexts }
                     => ncf::JUMPTABLE { i, xvar, nexts => map reduce nexts };
                        #
                        ncf::IF_THEN_ELSE { op, args, xvar, then_next,                     else_next                    }
                     => ncf::IF_THEN_ELSE { op, args, xvar, then_next => reduce then_next, else_next => reduce else_next };
                        #
                        ncf::ARITH { op, args, to_temp, type, next }      =>  ncf::ARITH { op, args, to_temp, type,  next => reduce next  };
                        ncf::PURE { op, args, to_temp, type, next }      =>  ncf::PURE { op, args, to_temp, type,  next => reduce next  };
                        #
                        ncf::FETCH_FROM_RAM { op, args, to_temp, type, next } =>  ncf::FETCH_FROM_RAM { op, args, to_temp, type, next => reduce next };
                        ncf::STORE_TO_RAM   { op, args,                next } =>  ncf::STORE_TO_RAM   { op, args,                next => reduce 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 => reduce next  };

                        (e as ncf::TAIL_CALL { fn, args })
                            => 
                            case (alias fn)
                                #
                                THE fn =>  ncf::TAIL_CALL { fn, args };
                                NULL     =>  e;
                            esac;

                        ncf::DEFINE_FUNS { funs, next }
                            =>
                            {   fun dosplit NIL =>   NIL;

                                    dosplit ((hd as (ncf::PUBLIC_FN, f, vl, cl, body)) ! tl)
                                        =>
                                        {   (get f) ->   { used=>REF u, called=>REF c };

                                            if (u!=c and c!=0)
                                                #
                                                # Function escapes AND
                                                # has known call sites:
                                                #
                                                f'  = copy_lvar f;
                                                vl' = map copy_lvar vl;
                                                click "S";
                                                makealias (f, ncf::CODETEMP f');

                                                ( (ncf::NO_INLINE_INTO,           f, vl', cl, ncf::TAIL_CALL { fn => ncf::CODETEMP f', args => map ncf::CODETEMP vl' })
                                                ! (ncf::PUBLIC_FN, f', vl, cl, body)
                                                ! (dosplit tl)
                                                );

                                            else hd ! (dosplit tl);
                                            fi;
                                        };

                                    dosplit (hd ! tl) => hd ! (dosplit tl);
                                end;

                                funs' = dosplit funs;

                                # Could check for NO_INLINE_INTO in reduce_body, so
                                # that we don't reduce in the body of something we've
                                # just split; but we might be using NO_INLINE_INTO
                                # for something else (e.g. UNCURRY).

                                fun reduce_body (fk, f, vl, cl, body)
                                    =
                                    (fk, f, vl, cl, reduce body);

                                ncf::DEFINE_FUNS  { funs =>  map reduce_body funs',
                                                    next =>  reduce next
                                                  };
                           };
                  end ;

                # Body of split_known_escaping_functions 

                debugprint "Etasplit: ";
                pass1 cexp;

                if *found_split   (fkind, fvar, fargs, ctyl, reduce cexp);
                else              (fkind, fvar, fargs, ctyl, cexp);
                fi
                then
                    debugprint "\n";

            };                                          # fun split_known_escaping_functions 
    };                                                  # generic package  split_known_escaping_functions_g 
end;                                                    # stipulate




## Copyright 1996 by Bell Laboratories 
## Subsequent changes by Jeff Prothero Copyright (c) 2010-2015,
## released per terms of SMLNJ-COPYRIGHT.


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext