"""

Local Register Allocator

This class provides a useful base class for register allocators.
While the register allocator returned by the machine definition is not
required to use this class, it should be generally applicable.

By itself, the class does a simple first come, first serve register
allocation on a per-basic-block basis; however, it is intended to be
augmented with a global register allocation strategy.

See the class docstring for details, but in short a helper class can
suggest registers to the LocalAllocator which will receive priority.
Additional temporaries will be assigned to registers if available.
Depending on the architecture, spills and/or soft registers may be
introduced as needed.

"""

import lowir, util
from lowir import regbits, argflags, MemArgument, AddrArgument, SoftRegArgument
from lowir import Operation, tartypes
import sys

INT_MAX = 0x7FFFFFFF

# ----------------------------------------------------------------------

def allocate_registers (cfg, finfo, comp):

    """ This function takes the control flow graph 'cfg', assigns
    registers and things as needed, and generates the bytes
    corresponding to it in 'finfo.buffer'.

    Every register allocation module includes an "allocate_registers"
    routine that takes the same set of arguments.  This allows them to
    be relatively interchangable. """

    alloc = LocalAllocator (cfg, finfo, finfo.md, comp)
    alloc.go ()
    return

# ----------------------------------------------------------------------

class RegisterNominator:
    
    """ This class defines the interface that LocalAllocator (below)
    uses to query the global allocator and find out which temporaries
    it nominated for various registers etc.  A short description of
    the methods you can override is included here, for more details
    see the method itself.

    get_block_region (self, blk, index) --- returns the set of
    temporaries that should get registers for each sub-region of a
    basic block.  Default is to nominate no registers and create no
    sub regions for any block.

    is_live_on_exit (self, blk, temp) --- should return true if 'temp'
    is love on exit from block 'blk'.  Currently returns true if
    'temp' is global (i.e., used in more than one basic block). """

    def get_block_region (self, blk, index):

        """ The same set of temporaries need not live in registers for
        the entirety of a basic block; instead, the blocks are broken
        into regions for which the register assignments are the same.
        get_block_region() allows us to query the subclass as to which
        which temporaries should be in registers for block 'blk', and
        region 'index'.  It should return a tuple (regset,
        firstoperation).  'regset' should be a list or other iterable
        object of temporaries to put in registers, and
        'firstoperation' is the first Operation of this region in the
        block.  The last Operation is assumed to be the Operation that
        preceded the first Operation for the following region.

        The regions are numbered from 0 counting backwards from the end of
        the block; so, the block always ends in region 0 but may begin in
        a higher numbered region.

        We will always ask for regions in order, but we may request the
        same region more than once before moving on to the next one.

        The basic version just returns ((), None), meaning that it
        treats every basic block as one large region and nominates no
        registers.  The result is that this will perform a simple
        first come first serve local allocation strategy. """

        return ((), None)

    def is_live_on_exit (self, blk, temp):

        """ Returns true if 'temp' is live on exit from block 'blk';
        currently just checks if the temp is local: if not, then we
        consider it live.  This is because this is the extent of
        liveness info the base class knows. """

        if temp.is_local:
            assert temp.lastblock is blk
            return False
        return True

    pass

# ----------------------------------------------------------------------

