linscan_regalloc.cc

Go to the documentation of this file.
00001 #include <lestes/backend_v2/structs/func_data.g.hh>
00002 #include <lestes/backend_v2/intercode/ge.g.hh>
00003 #include <lestes/backend_v2/workers/linscan_regalloc.g.hh>
00004 #include <lestes/backend_v2/workers/alloc_int_finder.g.hh>
00005 #include <lestes/backend_v2/workers/bb_finder.g.hh>
00006 #include <lestes/backend_v2/workers/helpers.hh>
00007 #include <lestes/md/instructions/tm_instr.g.hh>
00008 #include <lestes/md/registers/tm_register.g.hh>
00009 
00010 #include <lestes/backend_v2/debug/debug.hh>
00011 
00012 package(lestes);
00013 package(backend_v2);
00014 package(workers);
00015 
00016 using namespace ::lestes::msg;
00017 using namespace ::lestes::backend_v2::intercode;
00018 using namespace ::lestes::backend_v2::debug;
00019 using namespace ::lestes::md::instructions;
00020 using namespace ::lestes::md::registers;
00021 
00022 typedef set<ulint> id_set__type;
00023 typedef vector<ulint> id_vector__type;
00024 typedef vector<srp<ge_pi> > ge_pi_vector__type;
00025 typedef vector<srp<liveness_range> > liveness_rng_vector__type;
00026 typedef vector<srp<alloc_interval> > alloc_int_vector__type;
00027 typedef map<ulint, srp<alloc_interval> > reg2alloc_int__type;
00028 typedef map<srp<ge_operand_reg>, srp<tm_register> > ge_op_reg2tm_reg__type;
00029 
00030 declare_logger(rlog);
00031 initialize_logger( rlog, "register_allocator", backend_v2_logger );
00032 
00033 /*!
00034         \brief Allocates registers to register operands.
00035 */
00036 void linscan_regalloc::process() {
00037         rlog << "process start\n" << eolog;
00038         
00039         //Set all available registers free.
00040         setup_registers();
00041         
00042         //Get intervals for register allocation.
00043         waiting_intervals = alloc_int_vector__type::create(data_get()->alloc_ints_get());
00044 
00045         //Sort waiting intervals by increasing start point
00046         ::std::sort(waiting_intervals->begin(),waiting_intervals->end(),alloc_int_cmp1);
00047         
00048         for(alloc_int_vector__type::iterator it = waiting_intervals->begin(); it!=waiting_intervals->end();) {
00049                 ptr<alloc_interval> interval = *it;
00050 
00051                 //Remove old intervals.         
00052                 expire_old_intervals(interval);
00053                 
00054                 //Allocate register for interval.
00055                 ptr<tm_register> allocated_reg = get_free_register(interval);
00056                 
00057                 if ( allocated_reg ) {
00058                         //Free register for interval has been successfuly found.
00059                         interval->allocated_reg_set(allocated_reg);
00060                         
00061                         /*
00062                                 Find interval that was previous owner of the register.
00063                                 This information will help to set dependencies in order to
00064                                 prevent scheduler to schedule these two intervals in a way 
00065                                 where they overlap.
00066                         */
00067                         reg2alloc_int__type::iterator tmp = register_owners->find(allocated_reg->id_get());
00068                         
00069                         if ( tmp!=register_owners->end() ) {
00070                                 interval->allocated_obj_prev_owner_set(tmp->second);
00071                         }
00072                         
00073                         set_register_owner(allocated_reg,interval);
00074                 }
00075                 
00076                 //Set interval active.
00077                 it = waiting_intervals->erase(it);
00078                 active_intervals->push_back(interval);  
00079         }
00080 
00081         //Move rest of the active intervals to the expired intervals    
00082         for(alloc_int_vector__type::iterator it = active_intervals->begin(); it!=active_intervals->end();) {
00083                 expired_intervals->push_back(*it);
00084                 it = active_intervals->erase(it);
00085         }
00086 
00087         lassert(destroyers->size()==0); 
00088         lassert(waiting_intervals->size()==0);
00089         lassert(active_intervals->size()==0);
00090         
00091         rlog << "process end\n" << eolog;
00092 }
00093 
00094 /*!
00095         \brief Fills the all_registers set with all the available registers.
00096 */
00097 void linscan_regalloc::setup_registers() {
00098         for(ulint i = R_UNDEFINED+1; i<RIT_TERMINATOR; ++i) {
00099                 all_registers->insert(i);
00100         }
00101 }
00102 
00103 
00104 /*!
00105         \brief Finds a register that is to be assigned to a interval.
00106         
00107         It checks whether operand of the interval destroys another input operand of the currently processed pseudoinstruction. 
00108         If so, it returns a register assigned to the destroyed operand. In other case it looks to the free_registers set
00109         and picks a register that is compatible with the interval. If no free compatible register is found,
00110         it steals a register from an interval in active_intervals set.
00111         
00112         \param interval An interval.
00113         \return A free available register if found. NULL otherwise.
00114 */
00115 ptr<tm_register> linscan_regalloc::get_free_register(ptr<alloc_interval> interval) {
00116         rlog << "get_free_register start\n" << eolog;
00117         
00118         ptr<tm_register> reg = get_destroyed_register(interval->operand_get());
00119         
00120         ptr<id_set__type> avail_regs;
00121         if (reg) {
00122                 rlog << "** found destroyed register **\n" << eolog;
00123                 lassert(free_registers->find(reg->id_get())!=free_registers->end());
00124                 
00125                 avail_regs = id_set__type::create();
00126                 
00127                 ptr<id_set__type> tmp_set = reg->aliases_get();
00128                 
00129                 ::std::set_intersection(
00130                         interval->allowed_registers_get()->begin(),
00131                         interval->allowed_registers_get()->end(),
00132                         tmp_set->begin(),
00133                         tmp_set->end(),
00134                         ::std::inserter(*avail_regs,avail_regs->begin()));              
00135         } else {
00136                 avail_regs = id_set__type::create();
00137         
00138                 ::std::set_intersection(
00139                         free_registers->begin(),
00140                         free_registers->end(),
00141                         interval->allowed_registers_get()->begin(),
00142                         interval->allowed_registers_get()->end(),
00143                         ::std::inserter(*avail_regs,avail_regs->begin()));              
00144         }
00145         
00146         if ( avail_regs->size()==0 ) {  
00147                 //Sort active intervals by decreasing end point
00148                 ::std::sort(active_intervals->begin(),active_intervals->end(),alloc_int_cmp2);
00149 
00150                 //Steal register from an interval in active_interval set.
00151                 for(alloc_int_vector__type::iterator it = active_intervals->begin(); it!=active_intervals->end();++it) {
00152                         ptr<alloc_interval> victim = *it;
00153                         
00154                         if ( !victim->allocated_reg_get() ) {
00155                                 continue;
00156                         }
00157                         
00158                         ::std::set_intersection(
00159                                 interval->allowed_registers_get()->begin(),
00160                                 interval->allowed_registers_get()->end(),
00161                                 victim->allocated_reg_get()->aliases_get()->begin(),
00162                                 victim->allocated_reg_get()->aliases_get()->end(),
00163                                 ::std::inserter(*avail_regs,avail_regs->begin()));
00164                                 
00165                         if ( avail_regs->size()!=0 ) {
00166                                 active_intervals->erase(it);
00167                                 expired_intervals->push_back(victim);
00168                         
00169                                 //return the allocated register and its aliases to the free register set.
00170                                 used_registers->erase(victim->allocated_reg_get()->id_get());
00171 
00172                                 //set register owner to the previous owner of the register.                             
00173                                 set_register_owner(victim->allocated_reg_get(),victim->allocated_obj_prev_owner_get());
00174                                 
00175                                 victim->allocated_reg_set(NULL);
00176                                 victim->allocated_obj_prev_owner_set(NULL);
00177                                 break;
00178                         }
00179                 }
00180         }
00181         
00182         if (avail_regs->size()!=0) {    
00183                 reg = tm_register::instance(*avail_regs->begin());
00184         
00185                 //Add the allocated register to the used register set
00186                 used_registers->insert(reg->id_get());
00187         } else {
00188                 reg = NULL;
00189         }
00190         
00191         rlog << "get_free_register end\n" << eolog;
00192         return reg;
00193 }
00194 
00195 /*!
00196         \brief Sets an interval as owner of a register in the register_owners map.
00197 */
00198 void linscan_regalloc::set_register_owner(ptr<tm_register> reg, ptr<alloc_interval> interval) {
00199         for(id_set__type::iterator it = reg->aliases_get()->begin(); it!=reg->aliases_get()->end(); ++it) {
00200                 (*register_owners)[*it] = interval; 
00201         }
00202 }
00203 
00204 
00205 /*!
00206         \brief Recomputes the free_registers set.
00207         
00208         free_registers <- all_registers - used_registers
00209 */
00210 void linscan_regalloc::find_free_registers() {
00211         rlog << "find_free_registers start\n" << eolog;
00212         
00213         ptr<id_set__type> deep_used_registers = id_set__type::create();
00214         
00215         for(id_set__type::iterator it=used_registers->begin(); it!=used_registers->end(); ++it) {
00216                 ptr<tm_register> reg = tm_register::instance(*it);
00217                 
00218                 ptr<id_set__type> new_deep_used_registers = id_set__type::create();
00219                 
00220                 ::std::set_union(
00221                         reg->aliases_get()->begin(),
00222                         reg->aliases_get()->end(),
00223                         deep_used_registers->begin(),
00224                         deep_used_registers->end(),
00225                         ::std::inserter(*new_deep_used_registers,new_deep_used_registers->begin()));
00226                         
00227                 deep_used_registers = new_deep_used_registers;
00228         }
00229         
00230         ptr<id_set__type> updated_free_registers = id_set__type::create();
00231         
00232         ::std::set_difference(
00233                         all_registers->begin(),
00234                         all_registers->end(),
00235                         deep_used_registers->begin(),
00236                         deep_used_registers->end(),
00237                         ::std::inserter(*updated_free_registers,updated_free_registers->begin()));
00238                         
00239         free_registers = updated_free_registers;
00240         
00241         rlog << "find_free_registers end\n" << eolog;
00242 }
00243 
00244 /*!
00245         \brief Returns a target machine register that is destroyed by given operand within the currently processed pseudoinstruction.
00246 */
00247 ptr<tm_register> linscan_regalloc::get_destroyed_register(ptr<ge_operand_reg> op) {
00248         rlog << "get_destroyed_register start\n" << eolog;
00249         
00250         ptr<tm_register> reg = NULL;
00251         
00252         ge_op_reg2tm_reg__type::iterator it = destroyers->find(op);
00253         
00254         if (it!=destroyers->end()) {
00255                 reg = it->second;
00256                 destroyers->erase(it);
00257         }
00258         
00259         rlog << "get_destroyed_register end\n" << eolog;
00260         return reg;
00261 }
00262 
00263 /*!
00264         \brief Sets an operand of a pseudoinstruction as destroyer of a register.
00265         
00266         \param ge A pseudoinstruction.
00267         \param op A destroyer.
00268         \param reg A destroyed operand.
00269 */
00270 void linscan_regalloc::set_destroyed_register(ptr<ge_pi> ge, ptr<ge_operand_reg> op, ptr<tm_register> reg) {
00271         rlog << "set_destroyed_register start\n" << eolog;
00272         
00273         ptr<tm_instr_op_reg_base> tmop = ge_pi__find_tm_op_by_ge_op(ge,op);
00274         
00275         if ( !tmop ) {
00276                 rlog << "set_destroyed_register end 1\n" << eolog;
00277                 return;
00278         }
00279         
00280         ptr<id_vector__type> destroyed_by = tmop->destroyed_by_get();
00281         
00282         if ( !destroyed_by || destroyed_by->size()==0 ) {
00283                 rlog << "set_destroyed_register end 2\n" << eolog;
00284                 return;
00285         }
00286         
00287         ptr<tm_instr_base> tm = ge->instruction_get();
00288         lassert(tm);
00289                 
00290         for(ulint i=0; i<destroyed_by->size();++i) {            
00291                 ulint destroyer = (*destroyed_by)[i];
00292                 
00293                 for(ulint j=0; j<ge->operands_output_get()->size(); ++j) {
00294                         if ( (*tm->operands_output_get())[j]->id_get()==destroyer ) {
00295                                 (*destroyers)[(*ge->operands_output_get())[j].dncast<ge_operand_reg>()] = reg;
00296                                 rlog << "** destroyer set **\n" << eolog;
00297                                 break;
00298                         }
00299                 }
00300         }
00301         
00302         rlog << "set_destroyed_register end\n" << eolog;
00303         
00304 }
00305 
00306 /*!
00307         \brief Removes intervals whoose endpoints precede the currently processed interval from the active_intervals set.
00308         
00309         \param curr_interval A current interval.
00310 */
00311 void linscan_regalloc::expire_old_intervals(ptr<alloc_interval> curr_interval) {
00312         rlog << "expire_old_intervals start\n" << eolog;
00313         
00314         ulint curr_point = curr_interval->start_get();
00315         
00316         for(alloc_int_vector__type::iterator it = active_intervals->begin(); it!=active_intervals->end();) {
00317                 ptr<alloc_interval> interval = *it;
00318                 
00319                 if ( interval->end_get() <= curr_point ) {
00320                         rlog << "interval expired\n" << eolog;
00321                         
00322                         it = active_intervals->erase(it);
00323                         expired_intervals->push_back(interval);
00324                         
00325                         if ( interval->allocated_reg_get() ) {
00326                                 //return the allocated register and its aliases to the free register set.
00327                                 used_registers->erase(interval->allocated_reg_get()->id_get());
00328                                 
00329                                 if ( (*curr_interval->instructions_get())[0]==(*interval->instructions_get())[interval->instructions_get()->size()-1] ) {               
00330                                         set_destroyed_register((*curr_interval->instructions_get())[0],interval->operand_get(),interval->allocated_reg_get());
00331                                 }
00332                         }
00333                         
00334                 } else {
00335                         ++it;
00336                 }
00337         }
00338         
00339         find_free_registers();
00340         
00341         rlog << "expire_old_intervals end\n" << eolog;
00342 }
00343 
00344 ptr<id_set__type> linscan_regalloc::ge_pi__get_allowed_regs_for_op(ptr<ge_pi> ge,ptr<ge_operand_reg> op) {
00345         rlog << "ge_pi__get_allowed_regs_for_op start\n" << eolog;
00346         
00347         //get target machine description of the operand
00348         ptr<tm_instr_op_reg_base> tm_op = ge_pi__find_tm_op_by_ge_op(ge,op);
00349         lassert(tm_op);
00350         
00351         //allowed registers
00352         ptr<id_set__type> regs = id_set__type::create(tm_op->allowed_registers_get());
00353         
00354         ulint type_id = op->type_get()->id_get();
00355         /*
00356                 There are registers in the allowed register set that are incompatible with the operands datatype.
00357                 Those registers need to be removed from the set. 
00358         */
00359         for(id_set__type::iterator it = regs->begin(); it!=regs->end();) {
00360                 ptr<tm_register_base> reg = tm_register::instance(*it);
00361                 if ( reg->compatible_types_get()->find(type_id)==reg->compatible_types_get()->end() ) {
00362                         id_set__type::iterator del_it = it++;
00363                         regs->erase(del_it);
00364                 }
00365         }
00366         
00367         lassert(regs->size()!=0);
00368         
00369         rlog << "ge_pi__get_allowed_regs_for_op end\n" << eolog;
00370         return regs;
00371 }
00372 
00373 /*!
00374         \brief Returns a target machine operand description for given operand within given pseudoinstruction.
00375 */
00376 ptr<tm_instr_op_reg_base> linscan_regalloc::ge_pi__find_tm_op_by_ge_op(ptr<ge_pi> ge,ptr<ge_operand_reg> op) {
00377         rlog << "ge_pi__find_tm_op_by_ge_op start\n" << eolog;
00378         
00379         ptr<tm_instr_base> tm = ge->instruction_get();
00380         
00381         lassert(tm);
00382                                 
00383         for(ulint i=0; i<ge->operands_input_get()->size(); ++i) {
00384                 if ( op==(*ge->operands_input_get())[i] ) {
00385                         rlog << "ge_pi__find_tm_op_by_ge_op end 1\n" << eolog;
00386                         return (*tm->operands_input_get())[i].dncast<tm_instr_op_reg_base>();
00387                 }
00388         }
00389         
00390         for(ulint i=0; i<ge->operands_output_get()->size(); ++i) {
00391                 if ( op==(*ge->operands_output_get())[i] ) {
00392                         rlog << "ge_pi__find_tm_op_by_ge_op end 2\n" << eolog;
00393                         return (*tm->operands_output_get())[i].dncast<tm_instr_op_reg_base>();
00394                 }
00395         }
00396         
00397         rlog << "ge_pi__find_tm_op_by_ge_op end\n" << eolog;
00398         return NULL;
00399         
00400 }
00401 
00402 /*!
00403         \brief Sets up dependencies amoung dependent intervals.
00404         
00405         If two intervals uses the same register, it is needed to not allow them to overlap. It is done by
00406         setting dependence of a first pseudoinstruction of the second interval on a last pseudoinstruction 
00407         of the second interval.
00408 */
00409 void linscan_regalloc::set_interval_dependencies() {
00410         rlog << "set_dependencies start\n" << eolog;
00411         /*
00412                 If an interval B pick over register or spillplace of another interval A, it is important that the intervals
00413                 don't overlap and they are executed in the right order (A->B). It is accomplished by setting dependencies
00414                 among instructions of intervals.
00415         */
00416         for(alloc_int_vector__type::iterator it = expired_intervals->begin(); it!=expired_intervals->end(); ++it) {
00417                 ptr<alloc_interval> interval = *it;
00418                 ptr<alloc_interval> alloc_obj_prev_owner = interval->allocated_obj_prev_owner_get();
00419                 
00420                 if ( alloc_obj_prev_owner ) {
00421                         ptr<ge_pi> ge1 = alloc_obj_prev_owner->instructions_get()->back();
00422                         ptr<ge_pi> ge2 = interval->instructions_get()->front();
00423                         
00424                         if ( ge1->bb_get()==ge2->bb_get() ) {
00425                                 ge2->dependencies_get()->insert(ge1);
00426                         }
00427                 }
00428         }
00429         
00430         rlog << "set_dependencies end\n" << eolog;
00431 }
00432 
00433 /*!
00434         \brief Sets allocated registers of intervals to operands.
00435 */
00436 void linscan_regalloc::set_registers_to_operands() {
00437         rlog << "set_registers start\n" << eolog;
00438         /*
00439                 Set assigned_registers and assigned_spill_space properties of ge_operand_regs according to the result of
00440                 the register allocation 
00441         */
00442         for(alloc_int_vector__type::iterator it = expired_intervals->begin(); it!=expired_intervals->end(); ++it) {
00443                 ptr<alloc_interval> interval = *it;
00444                 
00445                 ptr<ge_operand_reg> operand = interval->operand_get();
00446                 
00447                 if ( interval->allocated_reg_get() ) {
00448                         //Operand has register assigned.
00449                         ulint assigned_register = interval->allocated_reg_get()->id_get();
00450                         
00451                         lassert(interval->allowed_registers_get()->find(assigned_register)!=interval->allowed_registers_get()->end());
00452                 
00453                         ptr<ge_pi_vector__type> instructions = interval->instructions_get();
00454                         for(ge_pi_vector__type::iterator it2 = instructions->begin(); it2!=instructions->end(); ++it2) {
00455                                 (*operand->assigned_registers_get())[*it2] = assigned_register;
00456                         }
00457                         
00458                 }       
00459         }
00460         rlog << "set_registers end\n" << eolog;
00461 }
00462 
00463 /*!
00464         \brief Returns data of the currently processed function with result of the register allocation.
00465 */
00466 ptr< ::lestes::backend_v2::structs::func_data > linscan_regalloc::get_result() {
00467         rlog << "get_result start\n" << eolog;
00468         
00469         set_registers_to_operands();
00470         
00471         set_interval_dependencies();
00472         
00473         data_get()->alloc_ints_set(expired_intervals);
00474         
00475         rlog << "get_result end\n" << eolog;
00476         return data_get();
00477 }
00478 
00479 end_package(workers);
00480 end_package(backend_v2);
00481 end_package(lestes);
00482 

Generated on Mon Feb 12 18:22:37 2007 for lestes by doxygen 1.5.1-20070107