next up previous
Next: Known Bugs Up: The C-Breeze Compiler Infrastructure Previous: Discussion

Loop Peeling: A MIR Example

This section provides an example of a loop peeler that operates at the MIR level.

/*
Loop Peeler, for dismantled code
11/30/03 Walter Chang <walter@cs.utexas.edu>

This demonstrates loop peeling at the MIR level, using cfg, dominators,
loopTree, and related goodies.

Expects: Your stuff in CFG form.  Unfortunately, you must pass it an entire
         unit at a time, because that's what Dismantle::dismantle wants, and
         we need to re-dismantle to fix up labels.

Gives:   Dismantled code with loops peeled.
*/

#include <algorithm>
#include "c_breeze.h"
#include "looppeeldis.h"
#include "cfg.h"

LoopPeelingChangerDismantled::LoopPeelingChangerDismantled(void) : 
  Changer (Postorder, Subtree, false) {}

/*
  at_proc(the_proc, ord)
  
  Runs dominators and loopTree on the_proc, then walks the loop
  tree for the_proc
*/
Node * LoopPeelingChangerDismantled::at_proc(procNode *the_proc, Order ord) {

  myProc = the_proc;
  
  /* let Dominators decorate things */
  Dominators d(the_proc, true);
  
  /* figure out the loop tree */
  loopTree l(the_proc);
    
  /* walk that tree */
  loopTreeWalk(l.topLoop());

  return the_proc;
}

/*
  at_unit(the_unit, ord)

  If we've finished everything for the unit (coming up in postorder),
  re-dismantle the unit to fixup label names.
  Changers should be postorder anyway.
*/
Node * LoopPeelingChangerDismantled::at_unit(unitNode *the_unit, Order ord) {
  cfg_changer::generate_cfg(the_unit);
  return the_unit;
}

/*
  change(u)
  Loop-peels the unit u.
*/
void LoopPeelingChangerDismantled::change(unitNode *the_unit) {
  if(!the_unit) return;
  LoopPeelingChangerDismantled lpcd;
  the_unit->change(lpcd);
}

/*
  change()
  Loop-peels everything.
*/
void LoopPeelingChangerDismantled::change() {
    unit_list_p u;
    for (u=CBZ::Program.begin(); u != CBZ::Program.end(); u++) {
      change( *u );  // change each unit
    }
}

/*
  loopTreeWalk(l)
  Walks loop tree in postorder.  Peel every natural (SingleEntry) loop.
*/
void LoopPeelingChangerDismantled::loopTreeWalk(loopTreeNode * l) {
  loop_set children = l->nestedLoops();
  loop_set::iterator child;

  /* walk the nested loops */
  for(child = children.begin(); child != children.end(); child++) {
    loopTreeWalk( (*child) );
  }

  /* peel this loop, if it is single-entry */
  if(l->kind() == loopTreeNode::SingleEntry) {
    peel(l);
  }
  return;
}

