123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690 |
- /*
- * Andrea Di Biagio
- * Politecnico di Milano, 2007
- *
- * axe_reg_alloc.c
- * Formal Languages & Compilers Machine, 2007/2008
- *
- */
- #include "axe_reg_alloc.h"
- #include "reg_alloc_constants.h"
- #include "axe_debug.h"
- #include "axe_errors.h"
- extern int errorcode;
- static int compareIntervalIDs(void *varA, void *varB);
- static int compareStartPoints(void *varA, void *varB);
- static int compareEndPoints(void *varA, void *varB);
- static t_list * updateListOfIntervals(t_list *result
- , t_cflow_Node *current_node, int counter);
- static t_list * allocFreeRegisters(int regNum);
- static t_list * addFreeRegister
- (t_list *registers, int regID, int position);
- static int assignRegister(t_reg_allocator *RA);
- static t_list * expireOldIntervals(t_reg_allocator *RA
- , t_list *active_intervals, t_live_interval *interval);
- static t_list * getLiveIntervals(t_cflow_Graph *graph);
- static int insertListOfIntervals(t_reg_allocator *RA, t_list *intervals);
- static int insertLiveInterval(t_reg_allocator *RA, t_live_interval *interval);
- static void finalizeLiveInterval (t_live_interval *interval);
- static t_live_interval * allocLiveInterval(int varID, int startPoint, int endPoint);
- static t_list * spillAtInterval(t_reg_allocator *RA
- , t_list *active_intervals, t_live_interval *interval);
- /*
- * Perform a spill that allows the allocation of the given
- * interval, given the list of active live intervals
- */
- t_list * spillAtInterval(t_reg_allocator *RA
- , t_list *active_intervals, t_live_interval *interval)
- {
- t_list *last_element;
- t_live_interval *last_interval;
-
- /* get the last element of the list of active intervals */
- /* Precondition: if the list of active intervals is empty
- * we are working on a machine with 0 registers available
- * for the register allocation */
- if (active_intervals == NULL)
- {
- RA->bindings[interval->varID] = RA_SPILL_REQUIRED;
- return active_intervals;
- }
-
- last_element = getLastElement(active_intervals);
- last_interval = (t_live_interval *) LDATA(last_element);
- /* If the current interval ends before the last one, spill
- * the last one, otherwise spill the current interval */
- if (last_interval->endPoint > interval->endPoint)
- {
- RA->bindings[interval->varID]
- = RA->bindings[last_interval->varID];
- RA->bindings[last_interval->varID] = RA_SPILL_REQUIRED;
- active_intervals = removeElement(active_intervals, last_interval);
-
- active_intervals = addSorted(active_intervals
- , interval, compareEndPoints);
- }
- else
- RA->bindings[interval->varID] = RA_SPILL_REQUIRED;
- return active_intervals;
- }
- /*
- * Given two live intervals, compare them by
- * the start point (find whichever starts first)
- */
- int compareStartPoints(void *varA, void *varB)
- {
- t_live_interval *liA;
- t_live_interval *liB;
-
- if (varA == NULL)
- return 0;
- if (varB == NULL)
- return 0;
- liA = (t_live_interval *) varA;
- liB = (t_live_interval *) varB;
- return (liA->startPoint - liB->startPoint);
- }
- /*
- * Given two live intervals, compare them
- * by the end point (find whichever ends first)
- */
- int compareEndPoints(void *varA, void *varB)
- {
- t_live_interval *liA;
- t_live_interval *liB;
-
- if (varA == NULL)
- return 0;
- if (varB == NULL)
- return 0;
- liA = (t_live_interval *) varA;
- liB = (t_live_interval *) varB;
- return (liA->endPoint - liB->endPoint);
- }
- /*
- * Given two live intervals, check if they
- * refer to the same interval
- */
- int compareIntervalIDs(void *varA, void *varB)
- {
- t_live_interval *liA;
- t_live_interval *liB;
- if (varA == NULL)
- return 0;
- if (varB == NULL)
- return 0;
- liA = (t_live_interval *) varA;
- liB = (t_live_interval *) varB;
- return (liA->varID == liB->varID);
- }
- /*
- * Insert a live interval in the register allocator
- */
- int insertLiveInterval(t_reg_allocator *RA, t_live_interval *interval)
- {
- /* test the preconditions */
- if (RA == NULL)
- return RA_INVALID_ALLOCATOR;
- if (interval == NULL)
- return RA_INVALID_INTERVAL;
- /* test if an interval for the requested variable is already inserted */
- if (CustomfindElement(RA->live_intervals
- , interval, compareIntervalIDs) != NULL)
- {
- return RA_INTERVAL_ALREADY_INSERTED;
- }
- /* add the given interval to the list of intervals,
- * in order of starting point */
- RA->live_intervals = addSorted(RA->live_intervals
- , interval, compareStartPoints);
-
- return RA_OK;
- }
- /*
- * Allocate and initialize the free registers list,
- * assuming regNum general purpose registers
- */
- t_list * allocFreeRegisters(int regNum)
- {
- int count;
- t_list *result;
- /* initialize the local variables */
- count = 1;
- result = NULL;
-
- while(count <= regNum)
- {
- /* add a new register to the list of free registers */
- result = addFreeRegister(result, count, -1);
- /* update the `count' variable */
- count ++;
- }
- /* return the list of free registers */
- return result;
- }
- /*
- * Return register regID to the free list in the given position
- */
- t_list * addFreeRegister(t_list *registers, int regID, int position)
- {
- int *element;
- /* Allocate memory space for the reg id */
- element = (int *) _AXE_ALLOC_FUNCTION(sizeof(int));
- if (element == NULL)
- notifyError(AXE_OUT_OF_MEMORY);
- /* initialize element */
- (* element) = regID;
-
- /* update the list of registers */
- registers = addElement(registers, element, position);
- /* return the list of free registers */
- return registers;
- }
- /*
- * Get a new register from the free list
- */
- int assignRegister(t_reg_allocator *RA)
- {
- int regID;
- /* Check for list not empty*/
- if (RA->freeRegisters == NULL)
- return RA_SPILL_REQUIRED;
- /* Get the name of the first free register */
- regID = (* ((int *) LDATA(RA->freeRegisters)));
- /* Free the space allocated to hold the register id */
- _AXE_FREE_FUNCTION(LDATA(RA->freeRegisters));
- /* Remove the structure from the free registers list */
- RA->freeRegisters = removeFirst(RA->freeRegisters);
- return regID;
- }
- /*
- * Allocate and initialize the register allocator
- */
- t_reg_allocator * initializeRegAlloc(t_cflow_Graph *graph)
- {
- t_reg_allocator *result; /* the register allocator */
- t_list *intervals;
- t_list *current_elem;
- t_list *current_cflow_var;
- t_cflow_var *cflow_var;
- int max_var_ID;
- int counter;
- /* Check preconditions: the cfg must exist */
- if (graph == NULL)
- notifyError(AXE_INVALID_CFLOW_GRAPH);
- /* allocate memory for a new instance of `t_reg_allocator' */
- result = (t_reg_allocator *) _AXE_ALLOC_FUNCTION(sizeof(t_reg_allocator));
- if (result == NULL)
- notifyError(AXE_OUT_OF_MEMORY);
-
- /* initialize the register allocator informations */
- /* Reserve a few registers (RA_MIN_REG_NUM) to handle spills */
- result->regNum = NUM_REGISTERS - RA_MIN_REG_NUM;
- max_var_ID = getLength(graph->cflow_variables);
- /* retrieve the max identifier from each live interval */
- current_cflow_var = graph->cflow_variables;
- while (current_cflow_var != NULL)
- {
- /* fetch the data informations about a variable */
- cflow_var = (t_cflow_var *) LDATA(current_cflow_var);
- assert(cflow_var != NULL);
-
- /* update the value of max_var_ID */
- max_var_ID = (max_var_ID < cflow_var->ID)? cflow_var->ID : max_var_ID;
- /* retrieve the next variable */
- current_cflow_var = LNEXT(current_cflow_var);
- }
-
- /* update the value of `result->varNum' with the correct var. identifier */
- result->varNum = max_var_ID;
-
- current_elem = graph->cflow_variables;
-
- /* Assuming there are some variables to associate to regs,
- * allocate space for the binding array, and initialize it */
-
- /*alloc memory for the array of bindings */
- result->bindings = (int *) _AXE_ALLOC_FUNCTION
- (sizeof(int) * (result->varNum + 1) );
-
- /* test if an error occurred */
- if (result->bindings == NULL)
- notifyError(AXE_OUT_OF_MEMORY);
-
- /* initialize the array of bindings */
- for(counter = 0; counter <= result->varNum; counter++)
- result->bindings[counter] = RA_REGISTER_INVALID;
- /* Liveness analysis: compute the list of live intervals */
- result->live_intervals = NULL;
- intervals = getLiveIntervals(graph);
- /* Copy the liveness info into the register allocator */
- if (intervals != NULL)
- {
- if (insertListOfIntervals(result, intervals) != RA_OK)
- {
- finalizeRegAlloc(result);
- notifyError(AXE_REG_ALLOC_ERROR);
- }
- /* deallocate memory used to hold the results of the
- * liveness analysis */
- freeList(intervals);
- }
- /* create a list of freeRegisters */
- if (result->regNum > 0)
- result->freeRegisters = allocFreeRegisters(result->regNum);
- else
- result->freeRegisters = NULL;
-
- /* return the new register allocator */
- return result;
- }
- /*
- * Deallocate the register allocator data structures
- */
- void finalizeRegAlloc(t_reg_allocator *RA)
- {
- if (RA == NULL)
- return;
- /* If the list of live intervals is not empty,
- * deallocate its content */
- if (RA->live_intervals != NULL)
- {
- t_list *current_element;
- t_live_interval *current_interval;
- /* finalize the memory blocks associated with all
- * the live intervals */
- for (current_element = RA->live_intervals
- ; current_element != NULL
- ; current_element = LNEXT(current_element))
- {
- /* fetch the current interval */
- current_interval = (t_live_interval *) LDATA(current_element);
- if (current_interval != NULL)
- {
- /* finalize the memory block associated with
- * the current interval */
- finalizeLiveInterval(current_interval);
- }
- }
- /* deallocate the list of intervals */
- freeList(RA->live_intervals);
- }
- /* Free memory used for the variable/register bindings */
- if (RA->bindings != NULL)
- _AXE_FREE_FUNCTION(RA->bindings);
- if (RA->freeRegisters != NULL)
- {
- t_list *current_element;
- current_element = RA->freeRegisters;
- while (current_element != NULL)
- {
- _AXE_FREE_FUNCTION(LDATA(current_element));
- current_element = LNEXT(current_element);
- }
- freeList(RA->freeRegisters);
- }
- _AXE_FREE_FUNCTION(RA);
- }
- /*
- * Allocate and initialize a live interval data structure
- * with a given varID, starting and ending points
- */
- t_live_interval * allocLiveInterval
- (int varID, int startPoint, int endPoint)
- {
- t_live_interval *result;
- /* create a new instance of `t_live_interval' */
- result = _AXE_ALLOC_FUNCTION(sizeof(t_live_interval));
- if (result == NULL)
- notifyError(AXE_OUT_OF_MEMORY);
- /* initialize the new instance */
- result->varID = varID;
- result->startPoint = startPoint;
- result->endPoint = endPoint;
- /* return the new `t_live_interval' */
- return result;
- }
- /*
- * Deallocate a live interval
- */
- void finalizeLiveInterval (t_live_interval *interval)
- {
- if (interval == NULL)
- return;
- /* finalize the current interval */
- _AXE_FREE_FUNCTION(interval);
- }
- /*
- * Insert all elements of the intervals list into
- * the register allocator data structure
- */
- int insertListOfIntervals(t_reg_allocator *RA, t_list *intervals)
- {
- t_list *current_element;
- t_live_interval *interval;
- int ra_errorcode;
-
- /* preconditions */
- if (RA == NULL)
- return RA_INVALID_ALLOCATOR;
- if (intervals == NULL)
- return RA_OK;
- for (current_element = intervals
- ; current_element != NULL
- ; current_element = LNEXT(current_element) )
- {
- /* Get the current live interval */
- interval = (t_live_interval *) LDATA(current_element);
-
- if (interval == NULL)
- return RA_INVALID_INTERVAL;
- /* insert a new live interval */
- ra_errorcode = insertLiveInterval(RA, interval);
- /* test if an error occurred */
- if (ra_errorcode != RA_OK)
- return ra_errorcode;
- }
- return RA_OK;
- }
- /*
- * Perform live intervals computation
- */
- t_list * getLiveIntervals(t_cflow_Graph *graph)
- {
- t_list *current_bb_element;
- t_list *current_nd_element;
- t_basic_block *current_block;
- t_cflow_Node *current_node;
- t_list *result;
- int counter;
- /* preconditions */
- if (graph == NULL)
- return NULL;
- if (graph->blocks == NULL)
- return NULL;
- /* initialize the local variable `result' */
- result = NULL;
- /* intialize the instruction counter */
- counter = 0;
-
- /* fetch the first basic block */
- current_bb_element = graph->blocks;
- while (current_bb_element != NULL)
- {
- current_block = (t_basic_block *) LDATA(current_bb_element);
- /* fetch the first node of the basic block */
- current_nd_element = current_block->nodes;
- while(current_nd_element != NULL)
- {
- current_node = (t_cflow_Node *) LDATA(current_nd_element);
- /* update the live intervals with the liveness informations */
- result = updateListOfIntervals(result, current_node, counter);
-
- /* fetch the next node in the basic block */
- counter++;
- current_nd_element = LNEXT(current_nd_element);
- }
- /* fetch the next element in the list of basic blocks */
- current_bb_element = LNEXT(current_bb_element);
- }
- return result;
- }
- /*
- * Update the liveness interval for the variable 'id', used or defined
- * at position 'counter'.
- */
- t_list *updateVarInterval( int id, int counter, t_list *intervals )
- {
- t_list *element_found;
- t_live_interval *interval_found;
- t_live_interval pattern;
- if (id == RA_EXCLUDED_VARIABLE)
- return;
-
- pattern.varID = id;
- /* search for the current live interval */
- element_found = CustomfindElement(intervals, &pattern, compareIntervalIDs);
- if (element_found != NULL)
- {
- interval_found = (t_live_interval *) LDATA(element_found);
- /* update the interval informations */
- if (interval_found->startPoint > counter)
- interval_found->startPoint = counter;
- if (interval_found->endPoint < counter)
- interval_found->endPoint = counter;
- }
- else
- {
- /* we have to add a new live interval */
- interval_found = allocLiveInterval(id, counter, counter);
- if (interval_found == NULL)
- notifyError(AXE_OUT_OF_MEMORY);
- intervals = addElement(intervals, interval_found, -1);
- }
-
- return intervals;
- }
- /*
- * Use liveness information to update the list of
- * live intervals
- */
- t_list * updateListOfIntervals(t_list *result
- , t_cflow_Node *current_node, int counter)
- {
- t_list *current_element;
- t_cflow_var *current_var;
-
- if (current_node == NULL)
- return result;
- current_element = current_node->in;
- while (current_element != NULL)
- {
- current_var = (t_cflow_var *) LDATA(current_element);
- result = updateVarInterval(current_var->ID, counter, result);
-
- /* fetch the next element in the list of live variables */
- current_element = LNEXT(current_element);
- }
-
- current_element = current_node->out;
- while (current_element != NULL)
- {
- current_var = (t_cflow_var *) LDATA(current_element);
- result = updateVarInterval(current_var->ID, counter, result);
-
- /* fetch the next element in the list of live variables */
- current_element = LNEXT(current_element);
- }
- if (current_node->def)
- result = updateVarInterval(current_node->def->ID, counter, result);
- return result;
- }
- /*
- * Remove from active_intervals all the live intervals that end before the
- * beginning of the current live interval
- */
- t_list * expireOldIntervals(t_reg_allocator *RA, t_list *active_intervals
- , t_live_interval *interval)
- {
- t_list *current_element;
- t_list *next_element;
- t_live_interval *current_interval;
- /* Check for valid register allocator and set of active intervals */
- if (active_intervals == NULL)
- return NULL;
- if (RA == NULL)
- return NULL;
- if (interval == NULL)
- return active_intervals;
- /* Iterate over the set of active intervals */
- current_element = active_intervals;
- while(current_element != NULL)
- {
- /* Get the live interval */
- current_interval = (t_live_interval *) LDATA(current_element);
- /* If the considered interval ends before the beginning of
- * the current live interval, we don't need to keep track of
- * it anymore; otherwise, this is the first interval we must
- * still take into account when assigning registers */
- if (current_interval->endPoint >= interval->startPoint)
- return active_intervals;
- /* Get the next live interval */
- next_element = LNEXT(current_element);
- /* Remove the current element from the list */
- active_intervals = removeElement(active_intervals, current_interval);
- /* Free all the registers associated with the removed interval */
- RA->freeRegisters = addFreeRegister
- (RA->freeRegisters, RA->bindings[current_interval->varID], 0);
- /* Step to the next interval */
- current_element = next_element;
- }
- /* Return the updated list of active intervals */
- return active_intervals;
- }
- /*
- * Main register allocation function
- */
- int execute_linear_scan(t_reg_allocator *RA)
- {
- t_list *current_element;
- t_live_interval *current_interval;
- t_list *active_intervals;
-
- /* test the preconditions */
- if (RA == NULL) /* Register allocator created? */
- return RA_INVALID_ALLOCATOR;
- if (RA->live_intervals == NULL) /* Liveness analysis ready? */
- return RA_OK;
- /* initialize the list of active intervals */
- active_intervals = NULL;
-
- /* Iterate over the list of live intervals */
- for( current_element = RA->live_intervals
- ; current_element != NULL
- ; current_element = LNEXT(current_element) )
- {
- /* Get the live interval */
- current_interval = (t_live_interval *) LDATA(current_element);
- /* Check which intervals are ended and remove
- * them from the active set, thus freeing registers */
- active_intervals = expireOldIntervals
- (RA, active_intervals, current_interval);
- /* If all registers are busy, perform a spill */
- if (getLength(active_intervals) == RA->regNum)
- {
- /* perform a spill */
- active_intervals = spillAtInterval
- (RA, active_intervals, current_interval);
- }
- else /* Otherwise, assign a new register to the current live interval */
- {
- RA->bindings[current_interval->varID] = assignRegister(RA);
- /* Add the current interval to the list of active intervals, in
- * order of ending points (to allow easier expire management) */
- active_intervals = addSorted(active_intervals
- , current_interval, compareEndPoints);
- }
- }
- /* free the list of active intervals */
- freeList(active_intervals);
-
- return RA_OK;
- }
|