PreviousUpNext

15.4.350  src/lib/compiler/back/low/regor/cluster-regor-g.pkg

## cluster-regor-g.pkg

# Compiled by:
#     src/lib/compiler/back/low/lib/lowhalf.lib


# This module provides services for the
# new register allocator when using the
# cluster representation.  
# The algorithm is adapted from
# Algorithm 19.17 from Appel, Modern Compiler Implementation in ML,
# Calculation of live ranges in SSA form.  We don't directly use SSA 
# here but the principles are the same.
#
# -- Allen Leung


###           "Always listen to the experts.
###            They'll tell you what can't be
###            done and why.  Then do it."
###
###               -- Robert A Heinlein



stipulate
    package fil =  file__premicrothread;                                # file__premicrothread                          is from   src/lib/std/src/posix/file--premicrothread.pkg
    package geh =  graph_by_edge_hashtable;                             # graph_by_edge_hashtable                       is from   src/lib/std/src/graph-by-edge-hashtable.pkg
    package iht =  int_hashtable;                                       # int_hashtable                                 is from   src/lib/src/int-hashtable.pkg
    package irc =  iterated_register_coalescing;                        # iterated_register_coalescing                  is from   src/lib/compiler/back/low/regor/iterated-register-coalescing.pkg
    package lem =  lowhalf_error_message;                               # lowhalf_error_message                         is from   src/lib/compiler/back/low/control/lowhalf-error-message.pkg
    package lms =  list_mergesort;                                      # list_mergesort                                is from   src/lib/src/list-mergesort.pkg
    package odg =  oop_digraph;                                         # oop_digraph                                   is from   src/lib/graph/oop-digraph.pkg
    package rkj =  registerkinds_junk;                                  # registerkinds_junk                            is from   src/lib/compiler/back/low/code/registerkinds-junk.pkg
    package rwv =  rw_vector;                                           # rw_vector                                     is from   src/lib/std/src/rw-vector.pkg
    package uwv =  unsafe::rw_vector;                                   # unsafe                                        is from   src/lib/std/src/unsafe/unsafe.pkg