/*
  peel(loopTreeNode * l)

  Peels the single-entry loop indicated by l.

  This should leave the original entry blocks to the loop going to the new
  copy, and all backedges in the loop copy going to the loop top pointing to
  the old loop top.  The body of the loop is not otherwise tinkered with.
*/
void LoopPeelingChangerDismantled::peel(loopTreeNode * l) {

  /* make list of all basic blocks in l's blockset */

  /*
    NOTE:
    We implicitly assume that when we iterate over the block list in a loop,
    that the FIRST block is the loop header.  This assumption is empirically
    true as of 11/30/03.  Should this no longer hold, we must find and move
    the header to the front BEFORE ref-cloning the thing.
  */

  stmt_list loopBlocks = bbset2list(all_blocks(l));

  /* put all basic blocks into one blockNode */
  blockNode loopBodyBlock(NULL,
                          &loopBlocks,
                          Coord::Unknown,
                          Coord::Unknown);
  /*
    ref-clone the block with all the basic blocks in the loop.
    ref-clone should not change the order of the basic blocks.
  */
  blockNode * loopBodyCopy = 
    (blockNode *) (ref_clone_changer::clone(&loopBodyBlock, false));

  stmt_list::iterator lp;
  basicblock_list::iterator lpp;
  stmt_list loop_preds;

  /*
    Look for the entries into the loop, ie, predecessors of
    the header that are not themselves part of the loop
  */
  basicblockNode * loop_header = l->header();
  basicblockNode * loop_newheader = 
    (basicblockNode *) loopBodyCopy->stmts().front();
  
  for(lpp = loop_header->preds().begin(); 
      lpp != loop_header->preds().end(); 
      lpp++) {
    /* If this pred is NOT in the loop's blockset */
    if( find(l->blocks().begin(), l->blocks().end(),
             (*lpp) ) 
        == l->blocks().end()) {
      /* add this pred to the list of loop entries */
      loop_preds.push_front( (*lpp) );
    }
  }

  string lnhName = ((labelNode *) loop_newheader->stmts().front())->name();

  /*
    A basic block always ends in a goto.  It is possible for the next-
    to-last to be a conditionGoto (but not a goto).  These are the only
    possible jump statements you will find in a basic block.
  */

  /* For every (external) entry point to this loop... */
  for(lp = loop_preds.begin(); lp != loop_preds.end(); lp++) {

    /* get the last statement in the block*/
    stmt_list::iterator lastStmtIter = 
      ((basicblockNode *) (*lp))->stmts().end(); lastStmtIter--;

    /* If it comes in via the fall-through... */
    if( ((gotoNode *) (*lastStmtIter))->label()->name() == lnhName) {

      /* ...join that block to the NEW header */
      bbjoin( (basicblockNode *) (*lp), loop_newheader);
    }

    /* if there is a next-to-last... */
    if(lastStmtIter != ((basicblockNode *) (*lp))->stmts().begin()) {
      lastStmtIter--;
      /* ... and it is a conditionGoto... */
      if( (*lastStmtIter)->typ() == Condition ) {
        /* ... and it enters the loop... */
        if( ((conditiongotoNode *) (*lastStmtIter))->label()->name() ==
            lnhName) {
          /* ...join that conditionGoto to the NEW header*/
          bbjoincond( (basicblockNode *) (*lp), loop_newheader);
        }
      }
    }
  }

  /*
    Now we go through all the blocks in the loop, and for any block that goes
    to the top of the loop, make sure it goes to the OLD top of the loop, and
    not the top of the cloned body.  Ordinarily there should be just one such
    block.  [I'm not sure about continue...]
  */

  /* The first thing in a basic block is ALWAYS a label */
  labelNode * loopTop = (labelNode *) loop_header->stmts().front();

  /* for all blocks b that head to the loop top */
  for(lp = loopBodyCopy->stmts().begin();
      lp != loopBodyCopy->stmts().end();
      lp++) {
    /* The last statement in a basic block is ALWAYS a goto */
    gotoNode * endingGoto = (gotoNode *) 
      ((basicblockNode *) (*lp))->stmts().back();
      
    /* If we're going to some loop top... */
    if(endingGoto->label()->name() == loopTop->name()) {
      /* ... make sure it goes to the OLD loop top */
      bbjoin( (basicblockNode *) (*lp), loop_header);
    }

    /* Now we fix up any conditional jumps to the top */

    stmt_list::iterator lastStmtIter = 
      ((basicblockNode *) (*lp))->stmts().end(); lastStmtIter--;

    /* if there is a next-to-last... */
    if(lastStmtIter != ((basicblockNode *) (*lp))->stmts().begin()) {
      lastStmtIter--;
      /* ... and it is a conditionGoto... */
      if( (*lastStmtIter)->typ() == Condition ) {
        /* ... and it goes to some loop top... */
        if( ((conditiongotoNode *) (*lastStmtIter))->label()->name() ==
            loopTop->name()) {
          /* ...join that conditionGoto to the OLD loop top */
          bbjoincond( (basicblockNode *) (*lp), loop_header);
        }
      }
    }
  }

  /*
    At this point, all the labels and gotos for the blocks should be pointing
    at the correct places, and all that remains is to put the blocks into the
    procedure proper.
  */

  /* We'll try to add the new blocks immediately before the original */
  stmt_list::iterator orig_start = find(myProc->body()->stmts().begin(),
                                        myProc->body()->stmts().end(),
                                        l->header());
  for(lp = loopBodyCopy->stmts().begin();
      lp != loopBodyCopy->stmts().end();
      lp++) {
    /* Add block to main body */
    myProc->body()->stmts().insert(orig_start, (*lp) );
    
    /* Add to our parent loop's blockset as well
       Note that we'll never peel the root "loop" which has no parent */
    l->parentLoop()->addBlock( (basicblockNode *) (*lp) );
  }  
}

