Index: build/custom/msvc_2003/jitrino/jitrino.vcproj =================================================================== --- build/custom/msvc_2003/jitrino/jitrino.vcproj (revision 638284) +++ build/custom/msvc_2003/jitrino/jitrino.vcproj (working copy) @@ -170,6 +170,12 @@ RelativePath="..\..\..\..\vm\jitrino\src\optimizer\CSEHash.h"> + + + + + + getNode()); } static void printInsts(std::ostream& cout, Node* node, uint32 indent); - static void printDotFile(ControlFlowGraph& cfg, MethodDesc& methodDesc,const char *suffix); + static void printDotFile(ControlFlowGraph& cfg, MethodDesc& methodDesc,const char *suffix, PrintDotFile* dotPrinter = NULL); private: static Node* duplicateNode(IRManager& irm, Node *source, Node *before, OpndRenameTable *renameTable); @@ -125,6 +125,20 @@ virtual uint32 getKeyHashCode(Node* key) const { return ((uint32)(((POINTER_SIZE_INT)key) >> sizeof(void*))); } }; +class HIRDotPrinter : public PrintDotFile { +public: + HIRDotPrinter(ControlFlowGraph& _fg) : fg(_fg), loopTree(NULL), dom(NULL){} + virtual void printDotBody(); + virtual void printDotNode(Node* node); + virtual void printDotNodeOtherInfo(Node* node) {} + virtual void printDotEdge(Edge* edge); +private: + ControlFlowGraph& fg; + LoopTree* loopTree; + DominatorTree* dom; +}; + + } //namespace Jitrino #endif // _FLOWGRAPH_ Index: vm/jitrino/src/optimizer/Opnd.h =================================================================== --- vm/jitrino/src/optimizer/Opnd.h (revision 638284) +++ vm/jitrino/src/optimizer/Opnd.h (working copy) @@ -277,15 +277,6 @@ // from the general set of types to the legal set of types for opnds. // Type* getOpndTypeFromLdType(Type* ldType); - // - // Change DefUse so that it does not depend on the Opnd::id - // Use a hashtable instead. - // - int getUniqueId(Opnd *op) { - int id = op->getId(); - if (op->isVarOpnd()) id += (nextSsaOpndId-1); - return id; - } static Opnd* getNullOpnd() {return &_nullOpnd;} VarOpnd* getVarOpnds() {return varOpnds;} uint32 getNumVarOpnds() {return nextVarId;} Index: vm/jitrino/src/optimizer/LiveVarAnalyzer.cpp =================================================================== --- vm/jitrino/src/optimizer/LiveVarAnalyzer.cpp (revision 0) +++ vm/jitrino/src/optimizer/LiveVarAnalyzer.cpp (revision 0) @@ -0,0 +1,293 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @author Pavel A. Ozhdikhin + */ + +#include "optpass.h" +#include "irmanager.h" +#include "ControlFlowGraph.h" +#include "FlowGraph.h" +#include "BitSet.h" +#include "PrintDotFile.h" +#include "Stl.h" + +namespace Jitrino { + +DEFINE_SESSION_ACTION(LivenessAnalysisPass, liveness, "HIR Operand Liveness Analysis") + +// Liveness analysis pass. +// Calculates HIR operand liveness info at the entry of every CFG node. +// Prints liveness info into the dot file if dot printing is enabled. +// Validates liveness in the debug mode with the assertion that there +// are no live operands at CFG entry. +// Add "liveness" pass into the optimizer path to use. For example: +// -XX:jit.SD1_OPT.path.optimizer=liveness,ssa,simplify,... +// +// Does not work on SSA form! +// Currently does not support obtaining the list of live operand objects at CFG node. +// To support the feature we need a single index for SSA and Var opnds and keeping +// the list of operands indexed by its id in the OpndManager. +class LiveVarAnalyzer : public HIRDotPrinter { +public: + LiveVarAnalyzer(MemoryManager& mm, IRManager & irm, SessionAction* sa) + : _memManager(mm), _irManager(irm), + _opndManager(irm.getOpndManager()), + _sa(sa), + _flowgraph(irm.getFlowGraph()), + _numSsaOpnds(_opndManager.getNumSsaOpnds() - 1), + _numOpnds(_numSsaOpnds + _opndManager.getNumVarOpnds()), + _numNodes(_flowgraph.getMaxNodeId()), + _completedNodes(mm, _numNodes), + _currLive(NULL), _livenessIsComputed(false), + HIRDotPrinter(irm.getFlowGraph()) { + _liveAtNodeEntry = new(_memManager) BitSet*[_numNodes]; + } + + // Compute liveness of global operands and variables + void computeLiveness(); + + void computeLiveAtNodeExit(Node* node, BitSet& live); + + // Get bit set of live variables at the node entry + BitSet& getLiveAtNodeEntry(Node *node); + + // Print liveness information + void printLive(std::ostream& os); + void printLiveAtNode(Node* node, std::ostream& os); + void printDotNodeOtherInfo(Node* node); +private: + void processInst(Inst* inst, BitSet& live); + void processNode(Node* node); + + // Setting dead/live bits + inline void markOpndDead(Opnd* opnd, BitSet& live) { + // if (opnd->isGlobal()) + live.setBit(getUniqueOpndId(opnd), false); + } + inline void markOpndLive(Opnd* opnd, BitSet& live) { + // if (opnd->isGlobal()) + live.setBit(getUniqueOpndId(opnd), true); + } + + uint32 getUniqueOpndId(Opnd *op) { + int id = op->getId(); + // Make sure unique ID fits into 32-bit + assert((id & 0xFFFF) >= 0); + if (op->isVarOpnd()) id += _numSsaOpnds; + assert(id >= 0); + return id; + } + + // Memory manager for internal structures + MemoryManager& _memManager; + // IRManager + IRManager& _irManager; + // Flow Graph analyzed for liveness + ControlFlowGraph& _flowgraph; + // Operand manager + OpndManager& _opndManager; + // Array of bit sets of variables live at each node entry + BitSet** _liveAtNodeEntry; + // Number of SSA operands + uint32 _numSsaOpnds; + // Overall number of operands + const uint32 _numOpnds; + // Number of CFG nodes + const uint32 _numNodes; + // Just a flag + bool _livenessChanged; + // Bit set of nodes where liveness is already known + BitSet _completedNodes; + // Temporary storage for liveness info at the node exit. Move to locals??? + BitSet* _currLive; + // Flag to know that liveness info is up to date + bool _livenessIsComputed; + // Parental optimization pass + SessionAction* _sa; +}; + +void LivenessAnalysisPass::_run(IRManager& irm) { + MemoryManager mm("LivenessAnalysisPass::_run"); + LiveVarAnalyzer lva(mm, irm, this); + lva.computeLiveness(); + // Log liveness info + if (Log::isLogEnabled(LogStream::DOTDUMP)) { + printDotFile(irm, getId(), getTagName(), "hir", &lva); + } +} + +// Compute liveness of operands and variables +void LiveVarAnalyzer::computeLiveness() { + + // Initialize array of bit sets for node entry liveness + for (uint32 i = 0; i < _numNodes; i++) { + _liveAtNodeEntry[i] = NULL; + } + + // Create array of nodes. Write nodes in order optimal for + // data flow analysis. + StlVector nodes(_memManager); + _flowgraph.getNodesPostOrder(nodes); + + // Data flow analysis + do { + _livenessChanged = false; + _completedNodes.clear(); + for (StlVector::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) { + Node* node = *it; + assert(node); + if (!_completedNodes.getBit(node->getId())) + processNode(node); + } + } while (_livenessChanged); + _livenessIsComputed = true; + + // Make sure there are no live opnds at CFG entry + assert(getLiveAtNodeEntry(_flowgraph.getEntryNode()).isEmpty()); +} + + +// Process node, i.e., update liveness at node entry +void LiveVarAnalyzer::processNode(Node* node) { + + // Mark node as completed + uint32 nodeId = node->getId(); + _completedNodes.setBit(nodeId, true); + + // Compute liveness at the node exit + if (_currLive == NULL) { + _currLive = new (_memManager) BitSet(_memManager, _numOpnds); + } + computeLiveAtNodeExit(node, *_currLive); + + // Process node's content + // Iterate backward through the instruction list + for (Inst* inst = (Inst*) node->getLastInst(); inst!=NULL; inst = inst->getPrevInst()) { + processInst(inst, *_currLive); + } + + // Decide whether node liveness has changed + bool nodeLivenessChanged = false; + if (_liveAtNodeEntry[nodeId] == NULL) + nodeLivenessChanged = !_currLive->isEmpty(); + else + nodeLivenessChanged = !_currLive->isEqual(*_liveAtNodeEntry[nodeId]); + + // Swap currLive and node's live set + if (_liveAtNodeEntry[nodeId] == NULL || nodeLivenessChanged) { + // Update node's liveness + BitSet* tmp = _currLive; + _currLive = _liveAtNodeEntry[nodeId]; + _liveAtNodeEntry[nodeId] = tmp; + } + + // Mark node's predecessors as not completed + if (nodeLivenessChanged) { + _livenessChanged = true; + // Mark node's predecessors as not completed + const Edges& inEdges = node->getInEdges(); + for(Edges::const_iterator eiter = inEdges.begin(); eiter != inEdges.end(); ++eiter) { + Edge* edge = *eiter; + assert(edge); + Node* srcNode = edge->getSourceNode(); + _completedNodes.setBit(srcNode->getId(), false); + } + } +} + + +// Compute liveness at node exit +void LiveVarAnalyzer::computeLiveAtNodeExit(Node* node, BitSet& live) { + assert(live.getSetSize() == _numOpnds); + uint32 numInputs = 0; + + const Edges& outEdges = node->getOutEdges(); + for (Edges::const_iterator eiter = outEdges.begin(), end = outEdges.end(); eiter!=end; ++eiter) { + Edge* edge = *eiter; + assert(edge); + Node* targetNode = edge->getTargetNode(); + BitSet* succLive = _liveAtNodeEntry[targetNode->getId()]; + if (succLive != NULL) { + if (numInputs == 0) + live.copyFrom(*succLive); + else + live.unionWith(*succLive); + numInputs++; + } + } + if (numInputs == 0) + live.clear(); +} + +// Process instruction, +// i.e., given liveness after instruction compute liveness before instruction +void LiveVarAnalyzer::processInst(Inst *inst, BitSet& live) { + + // Mark destination as dead + markOpndDead(inst->getDst(), live); + + // Mark live variables + uint32 numSrcs = inst->getNumSrcOperands(); + for (uint32 i = 0; i < numSrcs; i++) { + markOpndLive(inst->getSrc(i), live); + } +} + +// Get bit set of live variables at the node entry +BitSet& LiveVarAnalyzer::getLiveAtNodeEntry(Node *node) { + assert(_livenessIsComputed); + assert(_liveAtNodeEntry[node->getId()] != NULL); + return *(_liveAtNodeEntry[node->getId()]); +} + +/*======================================================================*/ +// Print liveness information +// +void LiveVarAnalyzer::printLive(std::ostream& os) { + const Nodes& nodes = _flowgraph.getNodes(); + for(Nodes::const_iterator niter = nodes.begin(); niter != nodes.end(); ++niter) { + Node* node = *niter; + os << "Operands live at entry to "; + FlowGraph::printLabel(os, node); + os << ":\t"; + printLiveAtNode(node, os); + os << std::endl; + } +} + +void LiveVarAnalyzer::printLiveAtNode(Node* node, std::ostream& os) { + BitSet& live = getLiveAtNodeEntry(node); + for (uint32 i = 0; i < live.getSetSize(); i++) { + if (live.getBit(i)) { + if (i <= _numSsaOpnds) os << "t" << i; + else os << "v" << i - _numSsaOpnds; + os << " "; + } + } +} + +void LiveVarAnalyzer::printDotNodeOtherInfo(Node* node) { + std::ostream& out = *os; + out << "\\l|\\" << std::endl; + out << "live: "; + printLiveAtNode(node, out); +} + +} //namespace Jitrino + Index: vm/jitrino/src/optimizer/FlowGraph.cpp =================================================================== --- vm/jitrino/src/optimizer/FlowGraph.cpp (revision 638284) +++ vm/jitrino/src/optimizer/FlowGraph.cpp (working copy) @@ -1207,21 +1207,13 @@ } } -class HIRDotPrinter : public PrintDotFile { -public: - HIRDotPrinter(ControlFlowGraph& _fg) : fg(_fg), loopTree(NULL), dom(NULL){} - void printDotBody(); - void printDotNode(Node* node); - void printDotEdge(Edge* edge); -private: - ControlFlowGraph& fg; - LoopTree* loopTree; - DominatorTree* dom; -}; - -void FlowGraph::printDotFile(ControlFlowGraph& fg, MethodDesc& methodDesc,const char *suffix) { - HIRDotPrinter printer(fg); - printer.printDotFile(methodDesc, suffix); +void FlowGraph::printDotFile(ControlFlowGraph& fg, MethodDesc& methodDesc,const char *suffix, PrintDotFile* dotPrinter) { + if (dotPrinter) { + dotPrinter->printDotFile(methodDesc, suffix); + } else { + HIRDotPrinter printer(fg); + printer.printDotFile(methodDesc, suffix); + } } void HIRDotPrinter::printDotBody() { @@ -1279,6 +1271,8 @@ out << ") "; } + printDotNodeOtherInfo(node); + if (!node->isEmpty()) { out << "\\l|\\" << std::endl; Inst* first = (Inst*)node->getFirstInst(); Index: vm/jitrino/src/optimizer/optpass.h =================================================================== --- vm/jitrino/src/optimizer/optpass.h (revision 638284) +++ vm/jitrino/src/optimizer/optpass.h (working copy) @@ -27,6 +27,7 @@ #include "CompilationContext.h" #include "PMFAction.h" +#include "PrintDotFile.h" #include @@ -49,6 +50,11 @@ virtual const char* getTagName() = 0; // + // Sequential id of the pass + // + const unsigned getId() { return id; } + + // // Run the pass. // void run(); @@ -69,8 +75,8 @@ static void smoothProfile(IRManager& irm); static void printHIR(IRManager& irm); - static void printDotFile(IRManager& irm, int id, const char* name, const char* suffix); - static void printDotFile(IRManager& irm, const char* suffix); + static void printDotFile(IRManager& irm, int id, const char* name, const char* suffix, PrintDotFile* dotPrinter = NULL); + static void printDotFile(IRManager& irm, const char* suffix, PrintDotFile* dotPrinter = NULL); static const char* indent(IRManager& irm); protected: Index: vm/jitrino/src/optimizer/optpass.cpp =================================================================== --- vm/jitrino/src/optimizer/optpass.cpp (revision 638284) +++ vm/jitrino/src/optimizer/optpass.cpp (working copy) @@ -199,16 +199,16 @@ } void -OptPass::printDotFile(IRManager& irm, int id, const char* name, const char* suffix) { +OptPass::printDotFile(IRManager& irm, int id, const char* name, const char* suffix, PrintDotFile* dotPrinter) { char temp[128]; snprintf(temp, sizeof(temp), "%.2i.%s.%s", id, name, suffix); - printDotFile(irm, temp); + printDotFile(irm, temp, dotPrinter); } void -OptPass::printDotFile(IRManager& irm, const char* name) { +OptPass::printDotFile(IRManager& irm, const char* name, PrintDotFile* dotPrinter) { ControlFlowGraph& flowGraph = irm.getFlowGraph(); - FlowGraph::printDotFile(flowGraph, irm.getMethodDesc(), name); + FlowGraph::printDotFile(flowGraph, irm.getMethodDesc(), name, dotPrinter); } const char*