class LocalAllocator:

    """

    A useful helper class for register allocators in pynto; given a
    set of nominations for which temporaries should go in registers in
    regions of each block, this class will handle everything else:

    - Selecting which physical register to use
    
    - Spilling as needed to accomodate limitations of the architecture etc
    
    - Generating the bytes from each instruction
    
    - Linking the blocks together
    
    - Generating transitions between blocks when register allocations
    change

    To use it by itself, you just create an instance and call go().

    To use it as part of another allocator, you create an instance of
    it and call set_nominator() with an object which descends from
    class RegisterNominator, specified above.  You then call go().

    """

    def __init__ (self, cfg, finfo, md, comp):

        """ Constructor

        cfg --- the lowir.BasicBlock object at the entrance to the function

        finfo --- the function info object for this function

        md --- the machine description

        comp --- the compiler object for this compilation

        """
        
        self.cfg = cfg
        self.finfo = finfo
        self.buffer = finfo.buffer
        self.md = md
        self.comp = comp
        self.log = comp.log

        # The master sets of registers.  tempregs can be used for anything,
        # whereas softregs are reserved for SoftRegisters.
        self.tempregs = finfo.get_available_registers ()
        self.log.high ('ra_local: %d temp registers', len (self.tempregs))

        # Tracks slots we used to temporary stack positions.  When one
        # becomes available, we push it here, and pop when we need
        # one.  These never outlast a block, so we don't need to worry
        # about detecting conflicts or anything.
        self.availsoftstacks = []

        # Track slots used for temps that are not live outside of this
        # block so they can be reused in other blocks.
        self.localtempslots = []

        # Fields initialized in select_registers_on_exit ():
        self.availtempregs = None  # general unused Registers
        self.curreglist = None     # list of temporaries in registers now
        self.spilledtemps = None   # list of temporaries spilled from registers

        # Use a default nominator initially
        self.nominator = RegisterNominator ()
        return

    # ----------------------------------------------------------------------
    # External Interface

    def go (self):

        """ The only public method on this object.  Walks the IR, assigning
        registers and emitting the bytes into the finfo's buffer. """
        
        self.emit_blocks ()
        pass

    def set_nominator (self, nom):
        self.nominator = nom
        pass

    # ----------------------------------------------------------------------
    # Convenience methods to invoke the nominator

    def get_block_region (self, blk, index):
        return self.nominator.get_block_region (blk, index)

    def is_live_on_exit (self, blk, temp):
        return self.nominator.is_live_on_exit (blk, temp)

    # ----------------------------------------------------------------------
    # The heart of the object: emit_* and friends

    def emit_blocks (self):

        """ This is where the code provided by regalloc really lies.
        It processes the basic blocks in post order.  For each block
        along that walk, it generates the bytes corresponding to the
        Operations contained within.  Note that the bytes are generated
        from the end of the buffer and filled in as we go.

        That is not so simple.  It involves:

        - Manages the linking of basic blocks together.
        
        - - Because different blocks may not have the same register
            assignments, this means generating Operations to manage
            transitions between blocks: at this point, temporaries may
            be spilled to the stack, loaded into registers, etc.

        - It invokes the code generation routines.

        - Selects the registers on entry and exit and ensures that all
        arguments to Operations have an appropriate assignment. """

        vtag = self.comp.get_visit_tag()
        self.emit_walk (vtag, self.cfg)

        pass

    def emit_walk (self, vtag, blk):

        """ Helper for code_emission(): performs a DFS on the tree,
        calling emit_block() in post order """

        # First walk to all children
        blk.visit (vtag)
        for sblk in blk.successors():
            if not sblk.visited (vtag): self.emit_walk (vtag, sblk)
            pass

        # Now process ourselves
        self.emit_block (blk)
        
        return

    def emit_block (self, blk):

        """ Helper for code_emission(): called in post order from
        emit_walk() for every block in the CFG.  Does the work of
        generating entrace and exit Operations as needed, performing
        final register allocation, and emitting the bytes for each
        Operation in the block. """

        emblkindent = self.log.indent ('emit_block blk=%s', blk)

        #
        # Compute the set of temporaries that will be in registers on
        # exit.  We must know this to generate the transition code below.
        #
        
        self.select_registers_on_exit (blk)

        #
        # First we must generate any transition code for when this block
        # exits to its successors.  There can only be two successors:
        # the branch target and the fallthrough.
        #
        # Note that the routine emit_transition() stores the size of the
        # RJBuffer at the beginning of the transition code in the block
        # under the field 'transition_point'; we store -1 if the block
        # could not be processed.  This is used in emit_oper() to resolve
        # branches and the like.
        #
        # Note that in the fallthrough case we must generate a 'BR' instruction
        # if the successor has not been generated; otherwise, though,
        # one will be generated at the end of the transition code if it is
        # needed.
        #
        
        if blk.opers:
            lastoper = blk.opers[-1]
            if lastoper.tartype == tartypes.OPER:
                self.emit_transition (blk, lastoper.target.block)
                pass
            pass
        
        if blk.fallthrough:
            if not self.emit_transition (blk, blk.fallthrough.block):
                # If the block we fall through to has not yet been
                # generated, we must emit a BR instruction.  This will
                # insert a pending pred in the fall through block
                # since we won't be able to resolve the target.
                self.emit_br (blk, blk.fallthrough.block)
                pass
            pass

        #
        # Now we must generate the code for each Operation in the block.
        #

        if blk.opers:
            self.get_block_region (blk, 0)
            nextregion = 0
            regionstart = blk.opers[-1]
            self.log.high ('len(blk.opers)=%d', len (blk.opers))
            for op in blk.opers.reverse_iter ():

                self.log.high ('oper=%s (prev=%s)', op, op.op_prev)
                
                # Ensure that the arguments are assigned to specific locations,
                # and emit the operation.
                self.select_assignments_and_emit_oper (blk, op)

                if op is regionstart:

                    newregionindent = self.log.indent ('starting region %d',
                                                       nextregion+1)
                    
                    regset, regionstart = \
                            self.get_block_region (blk, nextregion)
                    nextregion += 1

                    self.log.low ('len(regset)=%d regionstart=%s',
                                  len (regset), regionstart)

                    self.spilledtemps = []

                    # Mark those temporaries in this region
                    rvtag = self.comp.get_visit_tag()
                    for temp in regset: temp.visit (rvtag)

                    # Spill those items in the current register set but not
                    # the new one.  Note that this "spill" is in fact a load
                    # because the old register set came after us in the
                    # program.
                    newreglist = []
                    for temp in self.curreglist:
                        if not temp.visited (rvtag):
                            memslot = self.temp_stack_slot (temp)
                            self.emit_move (
                                temp.assignment, memslot,
                                "load item in new region's register set")
                            self.availtempregs.append (temp.assignment)
                            temp.set_assignment (blk, memslot)
                            self.log.mid (
                                'Temp %s spilled to %s b/c not in new region',
                                temp, memslot)
                        else:
                            newreglist.append (temp)
                            pass
                        pass
                    self.curreglist = newreglist                    

                    for temp in regset:

                        self.log.low (
                            '- Considering whether to promote temp %s', temp)
                        
                        # If this temp is already in a register, skip it
                        if (temp.was_assigned_by (blk) and 
                            temp.assignment.is_type (argflags.REG)):
                            self.log.low ('already in register')
                            continue

                        # Otherwise, if we can get a register, give it
                        # to 'em.  
                        if self.availtempregs:
                            self.promote_temp_to_reg (temp)
                        else:

                            # put on the list of spilled temporaries;
                            # when a register becomes available, wel'l
                            # give it to this guy
                            
                            self.log.low ('  insufficient registers')
                            self.spilledtemps.append (temp)
                            pass
                        pass
                    
                    self.log.undent (newregionindent)
                    pass
                
                self.log.mid ('prev oper=%s', op.op_prev)
                pass
            pass

        #
        # Compute the set of registers on entry, and record the size of
        # the buffer at the beginning of our first instruction.  Our preds
        # will need this to compute transitions.
        #
        
        blk.oper_point = self.finfo.buffer.size ()
        self.select_registers_on_entry (blk)

        #
        # Toss any stack slots used by temporaries local to this block
        # into the available soft stack pool for other basic blocks to use.
        #
        
        for slot in self.localtempslots: self.availsoftstacks.append (slot)
        self.localtempslots = []

        #
        # Finally, we have to generate transitions for any pending preds.
        # These are blocks that were generated before us but which
        # branch to us.  In this case, we generate the successor transition
        # code, which will end with a branch to us.  We then patch the
        # branch that was generated at the end of the pred to point at the
        # beginning of the transition code.
        #
        
        for pblk, ploc in blk.pending_preds:
            self.emit_transition (pblk, blk)  # generate transition code
            ploc.patch (0)                    # patch the branch that pred made
            pass

        self.log.undent (emblkindent)

        pass

    def select_registers_on_exit (self, blk):

        """ Determines the set of temporaries that are in registers on
        exit from a block, and sets blk.registers_on_exit to a
        dictionary mapping temporaries to the registers they are
        assigned to.  Attempts to reuse the assignments of successors
        to minimize transition code. """

        indent = self.log.indent ("select_registers_on_exit: %s", blk)

        # We will be determining the current register list now, so
        # initialize it to empty:
        self.curreglist = []
        
        # Determine a set of temporary->register mappings that our
        # successors have.  If there are conflicts, we arbitrarily
        # resolve in favor of successors that come earlier in the
        # list.
        temptoreg = {}
        regtotemp = {}
        indent2 = self.log.indent ('Obtaining registers from successors')
        for succ in blk.successors():
            # If we have not yet visited this block:
            if succ.registers_on_entry is None: continue
            for temp, reg in succ.registers_on_entry.items ():
                # If this reg is claimed or temp assigned, skip
                if reg in regtotemp: continue
                if temp in temptoreg: continue
                regtotemp[reg] = None     # initially store None as placeholder
                temptoreg[temp] = reg
                self.log.mid ('reg=%s temp=%s succ=%s', reg, temp, succ)
                pass
            pass
        self.log.undent (indent2)

        # Now load up the first region for this block and iterate over
        # the items we want in registers, using the register from our
        # successors if any.
        indent2 = self.log.indent ('Intersecting with first region')
        regset, self.regionstart = self.get_block_region (blk, 0)
        unassigned = False
        for temp in regset:
            try:
                reg = temptoreg[temp]
                regtotemp[reg] = temp          # store temp if we use it
                temp.set_assignment(blk, reg)
                self.curreglist.append (temp)
                self.log.mid ('reg = %s temp = %s', reg, temp)
            except KeyError: unassigned = True # remember if failed to assign
            pass
        self.log.undent (indent2)

        # Construct list of available registers.  Make all temps
        # available other than those we assigned in the previous loop.
        self.availtempregs = [ x for x in self.tempregs
                               if not regtotemp.get (x, None) ]
        self.spilledtemps = []
        self.log.mid ('that leaves %d registers remaining out of %d total',
                      len (self.availtempregs), len (self.tempregs))

        # Now assign those items we failed to assign in the above loop
        if unassigned:
            indent2 = self.log.indent (
                'Assigning to desirables from 1st region')
            for temp in regset:
                if self.availtempregs:
                    reg = self.availtempregs.pop()
                    temp.set_assignment (blk, reg)
                    regtotemp[reg] = temp         # remember assignment
                    self.curreglist.append (temp)
                    self.log.mid ('reg = %s temp = %s', reg, temp)
                else:
                    self.spilledtemps.append (temp)
                pass
            self.log.undent (indent2)
            pass

        # At this point, we could use the contents of regtotemp to
        # construct our list of register assignments on exit, except
        # for one annoying catch: if the last instruction in the block
        # is a branching instruction, such as BZ, it is possible under
        # extenuating circumstances that it would take an argument
        # which will require a spill register, forcing a spill to take
        # place.  Normally that puts a LOAD after the instruction to
        # load the item back into a register, but that won't work if
        # the last instruction branches out of the basic block.
        #
        # The proper way to deal with this it examine the instruction to
        # determine if anything will spill etc.  I am not doing this because
        # I am lame.  Instead I am ensure that we have at least a few
        # registers free to workaround it.  The "right" solution would just
        # do a better job of knowing how many registers to ensure are free.
        #
        # There is a similar problem that occurs if the last instruction
        # writes arguments and branches; again not a problem at the moment.

        # Per the reasoning above, spill a few temporaries if we must
        while len (self.availtempregs) < 2:
            temp = self.curreglist.pop ()
            reg = temp.assignment
            regtoremp[reg] = None
            temp.set_assignment (None, None)
            self.spilledtemps.append (temp)
            self.log.mid ('Spilled %s from %s to ensure a few avail regs',
                          temp, reg)
            pass

        # The set of registers we have on exit has been tracked
        # in regtotemp as we went along; note that we sometimes wrote
        # value of None in for the temporary to clear an entry, so
        # filter those out.  Also use this to build the list of current
        # register entries.
        regsonexit = {}
        self.curreglist = []
        for reg, temp in regtotemp.items():
            if not temp: continue
            regsonexit[temp] = reg # invert the hashtable
            pass

        # Store the hashtable of registers on exit
        blk.set_registers_on_exit (regsonexit)

        del indent

        return

    def select_registers_on_entry (self, blk):

        """ Computes the entry to the block.  This is pretty easy:
        just look at self.curreglist """
        
        regsonentry = {}
        for temp in self.curreglist:
            regsonentry[temp] = temp.assignment
            pass
        blk.set_registers_on_entry (regsonentry)
        return 

    def select_assignments_and_emit_oper (self, blk, oper):

        """ Ensures that each argument to the Operation has an
        appropriate assignments; this may generate spills and things
        like that! """

        saeoindent = self.log.indent (
            'select_assignments_and_emit_oper blk=%s oper=%s', blk, oper)

        # Useful subroutines to do the various kinds of work we
        # have to do:
        def assign_soft_reg (arg):
            # If we find a SOFTREG that is not currently
            # assigned, it *must* go in a register.

            # Already has a home?
            if arg.was_assigned_by (blk): return

            # Load a register if one is available, otherwise spill a temp
            if self.availtempregs:
                regloc = self.availtempregs.pop ()
            else:
                # If nothing is available, spill
                spilltemp = self.curreglist.pop ()
                regloc = spilltemp.assignment
                memslot = self.temp_stack_slot (spilltemp)
                self.emit_move (
                    regloc, memslot,
                    "spill to make room for new soft register")
                spilltemp.set_assignment (blk, memslot)
                self.log.mid ('forced to spill %s to %s [obtained reg %s]',
                              spilltemp, memslot, regloc)
                pass

            # No matter where we got the register from, it
            # is now associated with this SOFTREG.
            arg.set_assignment (blk, regloc)
            
            self.log.mid ('assigned soft reg %s to register %s',
                          arg, arg.assignment)
            return 
        
        def assign_temporary (arg):
            # If we find a TEMP, and it has no home, then it needs
            # to go to its home on the stack.
            if arg.was_assigned_by (blk): return

            # If we have extra temporary registers, well, might as
            # well use them; otherwise, find its home on the stack and
            # use that.
            if self.availtempregs:
                self.promote_temp_to_reg (blk, arg)
            else:
                stackslot = self.temp_stack_slot (arg)
                arg.set_assignment (blk, stackslot)
                self.log.mid ('assigned temp %s to stack slot %s',
                              arg, arg.assignment)
                pass
            return 
        
        def assign_soft_stack (arg):
            if arg.was_assigned_by (blk): return
            stackslot = self.soft_stack_slot ()
            arg.set_assignment (blk, stackslot)
            self.log.mid ('assigned soft stack %s to slot %s',
                          arg, arg.assignment)
            return 
        
        def assign_arg (arg):
            
            """ Purges the argument in a deep fashion so that it
            contains no references to a SOFTREG, TEMP, or
            SOFTSTACK.  Return value is irrelevant. """


            if arg.is_type (argflags.SOFTREG):
                return assign_soft_reg (arg)
            elif arg.is_type (argflags.TEMP):
                return assign_temporary (arg)
            elif arg.is_type (argflags.SOFTSTACK):
                return assign_stack_slot (arg)
            elif arg.is_type (argflags.ADDR):
                return assign_arg (arg.base)
            elif arg.is_type (argflags.MEM):
                # Note that mem arguments must be relative to registers
                base = arg.base
                assign_arg (base)
                if not base.assignment.is_type (argflags.REG):
                    newarg = SoftRegArgument ()
                    arg.base = newarg
                    srcmoves.append (Operation ('MOVE',
                                                dests=(newarg,),
                                                sources=(base,)))
                    assign_arg (newarg)
                    pass
                pass
            return
        
        srcmoves = []
        while 1: # (the break is in the middle of the loop)
            
            # Ensure that all Operands that need one have an appropriate
            # assignment.  This may cause temporaries to be spilled.
            self.log.mid ('* Assigning all sources...')
            for arg in oper.sources: assign_arg (arg)
            self.log.mid ('* Assigning all destinations...')
            for arg in oper.dests: assign_arg (arg)
            
            # Check to ensure that the Operation's arguments are
            # all valid.  If so, we're done.
            cgfunc, dstwraps, srcwraps = \
                    self.md.emit_table.apply_to_oper (oper)
            oper.set_code_generation_func (cgfunc)
            if not dstwraps and not srcwraps: break
            
            # Otherwise, new SoftRegisters or SoftStacks were
            # created so we have to process those.  This may cause
            # temporaries to be spilled etc, in which case we will
            # need to REprocess the arguments to the operation,
            # and keep doing this until everyone is happy and has
            # an assignment.
            
            for argidx, wrapclass in dstwraps:
                oldarg = oper.dests[argidx]
                newarg = wrapclass ()
                assign_arg (newarg)
                oper.dests[argidx] = newarg
                self.log.low (
                    'Wrapping destination #%d in %s; oldarg=%s, newarg=%s',
                    argidx, wrapclass, oldarg, newarg)
                self.emit_move (oldarg, newarg,
                                "wrapping destination argument")
                pass
            
            for argidx, wrapclass in srcwraps:
                oldarg = oper.sources[argidx]
                newarg = wrapclass ()
                self.log.low (
                    'Wrapping source #%d in %s; oldarg=%s, newarg=%s',
                    argidx, wrapclass, oldarg, newarg)
                oper.sources[argidx] = newarg
                srcmoves.append (Operation ('MOVE',
                                            dests=(newarg,),
                                            sources=(oldarg,)))
                assign_arg (newarg)
                pass
            
            pass

        # At this point, we have found a location that satisfies
        # everyone, possibly spilling temporaries along the way.
        # So we now emit the Operation itself.
        self.emit_oper (blk, oper)
        
        # We may have accumulated some moves from source registers
        # into temporary destinations.  Emit those now so so that
        # they proceed the oper's code and set up its arguments.
        for m in srcmoves: self.emit_oper (blk, m)
        
        # Now go through the destinations: the only thing we
        # really care about is if we see a SOFT{REG|STACKSLOT}
        # that is written, we can free its associated storage.
        # Note that there should be no SOFT* Arguments w/o uses,
        # so they should have storage assigned.  Also SOFT* must
        # be SSA.
        for arg in oper.dests:
            if arg.is_type (argflags.SOFTREG):
                reg = arg.assignment
                arg.set_assignment (None, None)

                # We have a new temp register available.  I'd hate
                # to see it go to waste, so if anyone has been deprived
                # of their rightful register give it to them:
                if self.spilledtemps:
                    temp = self.spilledtemps.pop ()
                    self.emit_move (temp.assignment, reg,
                                    "restoring temp to register")
                    temp.set_assignment (blk, reg)
                    curreglist.append (temp)
                else:
                    self.availtempregs.append (reg)
                    pass
                
                pass
            elif arg.is_type (argflags.SOFTSTACK):
                self.availsoftstacks.append (arg.assignemt)
                pass
            pass

        self.log.undent (saeoindent)

        pass


    def promote_temp_to_reg (self, blk, temp):

        """ Stores temp in one of the available temporary registers;
        emits a store if temp is thought to have a live value that is
        read later on in the generated code """

        regloc = self.availtempregs.pop ()

        # 'temp' is live if we've seen it already in this basic block
        # or it is thought to be live on the exit of the basic block:
        if temp.was_assigned_by (blk) or self.is_live_on_exit (blk, temp):
            # Note that though we are promoting the temp for the new region,
            # we insert a STORE to the stack: this is because in the future
            # (which we have already processed) it will be on the stack.
            # Remember, we are walking backwards.
            memslot = self.temp_stack_slot (temp)
            self.emit_move (memslot, regloc, "spilling temp from register")
            self.log.low ('promote_temp_to_reg: spilled regloc %s to %s',
                          regloc, memslot)
            pass
        
        temp.set_assignment (blk, regloc)
        self.curreglist.append (temp)
        self.log.mid ('promoted %s to register %s', temp, regloc)
        pass
    
    def emit_transition (self, fromblk, toblk):

        """ Emits instructions to manage a transition from 'fromblk'
        to 'toblk'.  This can only be done if 'fromblk' has a set of
        outgoing registers set in the field "registers_on_exit" and
        that 'toblk' has a set of incoming registers in the field
        "registers_on_entry."

        If those conditions are met, the code is generated and True is
        returned.  Also, the size of the buffer, which indicates the
        point of entry for the code, is stored in
        toblk.transition_point.  This will be used later by branch
        instructions in self.emit_oper ().

        If those conditions are not met, then no code is generated and
        False is returned.  -1 is stored in toblk.transition_point to
        indicate that no transition code was made. """

        fromreg = fromblk.registers_on_exit
        toreg = toblk.registers_on_entry

        if fromreg is None or toreg is None:
            toblk.transition_point = -1
            return False

        # We do not handle inter-register moves super intelligently.
        # Instead, we store all registers in 'fromreg' that are not
        # in the same spot in 'toreg', and load all the registers in
        # toreg that are not in the same spot in 'fromreg'.  This avoids
        # having to calculate dependencies or anything smart.  Finally,
        # we branch to the block if needed.

        # Note that we generate code backwards, so start with the branch:
        if self.finfo.buffer.size() != toblk.oper_point:
            assert toblk.oper_point != -1 # else registers_on_entry is None
            toblk.transition_point = toblk.oper_point
            self.emit_br (fromblk, toblk)
            pass

        # ...then the loads:
        for ttemp, treg in toreg.items ():

            # If this is in the same spot, no work is required.
            if fromreg.get (ttemp, None) is treg: continue

            # Otherwise, load 'ttemp' into 'treg':
            memslot = self.temp_stack_slot (ttemp)
            self.emit_move (treg, memslot,
                            "load temp for interblock transition")
            continue

        # ...then the stores:
        for ftemp, freg in fromreg.items ():

            # If in the same spot, do nothing
            if toreg.get (ftemp, None) is freg: continue

            # Otherwise, store 'ftemp' from 'freg':
            memslot = self.temp_stack_slot (ftemp)
            self.emit_move (memslot, freg,
                            "store temp for interblock transition")
            pass

        # ...finally, store the size at the beginning of the transition.
        toblk.transition_point = self.finfo.buffer.size ()

        return True
            
    def emit_move (self, dst, src, comment):

        """ Convenience function: given two Arguments, emits a 'MOVE'
        Operation to move data from src to dst. """

        return self.emit_oper (None, Operation ('MOVE',
                                                dests=(dst,),
                                                sources=(src,),
                                                comment=comment))

    def emit_br (self, fromblk, toblk):

        """ Convenience function: given a block, emits a Branch to its
        first instruction. """

        foper = toblk.opers[0]
        return self.emit_oper (fromblk, Operation ('BR', target=(tartypes.OPER, foper)))

    def emit_oper (self, blk, op):

        """ Emits the bytes for the given Operation 'op'.  The
        parameter 'targetsize' indicates the size of the RJBuffer() at
        the time that the transition code for the block that this
        instruction may target was generated; if it is -1, then the
        block that this instruction may target has yet to be
        generated.

        Note that for any given block, there is only one block which
        may be targeted by an instruction in the block, and it is
        always the last instruction. """

        emitoperindent = self.log.low_indent ('emit_oper op=%s', op)
        
        try:
            self.log.low ('sources=%s', op.sources)
            
            # Compute a code generation function if none is specified.
            # This had better work, we don't allow spills or anything at
            # this point.
            if not op.cgfunc:
                cgfunc, dwraps, swraps = self.md.emit_table.apply_to_oper (op)
                assert cgfunc and not dwraps and not swraps
                op.set_code_generation_func (cgfunc)
                self.log.low ('Selected code generation function %s', cgfunc)
                pass
            
            # Watch out: this may clobber some register that need to
            # be preserved!  Generate the code to restore any
            # registers that must be preserved first.  Note that we
            # recurse here on self.emit(), and that this won't work if
            # the Operation is a BR or something.  But we assume it's
            # not.
            if op.clobber:
                clindent = self.log.low_indent (
                    'Scanning clobbered registers...')
                clobberstack = []
                for reg in op.clobber:
                    if reg.is_in_use ():
                        slot = self.soft_stack_slot ()
                        clobberstack.append ( (reg, slot) )
                        self.log.low (
                            'Clobbered reg %s being preserved in %s',
                            reg, slot)
                        self.emit_move (reg, slot, "preserve clobbered reg")
                        pass
                    pass
                self.log.undent (clindent)
                pass        
            
            # Generate the bytes for this function.  This may return a
            # linkobj if linking is required; for example, branches
            # return a linkobj and let us determine the offset that
            # they should be pointing at.            
            if self.comp.monitor: self.comp.monitor.low_level_emit (op)
            linkobj = op.cgfunc (self.finfo, op)

            # Insert any commment that should appear before this
            # function in the disassembly
            if self.comp.monitor:
                if op.comment: self.finfo.buffer.add_comment (0, op.comment)
                self.finfo.buffer.add_comment (0, op.opcode)
                if op.creation_instr: self.finfo.buffer.add_comment (
                    0, op.creation_instr.get_name())
                pass
            
            # If the instruction deferred linking to us, take care of it:
            if linkobj:
                if op.tartype == tartypes.OPER:
                    # If this is branching to another operation, it
                    # must be the last Operation in the block.
                    tarblock = op.target.block
                    targetsize = tarblock.transition_point
                    if targetsize != -1:
                        # If we built transition code, branch to it.
                        # Note that we stored the size of the buffer
                        # when the transition code was done, so
                        # convert that to an offset from the beginning
                        # of the buffer for the call to patch():
                        linkobj.patch (self.finfo.buffer.size() - targetsize)
                    else:
                        # Otherwise, successor is not built yet.  Add
                        # this to the list of pending transitions for
                        # that block.
                        tarblock.add_pending_pred (blk, linkobj)
                        pass
                    pass
                elif op.tartype == tartypes.GEN:
                    self.finfo.link_call_to (linkobj, op.target)
                elif op.tartype == tartypes.LIB:
                    # When calling a library function, we put in the
                    # "c_func_ptr;" for most architectures, this is
                    # correct.  For the others, they have to set up
                    # their CALL Operations differently (for example,
                    # using a CALLPTR instruction).
                    linkobj.patch (op.target.c_func_ptr)
                else:
                    raise util.PyntoException ("Unhandled target type %s"
                                               % tartypes.string (op.tartype))
                pass
            
            # Save the value of any clobbered registers before executing the
            # instruction (hence we generate them afterwards).
            if op.clobber:
                for reg, slot in clobberstack:
                    self.emit_move (slot, reg, "restore clobbered register")
                    pass
                pass        
                   
            return
        
        finally:
            self.log.undent (emitoperindent)
            pass
        
        pass

    def soft_stack_slot (self):

        # Use a free slot if there is one:
        if not self.availsoftstacks:
            return self.finfo.new_stack_slot ()

        # Or grab a new one:
        return self.availsoftstacks.pop ()    
    
    def temp_stack_slot (self, temp):
        
        # If the temp already has a home on the stack, use that:
        if temp.stack_slot: return temp.stack_slot 

        # Otherwise make one...
        if temp.is_local:
            # If this temporary is local to the block, grab a
            # soft_stack_slot and stash it on a list so we can reuse
            # it after the block is done
            slot = self.soft_stack_slot ()
            self.localtempslots.append (slot)
            pass
        else:
            # Otherwise, just give the temporary its own stack slot.
            # Don't bother reusing it.
            slot = self.finfo.new_stack_slot ()
            pass

        # ...and return it:
        temp.stack_slot = slot
        return slot        

    pass