/*
  bbjoin(b1, b2)

  Joins basic blocks b1 and b2, that is, b1's ending goto points to b2's
  starting label.  Use bbjoincond if you want the conditional to be changed.

  Note that bbjoin does NOT make sure that preds and succs are kept sane.
  This shouldn't be a problem in practice because we'll only be joining "on
  the inside" and re-dismantling will fix it anyway.

*/
void LoopPeelingChangerDismantled::bbjoin(basicblockNode * b1, 
                                          basicblockNode * b2) {
  gotoNode * endingGoto = (gotoNode *) b1->stmts().back();
  labelNode * startLabel = (labelNode *) b2->stmts().front();

  /* remove old label's reference */
  endingGoto->label()->references().remove(endingGoto);
  /* hook up to new label */
  endingGoto->label(startLabel);
}

/*
  bbjoincond(b1, b2)

  Joins basic blocks b1 and b2 via b1's conditionGoto.  The conditionGoto in
  b1 will go to b2, and the fall-through remains unchanged.  Use bbjoin if
  you want to join via the fall-through.

  Note that bbjoincond does NOT make sure that preds and succs are kept
  sane.  This shouldn't be a problem in practice because we'll only be
  joining "on the inside" and re-dismantling will fix it anyway.

*/
void LoopPeelingChangerDismantled::bbjoincond(basicblockNode * b1, 
                                              basicblockNode * b2) {
  labelNode * startLabel = (labelNode *) b2->stmts().front();

  /* Find the next-to-last node, if it is there... */
  stmt_list::iterator i = (b1->stmts()).end(); i--;
  if (i != (b1->stmts()).begin()) {
    i--;
    stmtNode * nextToLast = (*i);
    /* if it is a conditionGoto, fix the target to go to b2. */
    if (nextToLast->typ() == Condition) {
      ((conditiongotoNode *) nextToLast)->
        label()->references().remove((conditiongotoNode *) nextToLast);
      ((conditiongotoNode *) nextToLast)->label(startLabel);
    }
  }
}

/*
  bbset2list(bbset)

  Converts a basicblock_set to a basicblock_list
*/
stmt_list LoopPeelingChangerDismantled::bbset2list(basicblock_set &
                                                     bbset) {
  stmt_list bbl;
  basicblock_set::iterator bbsi;
  for(bbsi = bbset.begin(); bbsi != bbset.end(); bbsi++) {
    bbl.push_back( (*bbsi));
  }
  return stmt_list(bbl);
}

/*
  all_blocks(l)

  Returns all the blocks the loop l includes, including nested
  loops.  Changes loopTreeNodes so that blocks include nested loops.
*/

basicblock_set & LoopPeelingChangerDismantled::all_blocks(loopTreeNode * l) {

  loop_set children = l->nestedLoops();
  loop_set::iterator i;
  basicblock_set::iterator j;
  basicblock_set & b = l->blocks();
  basicblock_set tmp;

  for(i = children.begin(); i != children.end(); i++) {
    tmp = all_blocks( (*i) );
    for(j = tmp.begin(); j != tmp.end(); j++) {
      b.insert( (*j) );
    }
  }
  return b;
}



Adam C. Brown 2006-01-26