/* * Obtain some details of a given call site. */ void COptimiser::getCallSite(int required, POS &i, POS begin, std::deque<CALL_PARAM> ¶ms) const { do { if (i->udt & UDT_LINE) { ++i; break; } else { POS j = i; if ((i->udt & UDT_FUNC) && i->params) { // Another function! std::deque<CALL_PARAM> reparams; getCallSite(i->params, --j, begin, reparams); } params.push_front(std::pair<POS, POS>(j, i)); i = j; if (!--required) break; } } while (i-- != begin); }
/** * * @brief returns all call sites that correspond with the given exit - return site pair * * @param - exitSite: the exit of the pair whose call sites to look for * @param = returnSite: the return site of the pair whose call sites to look for * @return the set of all call sites that correspond with the exit - return site pair * */ const TransitionStorage::States TransitionStorage::getCallSites( State exitSite, State returnSite ) const { States calls; const Info::Returns & exit = T_info.exitTrans(exitSite); for( Info::ReturnIterator it = exit.begin(); it != exit.end(); it++ ) { if( getReturnSite(*it) == returnSite ) calls.insert(getCallSite(*it)); } return calls; }
/** * Q: how can this be optimized? * @brief test if there exists a return transition with the given from state, * predecessor state, and symbol in this collection of transitions * * @param - from: the desired from state for the return transition * @param - pred: the desired predecessor state for the return transition * @param - sym: the desired symbol for the return transition * @return true if there exists a return transition with the given from state and * symbol in this collection of transitions * */ bool TransitionStorage::returnExists( State from, State pred, Symbol sym ) const { Returns const & outgoing = T_info.exitTrans(from); for( ReturnIterator rit = outgoing.begin(); rit != outgoing.end(); rit++ ) { if( (getCallSite(*rit) == pred) && (getReturnSym(*rit) == sym) ) return true; } return false; }
/** * * @brief creates transitions for 'dup' mirroring 'orig' outgoing transitions * * @param - orig: the state that is being duplicated * @param - dup: the state that is duplicating 'orig' * */ void TransitionStorage::dupTransOutgoing( State orig, State dup ) { // Duplicate outgoing internal transitions. const Info::Internals & from = T_info.fromTrans(orig); for( Info::InternalIterator it = from.begin(); it != from.end(); it++ ) { Internal iTrans(dup,getInternalSym(*it),getTarget(*it)); addInternal(iTrans); } // Duplicate call site call transitions. const Info::Calls & call = T_info.callTrans(orig); for( Info::CallIterator it = call.begin(); it != call.end(); it++ ) { Call cTrans(dup,getCallSym(*it),getEntry(*it)); addCall(cTrans); } // Duplicate exit point return transitions. const Info::Returns & exit = T_info.exitTrans(orig); for( Info::ReturnIterator it = exit.begin(); it != exit.end(); it++ ) { Return rTrans(dup,getCallSite(*it),getReturnSym(*it),getReturnSite(*it)); addReturn(rTrans); } // Q: Do we want to do these? Seems inconsistent. -Evan // A: Aditya says 'yes', and thinks we need it. So we definitely don't // want to change it without looking at uses and compensating. // Duplicate call predecessor return transitions. const Info::Returns & pred = T_info.predTrans(orig); for( Info::ReturnIterator it = pred.begin(); it != pred.end(); it++ ) { Return rTrans(getExit(*it),dup,getReturnSym(*it),getReturnSite(*it)); addReturn(rTrans); } }
/** * * @brief print the collection of transitions * * @param - o: the output stream to print to * @return the output stream that was printed to * */ std::ostream & TransitionStorage::print( std::ostream & o ) const { //Print call transitions. o << "Delta_c: {\n "; bool first = true; for( CallIterator cit = callTrans.begin(); cit != callTrans.end(); cit++, first=false ) { if( !first ) o << ", \n "; o << "("; printKey(o,getCallSite(*cit)); o << " (=" << getCallSite(*cit) << ") "; o << ", "; printKey(o,getCallSym(*cit)); o << ", "; printKey(o,getEntry(*cit)); o << " (=" << getEntry(*cit) << ") "; o << ")"; } o << "\n}\n"; //Print internal transitions. o << "Delta_i: {\n "; first = true; for(InternalIterator iit = internalTrans.begin(); iit != internalTrans.end(); iit++, first=false ) { if( !first ) o << ",\n "; o << "("; printKey(o,getSource(*iit)); o << " (=" << getSource(*iit) << ") "; o << ", "; printKey(o,getInternalSym(*iit)); o << ", "; printKey(o,getTarget(*iit)); o << " (=" << getTarget(*iit) << ") "; o << ")"; } o << "\n}\n"; //Print return transitions. o << "Delta_r: {\n "; first = true; for(ReturnIterator rit = returnTrans.begin(); rit != returnTrans.end(); rit++, first = false ) { if( !first ) o << ",\n "; o << "("; printKey(o,getExit(*rit)); o << " (=" << getExit(*rit) << ") "; o << ", "; printKey(o,getCallSite(*rit)); o << " (=" << getCallSite(*rit) << ") "; o << ", "; printKey(o,getReturnSym(*rit)); o << ", "; printKey(o,getReturnSite(*rit)); o << " (=" << getReturnSite(*rit) << ") "; o << ")"; } o << "\n}\n"; return o; }
/** * * @brief creates transitions for 'dup' mirroring 'orig' transitions * * @param - orig: the state that is being duplicated * @param - dup: the state that is duplicating 'orig' * */ void TransitionStorage::dupTrans( State orig, State dup ) { //Duplicate outgoing internal transitions. const Info::Internals & from = T_info.fromTrans(orig); for( Info::InternalIterator it = from.begin(); it != from.end(); it++ ) { Internal iTrans(dup,getInternalSym(*it),getTarget(*it)); addInternal(iTrans); // Q: This is also inconsistent with dupTransOutgoing, which didn't do // anything special with self loops. -Evan // A: Aditya says yes, but this is again what McVeto(?) needs. // dupTrans is used in 'duplicateState', but dupTransOutgoing is // different. if( orig == getTarget(*it) ) //Handle self-loops. { Internal loop(dup,getInternalSym(*it),dup); addInternal(loop); } } //Duplicate incoming internal transitions. const Info::Internals & to = T_info.toTrans(orig); for( Info::InternalIterator it = to.begin(); it != to.end(); it++ ) { Internal iTrans(getSource(*it),getInternalSym(*it),dup); addInternal(iTrans); } //Duplicate call site call transitions. const Info::Calls & call = T_info.callTrans(orig); for( Info::CallIterator it = call.begin(); it != call.end(); it++ ) { Call cTrans(dup,getCallSym(*it),getEntry(*it)); addCall(cTrans); if( orig == getEntry(*it) ) //Handle self-loops. { Call loop(dup,getCallSym(*it),dup); addCall(loop); } } //Duplicate entry point call transitions. const Info::Calls & entry = T_info.entryTrans(orig); for( Info::CallIterator it = entry.begin(); it != entry.end(); it++ ) { Call cTrans(getCallSite(*it),getCallSym(*it),dup); addCall(cTrans); } //Duplicate exit point return transitions. const Info::Returns & exit = T_info.exitTrans(orig); for( Info::ReturnIterator it = exit.begin(); it != exit.end(); it++ ) { Return rTrans(dup,getCallSite(*it),getReturnSym(*it),getReturnSite(*it)); addReturn(rTrans); if( orig == getCallSite(*it) ) //Handle self-loops. { Return loop(dup,dup,getReturnSym(*it),getReturnSite(*it)); addReturn(loop); } if( orig == getReturnSite(*it) ) //Handle self-loops. { Return loop(dup,getCallSite(*it),getReturnSym(*it),dup); addReturn(loop); } if( orig == getCallSite(*it) && orig == getReturnSite(*it) ) //Handle self-loops. { Return loop(dup,dup,getReturnSym(*it),dup); addReturn(loop); } } //Duplicate call predecessor return transitions. const Info::Returns & pred = T_info.predTrans(orig); for( Info::ReturnIterator it = pred.begin(); it != pred.end(); it++ ) { Return rTrans(getExit(*it),dup,getReturnSym(*it),getReturnSite(*it)); addReturn(rTrans); if( orig == getReturnSite(*it) ) //Handle self-loops. { Return loop(getExit(*it),dup,getReturnSym(*it),dup); addReturn(loop); } } //Duplicate return site return transitions. const Info::Returns & ret = T_info.retTrans(orig); for( Info::ReturnIterator it = ret.begin(); it != ret.end(); it++ ) { Return rTrans(getExit(*it),getCallSite(*it),getReturnSym(*it),dup); addReturn(rTrans); } }
/* * Inline expand a program's functions. * This also performs some rudimentary constant propagation * but a better constant propagator should be written at some * latter date! */ bool COptimiser::inlineExpand() { std::map<LPNAMED_METHOD, MACHINE_UNITS> methods; // Make copies of all the program's methods. { std::vector<tagNamedMethod>::iterator i = m_prg.m_methods.begin(); for (; i != m_prg.m_methods.end(); ++i) { if (!i->bInline) continue; MACHINE_UNITS &method = methods[&*i]; POS j = m_prg.m_units.begin() + i->i; int depth = 0; do { method.push_back(*j); if (j->udt & UDT_OPEN) ++depth; else if ((j->udt & UDT_CLOSE) && !--depth) break; } while (++j != m_prg.m_units.end()); method.pop_front(); method.pop_back(); // m_prg.m_units.erase(m_prg.m_units.begin() + i->i - 1, j + 1); } } if (!methods.size()) return false; // Locate method calls. TCHAR chr = 0; POS i = m_prg.m_units.begin(); for (; i != m_prg.m_units.end(); ++i) { if (!((i->udt & UDT_FUNC) && (i->func == CProgram::methodCall))) continue; POS unit = i - 1; if (unit->udt & UDT_OBJ) continue; LPNAMED_METHOD p = NAMED_METHOD::locate(unit->lit, i->params - 1, false, m_prg); if (!(p && p->bInline)) continue; LPMACHINE_UNITS pUnits = &methods.find(p)->second; // Back peddle to find where this call site begins. MACHINE_UNITS paramUnits; POS j = i; std::map<STRING, MACHINE_UNIT> subst; { std::deque<CALL_PARAM> params; getCallSite(i->params, --j, m_prg.m_units.begin(), params); MACHINE_UNIT assign; assign.udt = UNIT_DATA_TYPE(UDT_FUNC | UDT_LINE); assign.params = 2; assign.func = operators::assign; std::deque<CALL_PARAM>::iterator k = params.begin(); for (; k != (params.end() - 1); ++k) { TCHAR pos = p->params - (k - params.begin()); STRING var = STRING(_T(" ")) + pos; if ((k->first != k->second) || (~(k->first->udt) & UDT_ID)) { MACHINE_UNIT lhs; lhs.udt = UDT_ID; lhs.lit = var; paramUnits.push_back(lhs); paramUnits.insert(paramUnits.end(), k->first, k->second + 1); paramUnits.push_back(assign); } else { // The parameter is just a simple constant value so // we can plug it to the function directly later. subst.insert(std::pair<STRING, MACHINE_UNIT>( var, *k->first )); } } } paramUnits.insert(paramUnits.end(), pUnits->begin(), pUnits->end()); MACHINE_UNIT mu; mu.udt = UDT_ID; mu.lit = STRING(_T(" ret")) + ++chr; MACHINE_UNITS retVal; // Fix up return values. POS k; for (k = paramUnits.begin(); k != paramUnits.end(); ++k) { if (k->udt & UDT_ID) { std::map<STRING, MACHINE_UNIT>::iterator i = subst.find(k->lit); if (i != subst.end()) { *k = i->second; } else if (k->lit[0] == ' ') { // This is a parameter, but we can't leave it named this since we // are not in a local scope. k->lit[0] = '-'; } continue; } if (!((k->udt & UDT_FUNC) && (k->func == CProgram::returnVal))) continue; if (k == (paramUnits.end() - 1)) { // This return statement is the last statement in the function // so we can just replace the function call by its argument // rather than setting up the return properly. std::deque<CALL_PARAM> params; POS j = k - 1; getCallSite(1, j, paramUnits.begin(), params); retVal.insert(retVal.begin(), j, k); paramUnits.erase(j, k + 1); break; } else { k->func = operators::assign; k->params = 2; std::deque<CALL_PARAM> rparams; getCallSite(1, --k, paramUnits.begin(), rparams); k = paramUnits.insert(k, mu) + 1; } } k = m_prg.m_units.erase(j, i + 1); if (retVal.size()) { const int kpos = k - m_prg.m_units.begin(); m_prg.m_units.insert(k, retVal.begin(), retVal.end()); k = m_prg.m_units.begin() + kpos; } else { k = m_prg.m_units.insert(k, mu); } while (--k != m_prg.m_units.begin()) { if (k->udt & UDT_LINE) break; } // Now we insert the function's body! m_prg.m_units.insert(k + 1, paramUnits.begin(), paramUnits.end()); i = m_prg.m_units.begin(); // (Heh...) } /**{ POS i = m_prg.m_units.begin(); for (; i != m_prg.m_units.end(); ++i) { i->show(); } }**/ return true; }
/* * Propagate constant values by replacing variable references * with their values where those values are constants or other * variables. */ void COptimiser::propagateConstants() { // Beware: terrible algorithm here! // // When if/while/for/branch are encountered, and when if/while/for // end, the constant pool is reset even if conditional code does // does affect certain constants! This is to avoid trick situations // but it is far over-reaching. In general it is safe to maintain // the pool of constants for an if...else construct (using the same // pool that existed before the construct for both parts of it) but // this is too much work for now. (Branch will proabably break this // algorithm!) // // The technique here is to look for assignment instructions with // constant right hand sides and maintain a map (the "pool") of // {var : constant} that is updated with each new assignment. Then // when a reference to a var is found, it is replaced by the constant // in the pool. This is good enough to mitigate the ineffectuality of // the redundant assignment instructions generated by inlineExpand(), // but not good enough to do anything else! // // This algorithm also makes the assumption that all function calls // have no side effects. It is not safe (!) for general use because // of this! std::map<STRING, MACHINE_UNIT> pool; POS i = m_prg.m_units.begin(); for (; i != m_prg.m_units.end(); ++i) { if (i->udt & UDT_FUNC) { if (i->func == operators::assign) { POS j = i - 1; std::deque<CALL_PARAM> params; getCallSite(2, j, m_prg.m_units.begin(), params); CALL_PARAM lhs = params.front(); CALL_PARAM rhs = params.back(); // Don't store values if the left hand side is something // esoteric like a function or another assignment. if (lhs.first != lhs.second) continue; if ((rhs.first != rhs.second) || (rhs.first->udt & UDT_FUNC)) { // If this value is already in the constant pool, we need // to purge it. pool.erase(lhs.first->lit); continue; } // Add an entry to the pool. pool.insert(std::pair<STRING, MACHINE_UNIT>( lhs.first->lit, *rhs.first )); } else if ((i->func == CProgram::conditional) | (i->func == CProgram::elseIf) | (i->func == CProgram::whileLoop) | (i->func == CProgram::untilLoop) | (i->func == CProgram::forLoop) | (i->func == CProgram::skipMethod)) { // Clear the pool (!) pool.clear(); } } else if (i->udt & UDT_ID) { // Look ahead to make sure this isn't the left-hand-side // of an assignment. bool bFailed = false; for (POS j = i; j != m_prg.m_units.end(); ++j) { if ((j->udt & UDT_FUNC) && (j->func == operators::assign)) { POS k = j - 1; std::deque<CALL_PARAM> params; getCallSite(2, k, m_prg.m_units.begin(), params); if (params[0].first == i) { bFailed = true; break; } } else if (j->udt & UDT_LINE) break; } if (bFailed) continue; // This is a referece to a constant - but is this constant in the pool? std::map<STRING, MACHINE_UNIT>::iterator k = pool.find(i->lit); if (k != pool.end()) { *i = k->second; } } } }