PreviousUpNext

15.4.502  src/lib/compiler/back/top/lambdacode/generalized-sethi-ullman-reordering.pkg

## generalized-sethi-ullman-reordering.pkg

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



# Sethi-Ullman reordering of expression trees to minimize register usage
#
# See: Andrew W. Appel and  Kenneth J. Supowit,
# Generalizations of the Sethi-Ullman algorithm for register allocation,
# Software---Practice & Experience 17 (6):417-421, 1987.
#
# In the expression (M, N) or (M N) it may be that N requires more
# registers to compute than M, in which case it will be better
# to compute   let n=N and m=M in (M, N) end instead.
#
# This is no good if both M and N have side effects (read or write).
# And it's not safe for space if N is the last use of some large
# chunk, and M contains a function call that might allot an
# arbitrarily large amount.
#
# What does "last use" mean?
#    1. SELECT (0, r) where the other fields of r might now be dead.
#    2. boxed (r) where all the fields of r might now be dead.
#    3. etc.
# This is ONLY important if r is potentially of unbounded size.
# Thus it doesn't apply to (for example) boxed floats, which are small.
#
# The property "possible read or write side effect" is called "side."
# The property of "possible last use of some large chunk" is called "fetch."
# The property of "possible unbounded allocation" is called "allot."



###                 "Your work isn't a high stakes,
###                  nail-biting professional challenge.
###
###                 "It's a form of play.
###
###                 "Lighten up and have fun with it."
###
###                                   -- Sol LeWitt


api Generalized_Sethi_Ullman_Reordering {
    #
    reorder_via_generalized_sethi_ullman
        :
        lambdacode::Lambda_Expression
     -> lambdacode::Lambda_Expression; 
}; 



stipulate
    include package   access;
    include package   lambdacode;
    #
    package hbo = highcode_baseops; 
