C-Breeze
C Compiler Infrastructure

[ Project home page]
Main Page   Modules   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

inliner.cc

Go to the documentation of this file.
00001 // $Id: inliner.cc,v 1.6 2003/08/07 23:13:47 pnav Exp $
00002 // ----------------------------------------------------------------------
00003 //
00004 //  C-Breeze
00005 //  C Compiler Framework
00006 // 
00007 //  Copyright (c) 2003 University of Texas at Austin
00008 // 
00009 //  Samuel Z. Guyer
00010 //  Adam Brown
00011 //  Teck Bok Tok
00012 //  Paul Arthur Navratil
00013 //  Calvin Lin
00014 // 
00015 //  Permission is hereby granted, free of charge, to any person
00016 //  obtaining a copy of this software and associated documentation
00017 //  files (the "Software"), to deal in the Software without
00018 //  restriction, including without limitation the rights to use, copy,
00019 //  modify, merge, publish, distribute, sublicense, and/or sell copies
00020 //  of the Software, and to permit persons to whom the Software is
00021 //  furnished to do so, subject to the following conditions:
00022 //  
00023 //  The above copyright notice and this permission notice shall be
00024 //  included in all copies or substantial portions of the Software.
00025 //  
00026 //  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00027 //  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00028 //  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00029 //  NONINFRINGEMENT.  IN NO EVENT SHALL THE UNIVERSITY OF TEXAS AT
00030 //  AUSTIN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
00031 //  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
00032 //  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00033 //  THE SOFTWARE.
00034 //
00035 //  We acknowledge the C-to-C Translator from MIT Laboratory for
00036 //  Computer Science for inspiring parts of the C-Breeze design.
00037 //
00038 // ----------------------------------------------------------------------
00039 #include "inliner.h"
00040 
00041 /* Should only be used internally by the function inliner*/
00042 class identify_inlinees:public Walker
00043 {
00044    public:
00045       identify_inlinees(map<declNode *, procNode *> & m):
00046          Walker(Preorder, Subtree),
00047          _procmap(m) {};
00048       void at_threeAddr(threeAddrNode * ta, Order ord);
00049       inline set<threeAddrNode*> & callinline(){ return _callinline;}
00050       inline set<procNode *> & procs(){ return _procs;}
00051       inline set<procNode *> & inlined(){ return _inlined;}
00052 
00053    private:
00054       set<threeAddrNode *>  _callinline;
00055       map<declNode *, procNode *> & _procmap;
00056       set<procNode*> _procs;
00057       set<procNode*> _inlined;
00058 };
00059 
00060 
00076 void
00077 fi::run()
00078 {
00079    Linker l;
00080    unit_list_p up;
00081 
00082    /* first, be certain code is dismantled and CFG is correct*/
00083    for (up = CBZ::Program.begin(); up != CBZ::Program.end(); up++)
00084       cfg_changer::generate_cfg(*up);
00085 
00086    /* For the generic pass, we want the root of the callgraph to be
00087     * main.  So, we begin by finding main's location in the program
00088     */
00089    procNode * mainfunc = findmain();
00090 
00091    l.link(); /*linker connectes all procedure definitions with their
00092               *declaration - even across compilation units
00093               */
00094 
00095    callGraph cg(mainfunc, l); /*Creates a callgraph with main as the root node*/
00096 
00097 
00102    identify_inlinees i_i(l.procedure_declarations());
00103    function_inline f(i_i.callinline(), l.procedure_declarations(), 
00104                      i_i.procs(), i_i.inlined());
00105    /* This inserts main as a function which has not yet had its callees inlined.
00106     */
00107    if(mainfunc)
00108       f.insert_proc(mainfunc);
00109    else
00110       perror("Whoops!  Null root to callGraph.\n");
00111 
00112    /* Use identify inlinees find functions whose callsites should be inlined*/
00113    for (up = CBZ::Program.begin(); up != CBZ::Program.end(); ++up)
00114       (*up)->walk(i_i);
00115 
00116    /*Do the inlining*/
00117    f.process(cg);
00118    
00119    /*Re-dismantle to be certain all variables have unique names*/
00120    for (up = CBZ::Program.begin(); up != CBZ::Program.end(); ++up)
00121       Dismantle::dismantle(*up);
00122 
00123 }
00124 
00125 
00133 void
00134 function_inline::inline_functions(procNode * root)
00135 {
00136    Linker l;
00137    unit_list_p up;
00138 
00139    if(root == NULL)
00140    {
00141       root = findmain();
00142    }
00143 
00144    l.link(); 
00145    callGraph cg(root, l);
00146 
00147    if(!root)
00148       perror("oh no! null to callgraph");
00149 
00154    identify_inlinees i_i(l.procedure_declarations());
00155    function_inline f(i_i.callinline(), l.procedure_declarations(), 
00156                      i_i.procs(), i_i.inlined());
00157 
00158    f.insert_proc(root);
00159 
00160    for (up = CBZ::Program.begin(); up != CBZ::Program.end(); ++up)
00161       (*up)->walk(i_i);
00162 
00163    f.process(cg);
00164 
00165    for (up = CBZ::Program.begin(); up != CBZ::Program.end(); ++up)
00166       Dismantle::dismantle(*up);
00167 
00168 }
00169 
00170 /*This procedure fins the procNode for the "main" function in a given
00171  * program
00172  */
00173 procNode * findmain()
00174 {
00175    unit_list_p up;
00176    procNode * mainfunc=NULL;
00177    for (up = CBZ::Program.begin(); up != CBZ::Program.end(); ++up)
00178    {
00179       unitNode * unit = *up;
00180 
00181       for (def_list_p q = unit->defs().begin(); q != unit->defs().end(); ++q)
00182       {
00183          defNode * def = *q;
00184 
00185          if (def->typ() == Proc)
00186          {
00187             procNode * pr = (procNode *) def;
00188             if (pr->decl()->name() == "main")
00189             {
00190                mainfunc=pr;
00191             }
00192          }
00193       }
00194    }
00195 
00196    return mainfunc;
00197 }
00198 
00199 
00200 void
00201 function_inline::insert_proc(procNode * m)
00202 {
00203    _procs.insert(m);
00204 }
00205 
00206 /* This function calls the function inliner on callsites which are marked to be
00207  * marked to be inlined.  This marking is done by the identify_inlinees pass
00208  */
00209 void
00210 function_inline::process(callGraph & cg)
00211 {
00212 
00213    int size = 0;  /* In an attempt to be certain everything that should be 
00214                    * inlined is inlined, we first inline the callsites of 
00215                    * procedures with the smallest number of calls.  This
00216                    * variable keeps track of where we are - as this number 
00217                    * increases, more procedures become eligible to have their 
00218                    * callsites inlined.
00219                    */
00220    bool changed = false;  /* We have a set of procedure nodes whose callsites
00221                            * need to be inlined.  This variable keeps track of 
00222                            * whether any of these nodes had their callsites
00223                            * inlined and were removed from the set.
00224                            */
00225 #if DEBUG
00226    cout <<"in process" << endl;
00227 #endif
00228 
00229    /*Procs is the set of procedues which need their callsites inlined*/
00230    while(!_procs.empty())
00231    {
00232       /*while loop since iterator is not always incremented*/
00233       proc_set_p p = _procs.begin();
00234       while ( p!= _procs.end())
00235       {
00236 
00237          /* Find the current procNode in the callGraph, and get its             
00238           * callgraphNode
00239           */
00240          callGraphNode * cgn = cg.lookup(*p);
00241 
00242          /*If this call graph node has the number of calls we are looking
00243           *for or its calls have already had their calls inlined, then...
00244           */
00245          if(cgn->calls().size()==size || already_inlined_calls(cgn))
00246          {
00247 #if DEBUG
00248             cout << "going to function inliner "<< (*p)->decl()->name() <<endl;
00249 #endif
00250             /*go to function inliner at procNode...*/
00251             (*p)->change(*this);
00252 #if DEBUG
00253             cout << "inserting" << (*p)->decl()->name() << endl;
00254 #endif
00255             /* insert procedure into the set of procedurse with their calls
00256              * already inlined
00257              */
00258             _inlined.insert(*p);
00259 
00260             /* Continue through set of procedures-needing-their-calls-inlined
00261              * but erase this one out of the set
00262              */
00263             proc_set_p tmp = p;
00264             p++;
00265             _procs.erase(tmp);
00266 
00267 
00268             /*If we were previously allowing the inlining of function whose
00269              * calls did not yet have all of their callsites inlined, go back to
00270              * inlining only those with no callsites or whose calls have already
00271              * had their calls inlined
00272              * Otherwise, set changed to be true - this prevents us from 
00273              * infinite-looping
00274              */
00275             if(size!=0)
00276             {
00277                p = _procs.begin();
00278                size = 0;
00279             }
00280             else
00281                changed = true;
00282          }
00283          /*if this procedure cannot yet have its calls inlined, continue..*/
00284          else
00285             p++;
00286       }
00287       /* If nothing changed, increase the size*/
00288       if(!changed)
00289          size++;
00290       else
00291         changed=false;
00292    }
00293 
00294 #if DEBUG
00295    cout << "leaving process" << endl;
00296 #endif
00297 }
00298 
00299 /* This function checks to see if the procedure's calls have already had
00300  * their calls inlined.  If so, it returns true, otherwise, it returns
00301  * false
00302  */
00303 bool
00304 function_inline::already_inlined_calls(callGraphNode * cgn)
00305 { 
00306 
00307 #if DEBUG
00308    cout << "in already_inlined_calls"<<endl;
00309 #endif
00310 
00311    /*First, get the list of calls...*/
00312    const callgraph_edge_list & cgel = cgn->calls();
00313 
00314    /*If this list is empty, this procedure is self-contained, return true*/
00315    if(cgel.begin()==cgel.end())
00316    {
00317 #if DEBUG
00318       cout << "empty list, returning true"<< endl;
00319 #endif
00320       return true;
00321    }
00322 
00323    /* Iterate thourgh the list of procedurse called*/
00324    for(callgraph_edge_list_cp p = cgel.begin(); p != cgel.end(); p++)
00325    {
00326       callGraphNode * checkinline = *p;
00327    
00328       /*get the procedure related to the callGraphNode*/
00329       procNode * procinline = checkinline->proc();
00330 #if DEBUG
00331       cout << procinline->decl()->name() << endl;
00332 #endif
00333 
00334       /* If this procedure is not int he set of precedures already inlined,
00335        * return false.
00336        */
00337       if(_inlined.find(procinline)==_inlined.end())
00338          return false; 
00339    }
00340 
00341    /*All must have been found, return true*/
00342    return true;
00343 }
00344 
00345 /* Since we may be inlining callsites where the callee has not yet had its 
00346  * callsites inlined, we keep track of the callstack to prevent 
00347  * getting trapped by recursion of any sort
00348  */
00349 Node *
00350 function_inline::at_proc(procNode * p, Order ord)
00351 {
00352 #if DEBUG
00353    cout <<"inside at_proc" << endl;
00354 #endif
00355 
00356    if(ord == Preorder)
00357       _callstack.push_front(p);
00358    else
00359       _callstack.pop_front();
00360 
00361 #if DEBUG
00362    cout <<"leaving at_proc" << endl;
00363 #endif
00364 
00365    return p;
00366 }
00367 
00368 /*We find call sites marked inline from the exprstmtNode because we
00369  *need the call site to properly inline the node.
00370  *This checks for a call site and then checks if the callNode is in
00371  *the set of nodes to be inlined.  If it is, the inliner is called.
00372  */
00373 Node *
00374 function_inline::at_threeAddr(threeAddrNode * ta, Order ord)
00375 {
00376 
00377     /*Only want to do this Preorder...*/
00378     if(ord==Postorder)
00379        return ta;
00380 
00381 #if DEBUG
00382     cout << "in threeAddr" << endl;
00383 #endif
00384   
00385     /*return value*/
00386     stmtNode * s=NULL;
00387 
00388 
00389    /*Since the code has been dismantled, we have two cases to worry about.
00390     *1) a call by itself
00391     *2) a call as the rhs of an assignment statement
00392     */
00393 
00394    /*If the op is NULL, this is an assignment*/
00395    if(ta->op()==NULL)
00396       return ta;
00397 
00398    /*Do we have a callNode?*/
00399    if (ta->op()->id()==Operator::FUNC_CALL)
00400    {
00401       /* If it is a call node, find it's declaration and then get its
00402        * procedure declaration
00403        */
00404       operandNode * o = (operandNode *) ta->rhs1();
00405       indexNode * i = o->var();
00406    
00407       assert(i->typ()==Id); /*since we know this is a call, can't be a const*/
00408       idNode * call = (idNode *)i;
00409       declNode * decl = call->decl();
00410       procNode * proc = _procmap[decl];
00411 
00412       /*Was the linker able to find its body and is it set to be inlined?*/
00413       if (proc)
00414       {
00415          bool inlineit=false;
00416 
00417          /* if it is in this set, it should be an original callsite - not
00418           * one introduced by inlining
00419           */
00420          if(_callinline.find(ta)!=_callinline.end())
00421          {  
00422             //create local call stack, to be reset whenever at original callsite
00423             _localcallstack.clear();
00424             _localcallstack == _callstack; 
00425  
00426             inlineit = true;
00427          }
00428          else if(!inlocalstack(proc))
00429          {
00430             inlineit = true;
00431          }
00432 
00433          if(inlineit)
00434          {
00435             _localcallstack.push_front(proc);
00436             /*if so, let's inline!*/
00437             s = inliner(call, proc, ta);
00438             /*We inlined, so return result from inliner*/
00439             return s;
00440          }
00441       }
00442    }
00443 
00444    /*no inlining, return original*/
00445 #if DEBUG
00446    cout << " no inlining leaving threeAddr" << endl;
00447 #endif
00448    return ta;
00449 }
00450 
00451 /*Checks if p is contained in the local stack*/
00452 bool
00453 function_inline::inlocalstack(procNode *p)
00454 {
00455 #if DEBUG
00456    cout << "in inlocalstack" << endl;
00457 #endif
00458    for(int i = 0; i < _localcallstack.size(); i++)
00459    {
00460       if((_localcallstack[i])->decl()->name()==p->decl()->name())
00461          return true;
00462    }
00463    return false;
00464 }
00465 
00466 // Assumes dismantled code. Originally from Sam Guyer.
00467 stmtNode *
00468 function_inline::inliner(idNode * call, procNode * proc,
00469                          threeAddrNode * callsite)
00470 {
00471 
00476 
00477    procNode * dup_proc = (procNode *) ref_clone_changer::clone(proc, false);
00478 
00481 
00482    basicblockNode * exit = dup_proc->exit();
00483    blockNode * body = dup_proc->get_body();
00484 
00488 
00490 
00491    funcNode * fd = (funcNode *) dup_proc->decl()->type();
00492    decl_list & formal_args = fd->args();
00493 
00495 
00496    decl_list & topdecls = body->decls();
00497 
00499 
00500    threeAddrNode * dup_callsite = (threeAddrNode *) 
00501                               ref_clone_changer::clone(callsite, false);
00502    operand_list & actual_args = dup_callsite->arg_list();
00503 
00504 
00505    if (!fd->is_void_args())
00506    {
00507       operand_list::reverse_iterator actuals;
00508       decl_list::reverse_iterator formals;
00509  
00510       for(actuals = actual_args.rbegin(), formals = formal_args.rbegin();
00511           actuals!=actual_args.rend() && formals!=formal_args.rend();
00512           actuals++, formals++)
00513       { 
00514          declNode * one_arg = *formals;
00515          operandNode * one_act = *actuals;
00516 
00518 
00519          one_arg->init(one_act);
00520 
00522 
00523          one_arg->decl_location(declNode::BLOCK);
00524 
00525          topdecls.push_front(one_arg);
00526 
00527       }
00528    }
00529 
00533 
00534    returnNode * ret = (returnNode *) exit->stmts().back();
00535    exit->stmts().pop_back();
00536 
00537    assert(ret->typ() == Return);
00538 
00541    if(proc->return_decl())
00542    {
00543       cout << "return_decl non-null for "<< proc->decl()->name()<<endl;
00544    }
00545    if (proc->return_decl() && callsite->lhs())//in threeAddr, lhs non-null means
00546                                               //an assignment
00547    {
00548 
00550       declNode * retDecl = dup_proc->return_decl();
00551       idNode * returned_id = new idNode(retDecl);
00552 
00554 
00555       threeAddrNode * bin = (threeAddrNode *) callsite;
00556       operandNode * lhs = (operandNode *) 
00557                             ref_clone_changer::clone(bin->lhs(), false);
00558 
00561 
00562       operandNode * rhs = new operandNode(returned_id);
00563       threeAddrNode * assignment = 
00564          new threeAddrNode(lhs, rhs, call->coord());
00565       exit->stmts().push_back(assignment);
00566    }
00567 
00568    return body;
00569 
00570 }
00571 
00572 
00573 /* This is part of the first pass of the function inliner.  
00574  * At a callNode, we look to see
00575  * if the proc is of type inline.  If so, we make the function to
00576  * be inlined.  This phase also has an inline_all variable, typically
00577  * set to false, which can be set to true to inline all functions.
00578  */
00579 void
00580 identify_inlinees::at_threeAddr(threeAddrNode * call, Order ord)
00581 {
00582    if (call->op() && call->op()->id()==Operator::FUNC_CALL)
00583    {
00584 
00585       operandNode * o = (operandNode *) call->rhs1();
00586       indexNode * i = o->var();
00587       assert(i->typ()==Id); /*since we know this is a call, can't be a const*/
00588       idNode * c = (idNode *)i; 
00589       declNode * decl = c->decl();
00590       procNode * proc = _procmap[decl];
00591       if (proc)
00592       {
00593          funcNode * func = (funcNode *) proc->decl()->type();
00594          if(func->type_qualifiers() & typeNode::INLINE)
00595          {
00596             _callinline.insert(call);
00597             _procs.insert(proc);
00598          }
00599          else
00600          {
00601             _inlined.insert(proc);
00602          }
00603       }
00604 
00605    }
00606 }
00607 

Generated on August 27, 2003
Back to the C-Breeze home page