needed more than one phi (due to the dominance frontier containing more than one basic block).
		
			
				
	
	
		
			234 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			234 lines
		
	
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include "mcg.h"
 | |
| 
 | |
| static PMAPOF(struct basicblock, struct basicblock) dominancefrontiers;
 | |
| 
 | |
| static struct local* current_local;
 | |
| static ARRAYOF(struct basicblock) defining;
 | |
| static ARRAYOF(struct basicblock) needsphis;
 | |
| static ARRAYOF(struct ir) definitions;
 | |
| static ARRAYOF(struct basicblock) rewritten;
 | |
| 
 | |
| static void calculate_dominance_frontier_graph(void)
 | |
| {
 | |
|     /* This is the algorithm described here:
 | |
|      *
 | |
|      * Cooper, Keith D., Timothy J. Harvey, and Ken Kennedy.
 | |
|      * "A simple, fast dominance algorithm."
 | |
|      * Software Practice & Experience 4.1-10 (2001): 1-8.
 | |
|      *
 | |
|      * https://www.cs.rice.edu/~keith/EMBED/dom.pdf
 | |
|      */
 | |
| 
 | |
|     int i, j;
 | |
| 
 | |
|     dominancefrontiers.count = 0;
 | |
| 
 | |
|     for (i=0; i<cfg.postorder.count; i++)
 | |
|     {
 | |
|         struct basicblock* b = cfg.postorder.item[i];
 | |
|         struct basicblock* dominator = pmap_findleft(&dominance.graph, b);
 | |
|         if (b->prevs.count >= 2)
 | |
|         {
 | |
|             for (j=0; j<b->prevs.count; j++)
 | |
|             {
 | |
|                 struct basicblock* runner = b->prevs.item[j];
 | |
|                 while (runner != dominator)
 | |
|                 {
 | |
|                     tracef('S', "S: %s is in %s's dominance frontier\n",
 | |
|                         b->name, runner->name);
 | |
|                     pmap_add(&dominancefrontiers, runner, b);
 | |
|                     runner = pmap_findleft(&dominance.graph, runner);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| static bool is_local(struct ir* ir)
 | |
| {
 | |
|     return ((ir->opcode == IR_LOAD) &&
 | |
|             (ir->left->opcode == IR_LOCAL) &&
 | |
|             (ir->left->u.ivalue == current_local->offset));
 | |
| }
 | |
| 
 | |
| static bool rewrite_loads_cb(struct ir* ir, void* user)
 | |
| {
 | |
|     struct ir* definition = user;
 | |
| 
 | |
|     /* Rewrite in place where possible. */
 | |
| 
 | |
|     if (ir->left && is_local(ir->left))
 | |
|         ir->left = definition;
 | |
|     if (ir->right && is_local(ir->right))
 | |
|         ir->right = definition;
 | |
| 
 | |
|     /* Otherwise, go via a IR_NOP (which should, with luck, turn into no code). */
 | |
|     if (is_local(ir))
 | |
|     {
 | |
|         ir->opcode = IR_NOP;
 | |
|         ir->left = definition;
 | |
|         ir->right = NULL;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| /* Walks the tree, rewriting IRs to push new definitions downwards. */
 | |
| 
 | |
| static void recursively_rewrite_tree(struct basicblock* bb)
 | |
| {
 | |
|     int i;
 | |
|     int defcount = definitions.count;
 | |
| 
 | |
|     if (array_contains(&rewritten, bb))
 | |
|         return;
 | |
|     array_appendu(&rewritten, bb);
 | |
| 
 | |
|     for (i=0; i<bb->irs.count; i++)
 | |
|     {
 | |
|         struct ir* ir = bb->irs.item[i];
 | |
|         
 | |
|         if (definitions.count > 0)
 | |
|         {
 | |
|             ir_walk(ir, rewrite_loads_cb, definitions.item[definitions.count-1]);
 | |
|         }
 | |
| 
 | |
|         if (((ir->opcode == IR_STORE) &&
 | |
|              (ir->left->opcode == IR_LOCAL) &&
 | |
|              (ir->left->u.ivalue == current_local->offset)
 | |
|             ) ||
 | |
|             ((i == 0) &&
 | |
|              (ir->opcode == IR_PHI) &&
 | |
|              array_contains(&needsphis, bb)))
 | |
|         {
 | |
|             /* This is a definition. */
 | |
| 
 | |
|             if (ir->opcode == IR_STORE)
 | |
|             {
 | |
|                 ir->opcode = IR_NOP;
 | |
|                 ir->left = ir->right;
 | |
|                 ir->right = NULL;
 | |
|             }
 | |
|             array_push(&definitions, ir);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (i=0; i<bb->nexts.count; i++)
 | |
|     {
 | |
|         struct basicblock* nextbb = bb->nexts.item[i];
 | |
|         struct ir* ir = nextbb->irs.item[0];
 | |
| 
 | |
|         if ((definitions.count > 0) &&
 | |
|             (ir->opcode == IR_PHI) &&
 | |
|             array_contains(&needsphis, nextbb))
 | |
|         {
 | |
|             pmap_add(&ir->u.phivalue,
 | |
|                 bb, definitions.item[definitions.count-1]);
 | |
|         }
 | |
| 
 | |
|         recursively_rewrite_tree(nextbb);
 | |
|     }
 | |
| 
 | |
|     definitions.count = defcount;
 | |
| }
 | |
| 
 | |
| static void ssa_convert(void)
 | |
| {
 | |
|     int i, j;
 | |
| 
 | |
|     /* If this is a parameter, synthesise a load/store at the beginning of the
 | |
|      * program to force it into a register. (Unless it's written to it'll
 | |
|      * always be read from the frame.) */
 | |
| 
 | |
|     if (current_local->offset >= 0)
 | |
|     {
 | |
|         struct ir* ir = new_ir2(
 | |
|             IR_STORE, current_local->size,
 | |
|             new_localir(current_local->offset),
 | |
|             new_ir1(
 | |
|                 IR_LOAD, current_local->size,
 | |
|                 new_localir(current_local->offset)
 | |
|             )
 | |
|         );
 | |
| 
 | |
|         ir->root = ir;
 | |
|         ir->left->root = ir;
 | |
|         ir->right->root = ir;
 | |
|         ir->right->left->root = ir;
 | |
|         array_insert(&cfg.entry->irs, ir, 0);
 | |
|     }
 | |
| 
 | |
|     defining.count = 0;
 | |
|     needsphis.count = 0;
 | |
| 
 | |
|     /* Find everwhere where the variable is *defined*. */
 | |
| 
 | |
|     for (i=0; i<cfg.postorder.count; i++)
 | |
|     {
 | |
|         struct basicblock* bb = cfg.postorder.item[i];
 | |
|         for (j=0; j<bb->irs.count; j++)
 | |
|         {
 | |
|             struct ir* ir = bb->irs.item[j];
 | |
|             if ((ir->opcode == IR_STORE) &&
 | |
|                 (ir->left->opcode == IR_LOCAL) &&
 | |
|                 (ir->left->u.ivalue == current_local->offset))
 | |
|             {
 | |
|                 array_appendu(&defining, bb);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Every block which is in one of the defining block's dominance frontiers
 | |
|      * requires a phi. Remember that adding a phi also adds a definition. */
 | |
| 
 | |
|     for (i=0; i<defining.count; i++)
 | |
|     {
 | |
|         struct basicblock* bb = defining.item[i];
 | |
|         tracef('S', "S: local %d in defined in block %s\n", current_local->offset, bb->name);
 | |
|         for (j=0; j<dominancefrontiers.count; j++)
 | |
|         {
 | |
|             if (dominancefrontiers.item[j].left == bb)
 | |
|             {
 | |
|                 struct basicblock* dominates = dominancefrontiers.item[j].right;
 | |
|                 array_appendu(&needsphis, dominates);
 | |
|                 array_appendu(&defining, dominates);
 | |
|                 tracef('S', "S: local %d needs phi in block %s\n", current_local->offset, dominates->name);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* Add empty phi nodes. */
 | |
| 
 | |
|     for (i=0; i<needsphis.count; i++)
 | |
|     {
 | |
|         struct basicblock* bb = needsphis.item[i];
 | |
|         struct ir* ir = new_ir0(IR_PHI, current_local->size);
 | |
|         ir->root = ir;
 | |
|         array_insert(&bb->irs, ir, 0);
 | |
|     }
 | |
| 
 | |
|     /* Now do the rewriting by walking the tree, pushing definitions down the tree. */
 | |
| 
 | |
|     definitions.count = 0;
 | |
|     rewritten.count = 0;
 | |
|     recursively_rewrite_tree(cfg.entry);
 | |
| }
 | |
| 
 | |
| void pass_convert_locals_to_ssa(void)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     calculate_dominance_frontier_graph();
 | |
| 
 | |
|     for (i=0; i<current_proc->locals.count; i++)
 | |
|     {
 | |
|         current_local = current_proc->locals.item[i].right;
 | |
|         if (current_local->is_register)
 | |
|             ssa_convert();
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* vim: set sw=4 ts=4 expandtab : */
 | |
| 
 | |
| 
 |