herein 

    # This package is nowhere referenced:
    #
    package   generalized_sethi_ullman_reordering
    : (weak)  Generalized_Sethi_Ullman_Reordering               # Generalized_Sethi_Ullman_Reordering   is from   src/lib/compiler/back/top/lambdacode/generalized-sethi-ullman-reordering.pkg
    {

        fun bug s = error_message::impossible ("Reorder: " $ s);

        /*
        enum info X = I of { expression:X,  #  The expression itself 
                              regs: Int,  /* how many registers needed for the
                                            evaluation of this expression */
                              side: Bool, /* Does this expression read or write a REF
                                            (conservative approximation) */
                              fetch: Bool, #  See explanation above 
                              allot: Bool  /* Might this expression allot
                                             more than a constant number of cells? */
                              }

        fun swap (I { side=TRUE, ... }, I { side=TRUE, ... } ) = FALSE
              #  Don't interchange side effects 
          | swap (I { fetch=TRUE, ... }, I { allot=TRUE, ... } ) = FALSE
              /* Don't move a SELECT(_, r) to the right of a big allocation,
               as this is not safe for space: If r is otherwise dead, we want
               its other fields to be garbage collected before allocating a lot */
          | swap (I { regs=ra, ... }, I { regs=rb, ... } ) = ra < rb
              /* Evaluate the expression requiring more registers first,
                 then hold its value in one register while evaluating the
                 other expression.  Minimizes max register usage. 
              */

        fun inorder (a . (rest as b . _)) = if swap (a, b) then FALSE else inorder rest
          | inorder _ = TRUE

        fun insert (a as (_, _), b . c) = if swap(#1 a, #1 b) then b . insert (a, c) 
                                                           else a . insert (b, c)
          | insert (a, NIL) = a . NIL

        fun cost ((I { regs, ... }, _) . rest) = int::max (regs, 1+cost rest)
          | cost NIL = 0

        fun anyside ((I { side=TRUE, ... }, _) . rest) = TRUE
          | anyside (_ . rest) = anyside rest
          | anyside NIL = FALSE

        fun anyfetch ((I { fetch=TRUE, ... }, _) . rest) = TRUE
          | anyfetch (_ . rest) = anyfetch rest
          | anyfetch NIL = FALSE

        fun anyalloc ((I { allot=TRUE, ... }, _) . rest) = TRUE
          | anyalloc (_ . rest) = anyalloc rest
          | anyalloc NIL = FALSE

        fun combine (l, do_it) =
         let fun g (I { expression=e1, side=s1, regs=r1, fetch=f1, allot=a1 } . rest, e, s, r, f, a) =
                    g (rest, e1 . e, s1 or s, int::max (1+r, r1), f1 or f, a1 or a)
               | g (NIL, e, s, r, f, a) = I { expression=do_it e, side=s, regs=r, fetch=f, allot=a }
          in g (reverse l, NIL, FALSE, 0, FALSE, FALSE)
         end

        fun getexp (I { expression, ... } ) = expression

        fun fetchprim hbo::IS_BOXED = TRUE
          | fetchprim hbo::IS_UNBOXED = TRUE
          | fetchprim hbo::VECTOR_LENGTH_IN_SLOTS = TRUE
          | fetchprim hbo::HEAPCHUNK_LENGTH_IN_WORDS = TRUE
          | fetchprim hbo::SET_REFCELL = TRUE
          | fetchprim hbo::GET_REFCELL_CONTENTS = FALSE  /* The old REF cell might now be dead,
                                         but not its contents! */
          | fetchprim hbo::RW_VECTOR_SET = TRUE  /* The "last use" in question is the OLD contents of the rw_vector slot stored into */

          | fetchprim hbo::RW_VECTOR_SET_WITH_BOUNDSCHECK = TRUE
          | fetchprim hbo::SET_VECSLOT_TO_BOXED_VALUE = TRUE
          | fetchprim hbo::SET_VECSLOT_TO_TAGGED_INT_VALUE = TRUE

          | fetchprim hbo::RW_VECTOR_GET = TRUE
          | fetchprim hbo::RW_VECTOR_GET_WITH_BOUNDSCHECK = TRUE
          | fetchprim hbo::RO_VECTOR_GET = TRUE

          | fetchprim (hbo::GET_VECSLOT_NUMERIC_CONTENTS _) = TRUE
          | fetchprim (hbo::SET_VECSLOT_TO_NUMERIC_VALUE _) = TRUE
          | fetchprim hbo::GET_BATAG_FROM_TAGWORD = TRUE
          | fetchprim hbo::GET_STATE_OF_WEAK_POINTER_OR_SUSPENSION = TRUE
          | fetchprim hbo::USELVAR = TRUE
          | fetchprim _ = FALSE


        fun sort (do_it, l) = if inorder l 
             then combine (l, do_it)
             else let fun somevar (I { expression=VAR _, ... } ) = NULL
                        | somevar (I { expression=INT _, ... } ) = NULL
                        | somevar (I { expression=REAL _, ... } ) = NULL
                        | somevar (I { expression=STRING _, ... } ) = NULL
                        | somevar _ = THE (highcode_codetemp::make_lambda_variable())

                      l' = map (\\ x => (x, somevar x)) l

                      l'' = fold_backward insert [] l'

                      fun rename (_, THE v) = VAR v
                        | rename (I { expression, ... }, NULL) = expression

                      fun bind ((_, NULL), e) = e
                        | bind ((I { expression, ... }, THE v), e) = LET (v, SVAL expression, e)

                   in I { regs= cost l'',
                        side = anyside l'',
                        fetch = anyfetch l'',
                        allot = anyalloc l'',
                        expression = fold_backward bind (do_it (map rename l')) l''
                       }
                  end

        many = 12   #  how many regs to charge a function call 

        my rec lpsv:  value -> info( value ) =
          \\ e as VAR _ => I { regs=0, side=FALSE, expression=e, fetch=FALSE, allot=FALSE }
           | e as INT _ => I { regs=0, side=FALSE, fetch=FALSE, allot=FALSE, expression=e }
           | e as WORD _ => I { regs=0, side=FALSE, fetch=FALSE, allot=FALSE, expression=e }
           | e as INT1 _ => I { regs=0, side=FALSE, fetch=FALSE, allot=FALSE, expression=e }
           | e as WORD32 _ => I { regs=0, side=FALSE, fetch=FALSE, allot=FALSE, expression=e }
           | e as REAL _ => I { regs=0, side=FALSE, fetch=FALSE, allot=FALSE, expression=e }
           | e as STRING _ => I { regs=0, side=FALSE, fetch=FALSE, allot=FALSE, expression=e }
           | e as PRIM (i, t, _) => I { regs=0, side=FALSE, fetch=FALSE, allot=FALSE, expression=e }
           | _ => bug "unexpected case in lpsv"

        and loop:  Lambda_Expression -> info( Lambda_Expression ) =
          \\ e as SVAL sv => 
                let my I { regs, side, expression, fetch, allot } = lpsv sv
                 in I { regs=regs, side=side, expression=SVAL expression, fetch=fetch, allot=alloc }
                end
           | e as EXCEPTION_TAG _ => I { regs=1, side=TRUE, fetch=FALSE, allot=TRUE, expression=e }

           | FN (v, t, e) => I { regs=1, side=FALSE, fetch=FALSE, allot=FALSE,
                            expression= FN (v, t, getexp (loop e)) }
           | FIX (vl, t, el, e) => 
                  let my I { regs, side, expression, fetch, allot } = loop e 
                   in I { regs=regs+1, side=side, fetch=fetch, allot=alloc,
                        expression=FIX (vl, t, el, expression) }
                  end
           | APPLY (p as PRIM (i, t, _), b) => 
                  let my I { regs, side, fetch, allot, expression=e1 } = lpsv b
                   in I { regs=int::max (1, regs), side=not (hbo::purePrimop i), 
                        allot=FALSE, fetch=fetchprim i, expression=APPLY (p, e1) }
                  end
           | LET (v, b, a) =>
                  let my I { regs=ra, side=sa, expression=ea, fetch=fa, allot=aa } =loop a
                      my I { regs=rb, side=sb, expression=eb, fetch=fb, allot=ab } =loop b
                   in I { regs=int::max (rb, 1+ra), side=sa or sb,
                        fetch= fa or fb, allot=aa or ab,
                        expression=LET (v, eb, ea) }
                  end
           | APPLY (a, b) => 
                  let my I { expression=e1, ... } = 
                          sort (\\ [x, y]=>APPLY (x, y), map lpsv [a, b])
                   in I { regs=many, side=TRUE, expression=e1, fetch=TRUE, allot=TRUE }
                  end
           | SWITCH (v0, sign, l, d) => 
                  let my I { regs, side, expression, fetch, allot } = lpsv v0
                      fun combine ((c, e), (r, s, f, a, el)) =
                            let my I { regs, side, expression, fetch, allot } =loop e
                             in (int::max (r, regs), s or side, f or fetch,
                                a or allot, (c, expression) . el)
                            end
                      my (lr, ls, lf, la, l') = fold_backward combine (regs, side, fetch, allot,[]) l

                   in case d 
                       of THE d' => 
                           let my (lr, ls, lf, la,[((), de)]) = 
                                             combine(((), d'), (lr, ls, lf, la, NIL))
                            in I { regs=lr, side=ls, fetch=lf, allot=la,
                                 expression=SWITCH (expression, sign, l', THE de) }
                           end
                        | NULL => I { regs=lr, side=ls, fetch=lf, allot=la,
                                    expression=SWITCH (expression, sign, l', NULL) }
                  end

           | CON (c, ts, v) => let my I { regs, side, expression, fetch, allot } = lpsv v
                          in I { regs=int::max (1, regs), side=side, fetch=fetch, allot=alloc,
                               expression=CON (c, ts, expression) } #  Close enuf 
                         end
           | DECON (c, ts, v) => let my I { regs, side, expression, fetch, allot } = lpsv v
                          in I { regs=int::max (regs, 1), side=side, fetch=TRUE, allot=alloc,
                               expression=DECON (c, ts, expression) }
                         end
           | RECORD l => sort (\\ x => RECORD x, map lpsv l)
           | PACKAGE_RECORD l => sort (\\ x => PACKAGE_RECORD x, map lpsv l)
           | VECTOR (l, t) => sort (\\ x => VECTOR (x, t), map lpsv l)
           | SELECT (i, e) => let my I { regs, side, expression, fetch, allot } = lpsv e
                             in I { regs=int::max (1, regs), side=side, fetch=TRUE, allot=alloc,
                                  expression=SELECT (i, expression) }
                            end
           | RAISE (e, t) =>  let my I { regs, side, expression, fetch, allot } = lpsv e
                             in I { regs=int::max (1, regs), side=TRUE, fetch=fetch, allot=alloc,
                                  expression=RAISE (expression, t) }
                            end
           | EXCEPT (a, b) => let my I { regs=ra, side=sa, expression=ea, fetch=fa, allot=aa } = 
                                                                          loop a
                                my I { regs=rb, side=sb, expression=eb, fetch=fb, allot=ab } = 
                                                                          lpsv b
                             in I { regs=ra, side=sa or sb,
                                  fetch=fa or fb, allot = aa or ab,
                                  expression=EXCEPT (ea, eb) }
                            end
           | WRAP (t, c, e) => let my I { regs, side, expression, fetch, allot } = lpsv e
                           in I { regs=regs, side=side, fetch=TRUE, allot=alloc,
                                expression=WRAP (t, c, expression) }
                          end 
           | UNWRAP (t, c, e) => let my I { regs, side, expression, fetch, allot } = lpsv e
                             in I { regs=regs, side=side, fetch=TRUE, allot=alloc,
                                  expression=UNWRAP (t, c, expression) }
                            end 
           | _ => bug "unsupported lambda expression in loop" 

        */
        reorder_via_generalized_sethi_ullman = \\ x = bug "reorder not implemented"; #  getexp (loop x) 

    };                                                                          # package generalized_sethi_ullman_reordering
end;                                                                            # toplevel stipulate 



Comments and suggestions to: bugs@mythryl.org

PreviousUpNext