herein                                                                  # "okay, I'm cheating a bit here" -- Allen Leung.

    # This generic is invoked in:
    #
    #     src/lib/compiler/back/low/regor/regor-risc-g.pkg
    #     src/lib/compiler/back/low/intel32/regor/regor-intel32-g.pkg
    #
    generic package   cluster_regor_g   (
        #             ===============
        #
        package ae:  Machcode_Codebuffer_Pp;                            # Machcode_Codebuffer_Pp                        is from   src/lib/compiler/back/low/emit/machcode-codebuffer-pp.api

        package mcg: Machcode_Controlflow_Graph                         # Machcode_Controlflow_Graph                    is from   src/lib/compiler/back/low/mcg/machcode-controlflow-graph.api
                     where
                          mcf == ae::mcf                                # "mcf" == "machcode_form" (abstract machine code).
                     also pop == ae::cst::pop;                          # "pop" == "pseudo_op".

        package mu:  Machcode_Universals                                # Machcode_Universals                           is from   src/lib/compiler/back/low/code/machcode-universals.api
                     where
                         mcf == mcg::mcf;                               # "mcf" == "machcode_form" (abstract machine code).

        package spl: Register_Spilling                                  # Register_Spilling                             is from   src/lib/compiler/back/low/regor/register-spilling.api
                     where
                         mcf == mcg::mcf;                               # "mcf" == "machcode_form" (abstract machine code).
    )
    : (weak)  Regor_View_Of_Machcode_Controlflow_Graph                  # Regor_View_Of_Machcode_Controlflow_Graph      is from   src/lib/compiler/back/low/regor/regor-view-of-machcode-controlflow-graph.api
    {
        # Exported to client packages:
        #       
        package mcf =  mcg::mcf;                                        # "mcf" == "machcode_form" (abstract machine code).
        package rgk =  mcf::rgk;                                                # "rgk" == "registerkinds".
        package spl =  spl;                                             # "spl" == "spill".
        package cig =  codetemp_interference_graph;                     # codetemp_interference_graph                   is from   src/lib/compiler/back/low/regor/codetemp-interference-graph.pkg



        fun is_on (flag, mask)
            =
            unt::bitwise_and (flag, mask) != 0u0;

        print_interference_graph_size
            =
            lowhalf_control::make_bool (
                "print_interference_graph_size",
                "whether to show RA size"
            );

        Machcode_Controlflow_Graph                                      # Exported to client packages.
            =
            mcg::Machcode_Controlflow_Graph;                            # flowgraph is a cluster 

        fun error msg
            =
            lem::error("cluster_regor", msg);

        mode = 0u0;

        fun uniq_registers codetemps                                    # This has the effect of sorting list by color and dropping any duplicate colors.
            =
            rkj::sortuniq_colored_codetemps  codetemps;

        fun chase_register (c as rkj::CODETEMP_INFO { color=>REF (rkj::MACHINE r), ... } ) =>  (c, r);
            chase_register (     rkj::CODETEMP_INFO { color=>REF (rkj::ALIASED c), ... } ) =>  chase_register c;
            chase_register (c as rkj::CODETEMP_INFO { color=>REF  rkj::SPILLED,    ... } ) =>  (c,-1);
            chase_register (c as rkj::CODETEMP_INFO { color=>REF  rkj::CODETEMP, id, ... } ) =>  (c, id);
        end;

        fun color_of (rkj::CODETEMP_INFO { color=>REF (rkj::MACHINE r), ... } ) =>  r;
            color_of (rkj::CODETEMP_INFO { color=>REF (rkj::ALIASED c), ... } ) =>  color_of c;
            color_of (rkj::CODETEMP_INFO { color=>REF  rkj::SPILLED,    ... } ) =>  -1;
            color_of (rkj::CODETEMP_INFO { color=>REF  rkj::CODETEMP, id, ... } ) =>  id;
        end;

        fun chase (cig::NODE { color => REF (cig::ALIASED n), ... } )
                =>
                chase n;

            chase n
                =>
                n;
        end;

        exception NOT_THERE;

        fun dump_flowgraph (txt, mcg as odg::DIGRAPH graph, outstream)
            =
            {   fun say text
                    =
                    fil::write (outstream, text);

                fun say_pseudo  pseudo_op
                    =
                    {   say  (mcg::pop::pseudo_op_to_string  pseudo_op);
                        say  "\n";
                    };

                graph.graph_info ->   mcg::GRAPH_INFO { dataseg_pseudo_ops, ... };

                mcg::dump (outstream, txt, mcg);
                apply say_pseudo (reverse *dataseg_pseudo_ops);
            };

        get_global_graph_notes =  mcg::get_global_graph_notes; 

        dummy_block =   mcg::make_bblock { id => -1, execution_frequency => REF 0.0 };

        uniq =  lms::sort_list_and_drop_duplicates
                    #
                    (\\ ( { block=>b1, op=>i1 },{ block=>b2, op=>i2 } )
                        =
                        case (int::compare (b1, b2))
                            EQUAL =>  int::compare (i1, i2);
                            ord   =>  ord;
                        esac
                    );

        fun services (mcg as odg::DIGRAPH graph)
            =
            { build, 
              spill, 
              program_point =>  \\ { block, op } =  prog_pt (block, op),
              block_num, 
              instr_num
            }
            where
                graph.graph_info ->   mcg::GRAPH_INFO { notes => cl_notess, ... };

                blocks = graph.nodes ();

                fun max_block_id ((id, mcg::BBLOCK _) ! rest, curr)
                        => 
                        if (id > curr)   max_block_id (rest, id);
                        else             max_block_id (rest, curr);
                        fi;

                    max_block_id([], curr)
                        =>
                        curr;
                end;

                nnn =   max_block_id (blocks, graph.capacity ());


                # Construct program point:
                #
                fun prog_pt (block, op)
                  =
                  { block, op };

                fun block_num { block, op } =  block;
                fun instr_num { block, op } =  op;

                block_table             #  Blocks indexed by block id 
                    =
                    rwv::make_rw_vector (nnn, (graph.allot_node_id (), dummy_block));

                # Fill block table:
                #
                list::apply
                    (\\ b as (nid, _) =  rw_vector::set (block_table, nid, b))
                    blocks;

                exit
                    =
                    case (graph.exits ())

                         [e] =>  e;
                          _  =>  error "EXIT";
                    esac;


                # Building the interference graph:
                #       
                fun build_interference_graph
                    #
                    ( registerkind,  
                      graph' as cig::CODETEMP_INTERFERENCE_GRAPH
                                  {
                                    node_hashtable,
                                    is_globally_allocated_register_or_codetemp,                                 # Distinguishes registers allocated globally (e.g., esp and edi on intel32) from those allocated locally by the register allocator.
                                    mode,
                                    span,
                                    copy_tmps,
                                    ...
                                  }
                    )
                    =
                    {   # Definitions indexed by
                        # block id+op id: 

                        defs_table =  rwv::make_rw_vector (nnn, rwv::make_rw_vector (0, [] : List( cig::Node )));
                        marked     =  rwv::make_rw_vector (nnn, -1);
                        add_edge   =  irc::add_edge graph';

                        # Copies indexed by source.
                        # This table maps variable v
                        # to the program points where
                        # v is a source of a copy.
                        #
                        copy_table = iht::make_hashtable  { size_hint => nnn,  not_found_exception => NOT_THERE } 
                                   : iht::Hashtable(  List  { dst: rkj::Codetemp_Info, pt: cig::Program_Point } );

                        lookup_copy =  iht::find copy_table; 

                        lookup_copy
                            =
                            \\ r = case (lookup_copy r)
                                       THE c => c; 
                                       NULL  => [];
                                   esac;

                        add_copy      = iht::set copy_table;

                        stamp = REF 0;

                        # Allocate the arrays 
                        #
                        fun allot [] => ();

                            allot((id, mcg::BBLOCK { ops, ... } ) ! blocks)
                                => 
                                {   uwv::set (defs_table, id, rwv::make_rw_vector (length *ops+1, []));
                                    allot blocks;
                                };
                        end;

                        allot blocks;


                        # Remove pseudo use generated
                        # by copy temporaries:
                        #
                        fun rm_pseudo_uses []
                                =>
                                ();

                            rm_pseudo_uses (cig::NODE { uses, ... } ! ns)
                                =>
                                {   uses := [];
                                    rm_pseudo_uses ns;
                                };
                        end;


                        # Initialize the definitions before
                        # computing the liveness for v:
                        #
                        fun initialize (v, v', use_sites)
                            =
                            {
                                # First we remove all definitions for all copies 
                                # with v as source.
                                # When we have a copy and while we are processing v
                                #
                                #      x <- v
                                #
                                #  x does not really interfere with v at this point,
                                #  so we remove the definition of x temporarily.
                                #
                                fun mark_copies ([], trail)
                                        =>
                                        trail;

                                    mark_copies( { pt, dst } ! copies, trail)
                                        => 
                                        {   b     = block_num pt;
                                            i     = instr_num pt;
                                            defs  = uwv::get (defs_table, b);
                                            nodes = uwv::get (defs, i);

                                            fun reverse_and_prepend ([], nodes)
                                                    =>
                                                    nodes;

                                                reverse_and_prepend (n ! ns, nodes)
                                                    =>
                                                    reverse_and_prepend (ns, n ! nodes);
                                            end;

                                            dst_color = color_of dst;

                                            fun remove_dst ([], nodes')
                                                    =>
                                                    nodes';

                                                remove_dst((d as cig::NODE { id=>r, ... } ) ! nodes, nodes')
                                                    =>
                                                    if   (r == dst_color)

                                                         reverse_and_prepend (nodes', nodes);
                                                    else
                                                         remove_dst (nodes, d ! nodes');
                                                    fi;
                                            end;

                                            nodes' = remove_dst (nodes, []);
                                            uwv::set (defs, i, nodes');
                                            mark_copies (copies, (defs, i, nodes) ! trail);
                                        };
                                end;


                                # Then we mark all use sites of v
                                # with a fake definition so that
                                # the scanning will terminate
                                # correctly at these points.
                                #
                                fun mark_use_sites ([], trail)
                                        =>
                                        trail;

                                    mark_use_sites (pt ! pts, trail)
                                        => 
                                        {   b     = block_num pt;
                                            i     = instr_num pt;
                                            defs  = uwv::get (defs_table, b);
                                            nodes = uwv::get (defs, i);

                                            uwv::set (defs, i, v' ! nodes);
                                            mark_use_sites (pts, (defs, i, nodes) ! trail);
                                        };
                                end;

                                copies = lookup_copy v;
                                trail  = mark_copies (copies, []);
                                trail  = mark_use_sites (use_sites, trail);

                                trail;
                            };

                        fun cleanup []
                                =>
                                ();

                            cleanup ((defs, i, nodes) ! trail)
                                => 
                                {   uwv::set (defs, i, nodes);
                                    cleanup trail;
                                };
                        end; 

                        # Perform incremental liveness
                        # analysis on register v 
                        # and compute the span:
                        #
                        fun liveness (v, v' as cig::NODE { uses, ... }, register_v)
                            =
                            {   st = *stamp;
                                stamp := st + 1;

                                fun foreach_use_site ([], span)
                                        =>
                                        span;

                                    foreach_use_site (u ! uses, span)
                                        =>
                                        {   b = block_num u;
                                            i = instr_num u;

                                            (uwv::get (block_table, b))
                                                ->
                                                block as (_, mcg::BBLOCK { execution_frequency, ... } );

                                            span =  if (i == 0)
                                                        #
                                                        live_out_atablock (block, span);                        # Live out.
                                                    else 
                                                        f = *execution_frequency;

                                                        defs = uwv::get (defs_table, b);

                                                        live_out_at_statement (block, rwv::length defs, defs, i+1, f, span+f);
                                                    fi;

                                            foreach_use_site (uses, span);
                                        };
                                end 

                                also
                                fun visit_pred ((nid, _), span)
                                    =
                                    foreach_pred (graph.prior nid, span)
                                    where
                                        fun foreach_pred ([], span)
                                                =>
                                                span;

                                            foreach_pred (nid ! prior, span)
                                                =>
                                                {   span = live_out_atablock((nid, graph.node_info nid), span);
                                                    foreach_pred (prior, span); 
                                                };
                                        end;
                                    end

                                also
                                fun live_out_at_statement (block, n_defs, defs, pos, freq, span)
                                    = 
                                    # v is live out
                                    #
                                    if (pos >= n_defs)

                                         visit_pred (block, span);
                                    else

                                        foreach_def (uwv::get (defs, pos), FALSE)
                                        where 
                                            fun foreach_def ([], TRUE)
                                                    =>
                                                    span;

                                                foreach_def([], FALSE)
                                                    => 
                                                    live_out_at_statement (block, n_defs, defs, 
                                                                 pos+1, freq, span+freq);

                                                foreach_def((d as cig::NODE { id=>r, ... } ) ! ds, kill)
                                                    => 
                                                    if (r == v)
                                                         foreach_def (ds, TRUE);
                                                    else
                                                         add_edge (d, v');
                                                         foreach_def (ds, kill);
                                                    fi;
                                            end; 
                                        end; 
                                    fi

                                also
                                fun live_out_atablock (block as (nid, mcg::BBLOCK { execution_frequency, ... } ), span)
                                    = 
                                    # v is live out at the current block 
                                    #
                                    if (uwv::get (marked, nid) == st)
                                        #
                                        span;
                                    else 
                                        defs = uwv::get (defs_table, nid);

                                        uwv::set (marked, nid, st);
                                        live_out_at_statement (block, rwv::length defs, defs, 1, *execution_frequency, span);
                                    fi;

                                use_sites = uniq *uses; 
                                trail    = initialize (v, v', use_sites);
                                span     = foreach_use_site (use_sites, 0.0);
                                cleanup trail;

                                span;
                            };                  # fun build_interference_graph

                        new_nodes    =  irc::new_nodes  graph';

                        getnode      =  iht::get  node_hashtable;

                        op_def_use =  mu::def_use registerkind;

                        get_codetemp_infos_of_our_kind     =  rgk::get_codetemp_infos_for_kind  registerkind;



                        # Remove all globally allocated and
                        # spilled registers from the list 
                        #
                        fun drop_globally_allocated_and_spilled_registers regs
                            =
                            loop (regs, [])
                            where
                                fun loop ([], rs')
                                        =>
                                        rs';

                                    loop (r ! rs, rs')
                                        => 
                                        {   fun rmv (r as rkj::CODETEMP_INFO { color=>REF rkj::CODETEMP, id, ... } )
                                                    => 
                                                    if (is_globally_allocated_register_or_codetemp id)   loop (rs,     rs');
                                                    else                                     loop (rs, r ! rs');
                                                    fi;

                                                rmv (rkj::CODETEMP_INFO { color=>REF (rkj::ALIASED r), ... } )
                                                    =>
                                                    rmv r;

                                                rmv (r as rkj::CODETEMP_INFO { color=>REF (rkj::MACHINE col), ... } )
                                                    => 
                                                    if (is_globally_allocated_register_or_codetemp col)    loop (rs,     rs');
                                                    else                                       loop (rs, r ! rs');
                                                    fi;

                                                rmv (rkj::CODETEMP_INFO { color=>REF rkj::SPILLED, ... } )
                                                    =>
                                                    loop (rs, rs');
                                            end;

                                            rmv r; 
                                        };
                                end;
                            end;


                        # Create parallel move:
                        #
                        fun make_moves (op, pt, cost, mv, tmps)
                            =
                            case op
                                #
                                mcf::NOTE { op, ... }
                                    => 
                                    # Strip away the annotation.
                                    # Note: we are assuming annotations cannot change 
                                    # the semantics of the copies.
                                    #   
                                    make_moves (op, pt, cost, mv, tmps);

                                mcf::COPY { dst, src, kind, ... }
                                    =>
                                    # If it is a parallel copy, deal
                                    # with the copy temporary properly.
                                    #   
                                    # If it is a register, create a
                                    # pseudo use site just below the
                                    # end of  the copy op.
                                    # This is to make sure that the
                                    # temporary is colored properly.
                                    #   
                                    # If the copy temporary doesn't
                                    # exist or if it has been spilled,
                                    # do nothing.
                                    #   
                                    if (kind == registerkind)
                                        #
                                        tmps = 
                                            case (mu::move_tmp_r op)
                                                #
                                                THE r
                                                    => 
                                                    # Add a pseudo use for tmpR 
                                                    case (chase (getnode (color_of r)))
                                                        #
                                                        tmp as cig::NODE { uses, defs=>REF [d], ... }
                                                            =>
                                                            {   fun prev { block, op } = { block, op => op - 1 };
                                                                uses := [prev d]; 
                                                                tmp ! tmps;
                                                            };

                                                        _ => error "make_moves";
                                                    esac;


                                                NULL => tmps;
                                            esac;


                                        fun moves ([], [], mv)
                                                =>
                                                mv;

                                            moves (d ! ds, s ! ss, mv)
                                                =>
                                                {   (chase_register d) ->   (d, cd);
                                                    (chase_register s) ->   (s, cs);

                                                    if (  is_globally_allocated_register_or_codetemp cd
                                                       or is_globally_allocated_register_or_codetemp cs
                                                       )

                                                        moves (ds, ss, mv);
                                                        #
                                                    elif (cd == cs)
                                                        #
                                                        moves (ds, ss, mv);
                                                    else 
                                                        #
                                                        add_copy (cs, { dst=>d, pt } ! lookup_copy cs);

                                                        dst_reg = chase (getnode cd); 
                                                        src_reg = chase (getnode cs); 

                                                        moves (ds, ss, cig::MOVE_INT { dst_reg,
                                                                                       src_reg,
                                                                                       status  => REF cig::WORKLIST,
                                                                                       hicount => REF 0,
                                                                                       cost
                                                                                     } ! mv
                                                        ); 
                                                    fi;
                                                };
                                            moves _ => error "moves";
                                        end;

                                        (moves (dst, src, mv), tmps);
                                    else
                                        (mv, tmps);
                                    fi;

                                _ => (mv, tmps);
                            esac;


                        # Add the nodes first:
                        #
                        fun make_nodes ([], mv, tmps)
                                =>
                                (mv, tmps);

                            make_nodes((nid, blk) ! blocks, mv, tmps)
                                =>
                                {   blk ->    mcg::BBLOCK { ops, notes, execution_frequency => REF w, ... };

                                    next     =  graph.next nid;
                                    live_out =  mcg::liveout_note_of_bblock blk;
                                    dtab     =  rwv::get (defs_table, nid);

                                    fun scan ([], pt, i, mv, tmps)
                                            =>
                                            (pt, i, mv, tmps);

                                        scan (op ! rest, pt, i, mv, tmps)
                                            =>
                                            {   (op_def_use  op)
                                                    ->
                                                    (d, u);


                                                defs = drop_globally_allocated_and_spilled_registers d;
                                                uses = drop_globally_allocated_and_spilled_registers u;

                                                defs = new_nodes { cost=>w, pt, defs, uses };

                                                uwv::set (dtab, i, defs);

                                                my (mv, tmps)
                                                    =
                                                    make_moves (op, pt, w, mv, tmps);

                                                fun next { block, op }
                                                    =
                                                    { block, op => op + 1 };

                                                scan (rest, next pt, i+1, mv, tmps);  
                                            };
                                    end;

                                    my (pt, i, mv, tmps)
                                        = 
                                        scan (*ops, prog_pt (nid, 1), 1, mv, tmps);

                                    # If the block is escaping
                                    # then all liveout registers
                                    # are considered used here.
                                    #
                                    case next 
                                        #                                 
                                        [id] => 
                                            if (id == exit)
                                                #
                                                live_set =  drop_globally_allocated_and_spilled_registers
                                                                #
                                                                (uniq_registers (get_codetemp_infos_of_our_kind  live_out));

                                                new_nodes { cost=>w, pt=>prog_pt (nid, 0), defs => [], uses => live_set };
                                                ();
                                            fi;

                                        _ => ();
                                    esac;

                                    make_nodes (blocks, mv, tmps);
                                };
                        end;

                        # Add the edges 
                        #
                        my (moves, tmps)
                            =
                            make_nodes (blocks, [], []);

                        iht::keyed_apply
                            (   \\ (v, v' as cig::NODE { register, color, ... } )
                                    =
                                    {   fun compute_liveness ()
                                            = 
                                            set_span (v, liveness (v, v', register));

                                        case *color
                                            #
                                            cig::CODETEMP    =>  compute_liveness ();
                                            cig::COLORED _ =>  compute_liveness ();
                                            cig::RAMREG _  =>  compute_liveness ();
                                            _              =>  ();
                                        esac;
                                    }
                                    where
                                        my set_span:  ((Int, Float)) -> Void
                                            =
                                            if (is_on (mode, irc::compute_span))
                                                #
                                                span_map = iht::make_hashtable  { size_hint => iht::vals_count  node_hashtable,  not_found_exception => NOT_THERE };
                                                #
                                                set_span = iht::set  span_map;
                                                #
                                                span := THE span_map;
                                                #
                                                set_span;
                                            else
                                                \\ _ = ();
                                            fi;
                                    end
                            )
                            node_hashtable;

                        if (is_on (irc::save_copy_temps, mode))
                            #    
                            copy_tmps := tmps;
                        fi;

                        rm_pseudo_uses  tmps;

                        moves;
                    };                  # fun build_interference_graph


                # Build the interference graph initially:
                #
                fun build (cig, registerkind)
                    =
                    moves
                    where
                        moves = build_interference_graph (registerkind, cig);

                        i2s = int::to_string;

                        if *print_interference_graph_size
                            #
                            cig ->   cig::CODETEMP_INTERFERENCE_GRAPH { node_hashtable, edge_hashtable, ... };

                            ops =   fold_backward
                                        (\\ ((_, mcg::BBLOCK { ops, ... } ), n)
                                            =
                                            length *ops + n
                                        )
                                        0
                                        blocks;

                            fil::write
                               ( *lowhalf_control::debug_stream,
                                 #
                                 "RA #blocks=" + i2s nnn
                                 +      " #ops  ="  + i2s ops
                                 +      " #nodes="  + i2s (iht::vals_count  node_hashtable)
                                 +      " #edges="  + i2s (geh::get_edge_count *edge_hashtable)
                                 +      " #moves="  + i2s (length moves) + "\n"
                               );
                        fi;
                    end;


                # Rebuild the interference graph;
                # We'll just do it from scratch for now.
                #
                fun rebuild (registerkind, cig)
                    = 
                    {   irc::clear_nodes cig;
                        build_interference_graph (registerkind, cig);
                    };


                # Spill a set of nodes and rewrite the flowgraph 
                #
                fun spill
                      { copy_instr,
                        spill,
                        spill_src,
                        spill_copy_tmp, 
                        reload,
                        reload_dst,
                        rename_src,
                        graph,
                        registerkind,
                        nodes => nodes_to_spill
                      }
                    = 
                    {
                        irc::clear_graph graph;                                                         # Remove the interference graph now.

                        spill_set  = cig::ppt_hashtable::make_hashtable  { size_hint => 32,  not_found_exception => NOT_THERE };                        # Map program point to registers to be spilled.
                        reload_set = cig::ppt_hashtable::make_hashtable  { size_hint => 32,  not_found_exception => NOT_THERE };                        # Map program point to registers to be reloaded.
                        kill_set   = cig::ppt_hashtable::make_hashtable  { size_hint => 32,  not_found_exception => NOT_THERE };                        # Map program point to registers to be killed.

                        spill_rewrite
                            =
                            spl::spill_rewrite {
                              graph,
                              spill,
                              spill_src,
                              spill_copy_tmp,
                              reload,
                              reload_dst,
                              rename_src,
                              copy_instr,
                              registerkind,
                              spill_set,
                              reload_set,
                              kill_set
                            };

                        affected_blocks         # Set of basic blocks that are affected.
                            =
                            iht::make_hashtable  { size_hint => 32,  not_found_exception => NOT_THERE };

                        add_affected_blocks
                            =
                            iht::set affected_blocks;

                        fun ins set
                            =
                            enter
                            where
                                add =  cig::ppt_hashtable::set  set;

                                get =  cig::ppt_hashtable::find set;
                                get =  \\ r =  case (get r)
                                                   THE s => s;
                                                   NULL  => [];
                                               esac;

                                fun enter (r, [])
                                        =>
                                        ();

                                    enter (r, pt ! pts)
                                        => 
                                        {   add (pt, r ! get pt);
                                            add_affected_blocks (block_num pt, TRUE);
                                            enter (r, pts);
                                        };
                                end;
                            end;

                        ins_spill_set  = ins spill_set;
                        ins_reload_set = ins reload_set;

                        ins_kill_set
                            = 
                            enter
                            where
                                add =  cig::ppt_hashtable::set  kill_set;

                                get =  cig::ppt_hashtable::find kill_set;
                                get =  \\ r =  case (get r)
                                                   THE s => s;
                                                   NULL => [];
                                               esac;

                                fun enter (r, [])
                                        =>
                                        ();

                                    enter (r, pt ! pts)
                                        =>
                                        {   add (pt, r ! get pt);
                                            enter (r, pts);
                                        };
                                end;
                            end;


                        # Mark all spill/reload locations 
                        #
                        fun mark_spills (cig::NODE { color, register, defs, uses, ... } )
                            =
                            {   fun spill_it (defs, uses)
                                    = 
                                    {   ins_spill_set (register, defs);
                                        ins_reload_set (register, uses);

                                        # Definitions but no use!

                                        case uses
                                            [] =>  ins_kill_set (register, defs);
                                            _  =>  ();
                                        esac;
                                    };

                                d = *defs;
                                u = *uses;

                                case *color
                                    #
                                    cig::SPILLED     =>  spill_it (d, u);
                                    cig::SPILL_LOC _ =>  spill_it (d, u);
                                    cig::RAMREG _    =>  spill_it (d, u);
                                    cig::CODETEMP      =>  spill_it (d, u);
                                    _ => ();
                                esac;
                            };

                        apply  mark_spills  nodes_to_spill;


                        # Rewrite all affected blocks:
                        #
                        fun rewrite_all (blknum, _)
                            =
                            {   (rwv::get (block_table, blknum))
                                    ->
                                    (_, mcg::BBLOCK { ops as REF ops', notes, ... } );

                                ops'=  spill_rewrite { pt=>prog_pt (blknum, length ops'), ops => ops', notes };

                                ops := ops';
                            };


                        fun mark (cig::NODE { color, ... } )
                            = 
                            case *color
                                #
                                cig::CODETEMP      => color := cig::SPILLED;
                                cig::SPILLED     => ();
                                cig::SPILL_LOC _ => ();
                                cig::ALIASED _   => ();
                                cig::RAMREG _    => ();
                                cig::COLORED _   => error "mark: COLORED";
                                cig::REMOVED     => error "mark: REMOVED";
                            esac;


                        iht::keyed_apply
                            rewrite_all
                            affected_blocks;

                        apply  mark  nodes_to_spill;

                        rebuild (registerkind, graph);
                    };                          # fun spill 
            end;                                # fun services
    };
end;


## COPYRIGHT (c) 2002 Bell Labs, Lucent Technologies
## Subsequent changes by Jeff Prothero Copyright (c) 2010-2015,
## released per terms of SMLNJ-COPYRIGHT.


Comments and suggestions to: bugs@mythryl.org

PreviousUpNext