blob: 0b6752db6fbc7f415c55e4e43a14cc98f1769a6e [file] [log] [blame]
/*
* 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 Intel, Pavel A. Ozhdikhin
*
*/
/*
* the following file contains utilities to convert/deconvert to/from SSA form
* for comments on the de-converter, see the second part of this file
*/
#include "Dominator.h"
#include "SSA.h"
#include "Inst.h"
#include "opndmap.h"
#include "walkers.h"
#include "Log.h"
#include "unionfind.h"
#include "BitSet.h"
#include "optimizer.h"
#include "CompilationContext.h"
namespace Jitrino {
DEFINE_SESSION_ACTION(FixupVarsPass, fixupvars, "Fixup SSA Vars")
void
FixupVarsPass::_run(IRManager& irm) {
OptPass::computeDominators(irm);
DominatorTree* dominatorTree = irm.getDominatorTree();
ControlFlowGraph& flowGraph = irm.getFlowGraph();
DomFrontier frontier(irm.getNestedMemoryManager(),*dominatorTree,&flowGraph);
SSABuilder ssaBuilder(irm.getOpndManager(),irm.getInstFactory(),frontier,&flowGraph, irm.getOptimizerFlags());
bool phiInserted = ssaBuilder.fixupVars(&irm.getFlowGraph(), irm.getMethodDesc());
irm.setInSsa(true);
if (phiInserted)
irm.setSsaUpdated();
}
DEFINE_SESSION_ACTION(SSAPass, ssa, "SSA Construction")
void
SSAPass::_run(IRManager& irm) {
OptPass::computeDominators(irm);
DominatorTree* dominatorTree = irm.getDominatorTree();
ControlFlowGraph& flowGraph = irm.getFlowGraph();
DomFrontier frontier(irm.getNestedMemoryManager(),*dominatorTree,&flowGraph);
SSABuilder ssaBuilder(irm.getOpndManager(),irm.getInstFactory(),frontier,&flowGraph, irm.getOptimizerFlags());
ssaBuilder.convertSSA(irm.getMethodDesc());
irm.setInSsa(true);
irm.setSsaUpdated();
}
DEFINE_SESSION_ACTION(DeSSAPass, dessa, "SSA Deconstruction")
void
DeSSAPass::_run(IRManager& irm) {
SSABuilder::deconvertSSA(&irm.getFlowGraph(),irm.getOpndManager());
irm.setInSsa(false);
}
DEFINE_SESSION_ACTION(SplitSSAPass, splitssa, "SSA Variable Web Splitting")
void
SplitSSAPass::_run(IRManager& irm) {
SSABuilder::splitSsaWebs(&irm.getFlowGraph(), irm.getOpndManager());
}
//#define DEBUG_SSA
class RenameStack : public SparseScopedMap<Opnd *, SsaVarOpnd *> {
public:
typedef SparseScopedMap<Opnd *, SsaVarOpnd *> BaseMap;
RenameStack(MemoryManager& mm, U_32 n, const OptimizerFlags& optimizerFlags)
: BaseMap(n, mm,
optimizerFlags.hash_init_factor,
optimizerFlags.hash_resize_factor,
optimizerFlags.hash_resize_to)
{}
~RenameStack()
{};
};
// Checks that phi insts can start from the second or third position only
// and goes in a row
// The first instruction is usually a LabelInst.
// The second one is Phi (if any). But in catch blocks the second inst
// is a CatchInst, and phi may be shifted to the third position.
bool SSABuilder::phiInstsOnRightPositionsInBB(Node* node) {
Inst* inst = (Inst*)node->getSecondInst();
if(inst && !inst->isPhi()) {
// try the next one (third)
inst = inst->getNextInst();
}
// skip all phis
while ( inst!=NULL && inst->isPhi() ) {
inst = inst->getNextInst();
}
// 'true' only if there is no any other phis in the node
while ( inst!=NULL ) {
if(inst->isPhi()) {
return false;
}
inst = inst->getNextInst();
}
return true;
}
//
// find def sites (blocks) of var operand
//
void SSABuilder::findDefSites(DefSites& allDefSites) {
const Nodes& nodes = fg->getNodes();
Nodes::const_iterator niter;
for(niter = nodes.begin(); niter != nodes.end(); ++niter) {
Node* node = *niter;
if (!node->isBlockNode()) continue;
// go over each instruction to find var definition
Inst* first = (Inst*)node->getFirstInst();
for (Inst* inst = first->getNextInst(); inst != NULL; inst = inst->getNextInst()) {
// look for var definitions
if (!inst->isStVar())
continue;
assert(inst->isVarAccess());
// if inst->getVar() return NULL, then inst is accessing SSAOpnd.
// Hence, there is no need to do SSA transformation (addVarDefSite()
// immediately returns.
allDefSites.addVarDefSite(((VarAccessInst*)inst)->getVar(),node);
}
}
}
void SSABuilder::addPhiSrc(PhiInst* i,
SsaVarOpnd* src) {
assert(i->isPhi());
U_32 numOpnds = i->getNumSrcOperands();
for (U_32 j = 0; j < numOpnds; j++) {
// no duplicate SSA opnd
if (i->getSrc(j) == src)
return;
}
instFactory.appendSrc(i, src);
}
// a DomWalker, to be used pre-order
class SsaRenameWalker {
MemoryManager &localMemManager;
SSABuilder *ssaBuilder;
RenameStack *rs;
U_32 n;
const StlVectorSet<VarOpnd *> *whatVars;
const OptimizerFlags& optimizerFlags;
public:
SsaRenameWalker(SSABuilder *builder0,
MemoryManager &localMM,
U_32 num, const OptimizerFlags& _optimizerFlags)
: localMemManager(localMM),
ssaBuilder(builder0),
rs(0),
n(num),
whatVars(0),
optimizerFlags(_optimizerFlags)
{};
void setChangedVarsSet(const StlVectorSet<VarOpnd *> *whatVars0) {
whatVars = whatVars0;
}
void applyToDominatorNode(DominatorNode *domNode) {
ssaBuilder->renameNode(rs, domNode, whatVars);
}
void enterScope() {
if (!rs)
rs = new (localMemManager)
RenameStack(localMemManager, n, optimizerFlags);
rs->enter_scope();
}
void exitScope() {
rs->exit_scope();
};
};
//
// traverse dominator tree and rename variables
//
void SSABuilder::renameNode(RenameStack *renameStack, DominatorNode* dt,
const StlVectorSet<VarOpnd *> *whatVars)
{
if (dt == NULL) return;
Node* node = dt->getNode();
Inst* head = (Inst*)node->getFirstInst();
#ifdef DEBUG_SSA
std::ostream &cout = Log::out();
if (Log::isEnabled()) {
cout << "renameNode "; FlowGraph::printLabel(cout, node); cout << std::endl;
}
#endif
for (Inst* i = head->getNextInst(); i != NULL; i = i->getNextInst()) {
if (!i->isPhi()) {
// replace src with ssa opnd
U_32 nSrcs = i->getNumSrcOperands();
for (U_32 j = 0; j < nSrcs; j++) {
Opnd *srcj = i->getSrc(j);
VarOpnd *srcjVar = (srcj->isSsaVarOpnd()
? srcj->asSsaVarOpnd()->getVar()
: srcj->asVarOpnd());
if (!(srcjVar && !srcjVar->isAddrTaken()))
continue;
if (whatVars && !whatVars->has(srcjVar)) continue;
SsaVarOpnd* ssa = renameStack->lookup(srcjVar);
assert(ssa);
i->setSrc(j,ssa);
}
}
// for both Phi and non-Phi
// we replace any non-ssa dst with a new ssa opnd
// and record it in the RenameStack map
Opnd* dst = i->getDst();
VarOpnd *theVar = dst->asVarOpnd();
if (theVar && (!theVar->isAddrTaken())
&& !(whatVars && !whatVars->has(theVar))) {
SsaVarOpnd* ssaDst = opndManager.createSsaVarOpnd((VarOpnd*)dst);
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
cout << "SSA "; ssaDst->print(cout); cout << ::std::endl;
}
#endif
renameStack->insert((VarOpnd*)dst, ssaDst);
i->setDst(ssaDst);
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
i->print(cout); cout << ::std::endl;
}
#endif
// record stVar inst
} else if (dst->isSsaVarOpnd()) {
SsaVarOpnd* ssaDst = dst->asSsaVarOpnd();
theVar = ssaDst->getVar();
if (whatVars && !whatVars->has(theVar))
continue;
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
cout << "SSA "; ssaDst->print(cout); cout << ::std::endl;
}
#endif
renameStack->insert(ssaDst->getVar(), ssaDst);
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
i->print(cout); cout << ::std::endl;
}
#endif
}
}
// add var sources to following phi instructions
const Edges& edges = node->getOutEdges();
Edges::const_iterator
eiter = edges.begin(),
eend = edges.end();
for(eiter = edges.begin(); eiter != eend; ++eiter) {
Edge* e = *eiter;
Node* succ = e->getTargetNode();
// Phi insts are inserted to the beginning of the block
// if succ does not have phi insts, then we can skip it
Inst *phi = (Inst*)succ->getSecondInst();
if (phi==NULL || !phi->isPhi()) continue;
// node is jth predecessor for succ
// replace jth var of phi insts
Inst* nextphi = phi->getNextInst();
for (;phi!=NULL && phi->isPhi(); phi = nextphi) {
nextphi = phi->getNextInst();
// get var
Opnd *theopnd = phi->getDst();
VarOpnd *thevar = theopnd->asVarOpnd();
if (!thevar) {
SsaVarOpnd *theSsaVar = theopnd->asSsaVarOpnd();
assert(theSsaVar);
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "case 2" << ::std::endl;
}
#endif
thevar = theSsaVar->getVar();
}
if (whatVars && !whatVars->has(thevar)) continue;
SsaVarOpnd* ssa = renameStack->lookup((VarOpnd*)thevar);
if (ssa != NULL) {
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
cout << "redge";
// cout << (I_32)j;
cout << " with ssa "; ssa->print(cout); cout << ::std::endl;
}
#endif
addPhiSrc((PhiInst*)phi,ssa);
} else {
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
cout << "no source for phi of var ";
thevar->print(cout);
cout << ::std::endl;
}
#endif
// if ssa is NULL, then the phi must be a dead phi inst
// (no more use of var afterwards). it will be removed.
}
}
}
}
void SSABuilder::insertPhi(DefSites& allDefSites) {
HashTableIter<void,VarDefSites> tableIter(allDefSites.getVarDefSites());
void* var;
VarDefSites* defSites;
while (tableIter.getNextElem(var,defSites)) {
VarOpnd* varOpnd = (VarOpnd*)var;
//
// skip var opnd whose address has been taken
//
if (varOpnd->isAddrTaken())
continue;
Node* node;
Node *returnNode = fg->getReturnNode();
Node *unwindNode = fg->getUnwindNode();
Node *exitNode = fg->getExitNode();
while ((node = defSites->removeDefSite()) != NULL) {
List<Node>* df = frontier.getFrontiersOf(node);
for (; df != NULL; df = df->getNext()) {
// block where phi inst is going to be inserted
Node* insertedLoc = df->getElem();
// if phi has been inserted, then skip
// no need to insert phi inst in the epilog,
// return node, or unwind node because
// there is no var use in these nodes
if (!defSites->beenInsertedPhi(insertedLoc) &&
!((insertedLoc == unwindNode) ||
(insertedLoc == returnNode) ||
(insertedLoc == exitNode))) {
// create a new phi instruction for varOpnd
createPhiInst(varOpnd,insertedLoc);
// record phi(var) inst is created in df->getElem() block
defSites->insertPhiSite(insertedLoc);
}
}
}
}
}
void SSABuilder::createPhiInst(VarOpnd* var, Node* insertedLoc) {
// create phi instruction
Inst* phi = instFactory.makePhi(var,0, 0);
// insert to the beginning of insertedLoc
insertedLoc->prependInst(phi);
createPhi = true;
}
// this is the main function which returns true if a PHI node has been inserted
bool SSABuilder::convertSSA(MethodDesc& methodDesc) {
// create structure recording the blocks that define var opnds
MemoryManager ssaMemManager("SSABuilder::convertSSA.ssaMemManager");
DefSites allDefSites(ssaMemManager,frontier.getNumNodes());
// find out where vars are defined
findDefSites(allDefSites);
// insert phi nodes
insertPhi(allDefSites);
// rename
SsaRenameWalker renameWalker(this, ssaMemManager,
optimizerFlags.hash_node_var_factor *
(U_32) fg->getNodes().size(), optimizerFlags);
DomTreeWalk<true, // pre-order
SsaRenameWalker>(frontier.getDominator(), renameWalker, ssaMemManager);
return createPhi;
}
// a NodeWalker
class ClearPhiSrcsWalker {
SSABuilder *thePass;
const StlVectorSet<VarOpnd *> *whatVars;
public:
void applyToCFGNode(Node *node) { thePass->clearPhiSrcs(node,
whatVars); };
ClearPhiSrcsWalker(SSABuilder *thePass0)
: thePass(thePass0),
whatVars(0)
{
};
void setChangedVarsSet(const StlVectorSet<VarOpnd *> *changedVarsSet) {
whatVars = changedVarsSet;
}
};
// a NodeWalker
class ClearPhiSrcsWalker2 {
SSABuilder *thePass;
const StlVectorSet<VarOpnd *> *oldVars; // vars of interest
StlVector<VarOpnd *> *newVars; // vars which we have changed
const StlVectorSet<Opnd *> *removedVars; // vars defined by removed Phis
StlVector<Node *> &tmpNodeList;
public:
void applyToCFGNode(Node *node) { thePass->clearPhiSrcs2(node,
oldVars,
newVars,
removedVars,
tmpNodeList); };
ClearPhiSrcsWalker2(SSABuilder *thePass0,
const StlVectorSet<VarOpnd *> *oldVars0,
StlVector<VarOpnd *> *newVars0,
const StlVectorSet<Opnd *> *removedVars0,
StlVector<Node *> &tmpNodeList0)
: thePass(thePass0),
oldVars(oldVars0), newVars(newVars0),
removedVars(removedVars0),
tmpNodeList(tmpNodeList0)
{
};
void setOldVars(const StlVectorSet<VarOpnd *> *vars) {
oldVars = vars;
}
void setNewVars(StlVector<VarOpnd *> *vars) {
newVars = vars;
}
void setRemovedVars(const StlVectorSet<Opnd *> *vars) {
removedVars = vars;
}
};
void SSABuilder::clearPhiSrcs(Node *node, const StlVectorSet<VarOpnd *> *whatVars)
{
Inst* phi = (Inst*)node->getSecondInst();
if (whatVars) {
for (;phi!=NULL && phi->isPhi(); phi = phi->getNextInst()) {
Opnd *dstOp = phi->getDst();
VarOpnd *varOpnd = dstOp->asVarOpnd();
if (!varOpnd) {
SsaVarOpnd *ssaOpnd = dstOp->asSsaVarOpnd();
assert(ssaOpnd);
varOpnd = ssaOpnd->getVar();
}
if (whatVars->has(varOpnd)) {
PhiInst *phiInst = phi->asPhiInst();
assert(phiInst);
phiInst->setNumSrcs(0);
}
}
} else {
for (;phi!=NULL && phi->isPhi(); phi = phi->getNextInst()) {
PhiInst *phiInst = phi->asPhiInst();
assert(phiInst);
phiInst->setNumSrcs(0);
}
}
}
void SSABuilder::clearPhiSrcs2(Node *node,
const StlVectorSet<VarOpnd *> *whatVars,
StlVector<VarOpnd *> *changedVars,
const StlVectorSet<Opnd *> *removedVars,
StlVector<Node *> &scratchNodeList)
{
bool needPreds = true;
StlVector<Node *> &preds = scratchNodeList;
Inst* inst = (Inst*)node->getSecondInst();
for (;inst!=NULL && inst->isPhi(); inst = inst->getNextInst()) {
Opnd *dstOp =inst->getDst();
VarOpnd *varOpnd = 0;
if (whatVars) {
varOpnd = dstOp->asVarOpnd();
if (!varOpnd) {
SsaVarOpnd *ssaOpnd = dstOp->asSsaVarOpnd();
assert(ssaOpnd);
varOpnd = ssaOpnd->getVar();
}
if (!whatVars->has(varOpnd))
continue;
}
bool changed=false;
U_32 numSrcs = inst->getNumSrcOperands();
for (U_32 i=0; i<numSrcs; ++i) {
Opnd *thisOpnd = inst->getSrc(i);
if (!(removedVars && removedVars->has(thisOpnd))) {
// need to test whether need to remove
if (needPreds) {
needPreds = false;
const Edges& edges2 = node->getInEdges();
preds.clear();
preds.reserve(edges2.size());
Edges::const_iterator eiter2;
for(eiter2 = edges2.begin(); eiter2 != edges2.end(); ++eiter2){
preds.push_back((*eiter2)->getSourceNode());
}
}
DominatorTree &domTree = frontier.getDominator();
Inst *thisOpndInst = thisOpnd->getInst();
Node *thisOpndInstNode = thisOpndInst->getNode();
if (thisOpndInstNode) {
// the operand's source instruction was not already dead.
StlVector<Node *>::const_iterator
predIter = preds.begin(),
predEnd = preds.end();
bool foundDom = false;
for ( ; predIter != predEnd; ++predIter) {
Node *predNode = *predIter;
if (domTree.dominates(thisOpndInstNode, predNode)) {
// we found it, leave this operand alone.
foundDom = true;
break;
}
}
if (foundDom) continue; // leave operand alone.
}
}
// remove this operand;
if (i < numSrcs-1) {
inst->setSrc(i, inst->getSrc(numSrcs-1));
--i; // re-examine this operand, which is now the last
}
--numSrcs; // we deleted one operand
PhiInst *phiInst = inst->asPhiInst();
assert(phiInst);
phiInst->setNumSrcs(numSrcs);
changed = true;
}
if (changed) {
// note the changed var;
if (!varOpnd) {
varOpnd = dstOp->asVarOpnd();
if (!varOpnd) {
SsaVarOpnd *ssaOpnd = dstOp->asSsaVarOpnd();
assert(ssaOpnd);
varOpnd = ssaOpnd->getVar();
}
}
changedVars->push_back(varOpnd);
}
}
}
// a NodeWalker
class CheckForTrivialPhisWalker {
SSABuilder *thePass;
MemoryManager &localMemManager;
bool removedPhi;
bool removedPhiRecently;
StlVector<VarOpnd *> changedVars;
StlVectorSet<VarOpnd *> changedVarsSet;
public:
void applyToCFGNode(Node *node) {
if (thePass->checkForTrivialPhis(node, changedVars)) {
removedPhi = true;
removedPhiRecently = true;
}
};
CheckForTrivialPhisWalker(SSABuilder *thePass0,
MemoryManager &localMM)
: thePass(thePass0), localMemManager(localMM),
removedPhi(false), removedPhiRecently(false), changedVars(localMM),
changedVarsSet(localMM)
{
};
bool foundTrivial() { return removedPhi; }
void resetFound() { removedPhi = false; changedVars.clear(); changedVarsSet.clear(); }
const StlVectorSet<VarOpnd *> *getChangedVarsSet() {
if (changedVars.empty()) {
return 0;
} else {
if (removedPhiRecently) {
changedVarsSet.clear();
changedVarsSet.insert(changedVars.begin(), changedVars.end());
removedPhiRecently = false;
}
return &changedVarsSet;
}
};
};
bool SSABuilder::checkForTrivialPhis(Node *node,
StlVector<VarOpnd *> &changedVars)
{
// Check that phi insts can start from the second or third position only
// and goes in a row
assert(phiInstsOnRightPositionsInBB(node));
Inst* phi = (Inst*)node->getSecondInst();
if(phi && !phi->isPhi()) {
// try the next one (third)
phi = phi->getNextInst();
}
bool removedPhi = false;
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "Checking node " << (int)node->getId()
<< " for trivial phis" << ::std::endl;
}
#endif
Inst* nextphi = NULL;
for (;phi!=NULL && phi->isPhi(); phi = nextphi) {
nextphi = phi->getNextInst();
#ifdef _DEBUG
PhiInst *phiInst = phi->asPhiInst();
assert(phiInst);
#endif
U_32 nSrcs = phi->getNumSrcOperands();
if (nSrcs <= 1) {
// phi must be trivial
#ifdef DEBUG_SSA
::std::ostream &cout = Log::out();
if (Log::isEnabled()) {
cout << "removing trivial instruction "; phi->print(cout); cout << ::std::endl;
}
#endif
Opnd *dstOp = phi->getDst();
VarOpnd *varOp = dstOp->asVarOpnd();
if (!varOp) {
SsaVarOpnd *ssaOp = dstOp->asSsaVarOpnd();
assert(ssaOp);
varOp = ssaOp->getVar();
}
changedVars.push_back(varOp);
removedPhi = true;
phi->unlink();
}
}
return removedPhi;
}
// a NodeWalker
class CheckForTrivialPhisWalker2 {
SSABuilder *thePass;
const StlVectorSet<VarOpnd *> *lookatVars; // vars to look at
StlVector<VarOpnd *> *changedVars; // note which vars are changed
StlVector<Opnd *> *removedVars; // record dst of removed Phis
public:
void applyToCFGNode(Node *node) {
thePass->checkForTrivialPhis2(node, lookatVars, changedVars,
removedVars);
};
CheckForTrivialPhisWalker2(SSABuilder *thePass0,
StlVectorSet<VarOpnd *> *lookatVars0,
StlVector<VarOpnd *> *changedVars0,
StlVector<Opnd *> *removedVars0)
: thePass(thePass0),
lookatVars(lookatVars0), changedVars(changedVars0),
removedVars(removedVars0)
{
};
void setLookatVars(const StlVectorSet<VarOpnd *> *vars) { lookatVars = vars; }
void setChangedVars(StlVector<VarOpnd *> *vars) { changedVars = vars; }
};
void SSABuilder::checkForTrivialPhis2(Node *node,
const StlVectorSet<VarOpnd *> *lookatVars,
StlVector<VarOpnd *> *changedVars,
StlVector<Opnd *> *removedVars)
{
// Check that phi insts can start from the second or third position only
// and goes in a row
assert(phiInstsOnRightPositionsInBB(node));
Inst* phi = (Inst*)node->getSecondInst();
if(phi && !phi->isPhi()) {
// try the next one (third)
phi = phi->getNextInst();
}
Inst *nextphi = NULL;
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "Checking node " << (int)node->getId()
<< " for trivial phis2" << ::std::endl;
}
#endif
for (;phi->isPhi(); phi = nextphi) {
nextphi = phi->getNextInst();
#ifdef _DEBUG
PhiInst *phiInst = phi->asPhiInst();
assert(phiInst);
#endif
U_32 nSrcs = phi->getNumSrcOperands();
if (nSrcs <= 1) {
// phi must be trivial
#ifdef DEBUG_SSA
::std::ostream &cout = Log::out();
if (Log::isEnabled()) {
cout << "removing trivial2 instruction "; phi->print(cout); cout << ::std::endl;
}
#endif
Opnd *dstOp = phi->getDst();
VarOpnd *varOp = dstOp->asVarOpnd();
if (!varOp) {
SsaVarOpnd *ssaOp = dstOp->asSsaVarOpnd();
assert(ssaOp);
varOp = ssaOp->getVar();
}
assert(!lookatVars || (lookatVars->has(varOp)));
changedVars->push_back(varOp);
removedVars->push_back(dstOp);
phi->unlink();
}
}
}
bool SSABuilder::fixupSSA(MethodDesc& methodDesc, bool useBetter) {
// clear out all Phi args
MemoryManager localMM("SSABuilder::fixupSSA::memManager");
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "Starting fixupSSA" << ::std::endl;
FlowGraph::printDotFile(*fg, methodDesc, "midfixup-0");
}
#endif
if (useBetter) {
StlVector<VarOpnd *> newChangedVars(localMM);
StlVector<Opnd *> removedVars(localMM);
StlVector<Node *> tmpNodeList(localMM);
// first cleanup trivial Phis to avoid mess later
CheckForTrivialPhisWalker2 checkTrivialWalker(this,
0,
&newChangedVars,
&removedVars);
NodeWalk<CheckForTrivialPhisWalker2>(*fg, checkTrivialWalker);
StlVectorSet<Opnd *> removedVarsSet(localMM);
removedVarsSet.insert(removedVars.begin(), removedVars.end());
// checks for vars which don't dominate an in-edge of the phi
ClearPhiSrcsWalker2 clearPhisWalker2(this,
0,
&newChangedVars,
&removedVarsSet,
tmpNodeList);
NodeWalk<ClearPhiSrcsWalker2>(*fg, clearPhisWalker2);
if (newChangedVars.empty()) {
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "newChangedVars is empty" << ::std::endl;
}
#endif
return false; // nothing changed, should still be non-trivial
}
// do renaming to propagate vars to Phis which had vars removed
SsaRenameWalker renameWalker(this, localMM,
optimizerFlags.hash_node_var_factor *
(U_32) fg->getNodes().size(), optimizerFlags);
// oldChangedVarsSet will be the set for looking up Vars to consider
StlVectorSet<VarOpnd *> oldChangedVarsSet(localMM);
oldChangedVarsSet.insert(newChangedVars.begin(), newChangedVars.end());
renameWalker.setChangedVarsSet(&oldChangedVarsSet);
DomTreeWalk<true, // preorder
SsaRenameWalker>(frontier.getDominator(), renameWalker, localMM);
// check for Phis which are now trivial, but only need to look at oldChangedVarsSet
newChangedVars.clear(); // note that the checkTrivialWalker updates newChangedVars
checkTrivialWalker.setLookatVars(&oldChangedVarsSet);
NodeWalk<CheckForTrivialPhisWalker2>(*fg, checkTrivialWalker);
while (!newChangedVars.empty()) {
// lather, rinse, repeat
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "beginning iteration" << ::std::endl;
}
#endif
oldChangedVarsSet.clear();
oldChangedVarsSet.insert(newChangedVars.begin(), newChangedVars.end());
newChangedVars.clear();
// clear phis with unreaching parameters
clearPhisWalker2.setOldVars(&oldChangedVarsSet);
NodeWalk<ClearPhiSrcsWalker2>(*fg, clearPhisWalker2);
if (newChangedVars.empty()) {
return true; // nothing changed, still non-trivial
}
// rename just newVars
DomTreeWalk<true, // pre-order
SsaRenameWalker>(frontier.getDominator(), renameWalker,
localMM);
oldChangedVarsSet.clear();
oldChangedVarsSet.insert(newChangedVars.begin(), newChangedVars.end());
newChangedVars.clear();
// check oldChangedVars for trivial Phis
NodeWalk<CheckForTrivialPhisWalker2>(*fg, checkTrivialWalker);
}
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "done iteration" << ::std::endl;
}
#endif
} else {
// old algorithm
ClearPhiSrcsWalker clearPhisWalker(this);
NodeWalk<ClearPhiSrcsWalker>(*fg, clearPhisWalker);
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
FlowGraph::printDotFile(*fg, methodDesc, "midfixup-1");
}
#endif
SsaRenameWalker renameWalker(this, localMM,
optimizerFlags.hash_node_var_factor *
(I_32) fg->getNodes().size(), optimizerFlags);
DomTreeWalk<true, // pre-order
SsaRenameWalker>(frontier.getDominator(), renameWalker,
localMM);
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
FlowGraph::printDotFile(*fg, methodDesc, "midfixup-2");
}
#endif
CheckForTrivialPhisWalker checkTrivialWalker(this, localMM);
NodeWalk<CheckForTrivialPhisWalker>(*fg, checkTrivialWalker);
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "before iteration" << ::std::endl;
}
#endif
while (checkTrivialWalker.foundTrivial()) {
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "starting iteration" << ::std::endl;
}
#endif
// we deleted some Phi instruction(s)
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
FlowGraph::printDotFile(*fg, methodDesc, "midfixup-3");
}
#endif
// clear all other Phis for that var(s)
clearPhisWalker.setChangedVarsSet(checkTrivialWalker.getChangedVarsSet());
NodeWalk<ClearPhiSrcsWalker>(*fg, clearPhisWalker);
// do renaming for that var(s)
renameWalker.setChangedVarsSet(checkTrivialWalker.getChangedVarsSet());
DomTreeWalk<true, // pre-order
SsaRenameWalker>(frontier.getDominator(), renameWalker,
localMM);
// check again
checkTrivialWalker.resetFound();
NodeWalk<CheckForTrivialPhisWalker>(*fg, checkTrivialWalker);
}
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "done iteration" << ::std::endl;
}
#endif
}
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
FlowGraph::printDotFile(*fg, methodDesc, "midfixup-4");
Log::out() << "Finished fixupSSA" << ::std::endl;
}
#endif
return true;
}
// an InstWalker, arbitrary order
class ScanSsaVarsWalker {
SSABuilder *thePass;
StlVectorSet<VarOpnd *> &usedOutOfSsa;
public:
ScanSsaVarsWalker(SSABuilder *from,
StlVectorSet<VarOpnd *> &usedOutOfSsa0)
: thePass(from),
usedOutOfSsa(usedOutOfSsa0)
{
};
void recordOpnd(Opnd *thisOpnd) {
assert(thisOpnd);
if (thisOpnd->isSsaVarOpnd()) {
SsaVarOpnd *ssaOpnd = thisOpnd->asSsaVarOpnd();
ssaOpnd->getVar();
} else if (thisOpnd->isVarOpnd()) {
VarOpnd *var = thisOpnd->asVarOpnd();
usedOutOfSsa.insert(var);
}
};
void applyToInst(Inst *inst) {
U_32 numSrcs = inst->getNumSrcOperands();
for (U_32 i=0; i<numSrcs; ++i) {
Opnd *thisOpnd = inst->getSrc(i);
recordOpnd(thisOpnd);
}
Opnd *dstOpnd = inst->getDst();
recordOpnd(dstOpnd);
}
};
// an InstWalker, arbitrary order
class DeSsaVarsWalker {
SSABuilder *thePass;
StlVectorSet<VarOpnd *> &usedOutOfSsa;
public:
DeSsaVarsWalker(SSABuilder *from,
StlVectorSet<VarOpnd *> &usedOutOfSsa0)
: thePass(from),
usedOutOfSsa(usedOutOfSsa0)
{
};
VarOpnd *needToDeSsaOpnd(Opnd *thisOpnd) {
assert(thisOpnd);
if (thisOpnd->isSsaVarOpnd()) {
SsaVarOpnd *ssaOpnd = thisOpnd->asSsaVarOpnd();
VarOpnd *var = ssaOpnd->getVar();
if (usedOutOfSsa.has(var)) {
return var;
}
}
return 0;
};
void applyToInst(Inst *inst) {
if (inst->getOpcode() == Op_Phi) {
// we should just delete the whole thing.
Opnd *dstOpnd = inst->getDst();
if (dstOpnd->isSsaVarOpnd()) {
SsaVarOpnd *ssaOpnd = dstOpnd->asSsaVarOpnd();
VarOpnd *var = ssaOpnd->getVar();
if (usedOutOfSsa.has(var)) {
// remove instruction
inst->unlink();
return;
}
} else if (dstOpnd->isVarOpnd()) {
VarOpnd *var = dstOpnd->asVarOpnd();
if (usedOutOfSsa.has(var)) {
// remove instruction
inst->unlink();
return;
}
}
}
U_32 numSrcs = inst->getNumSrcOperands();
for (U_32 i=0; i<numSrcs; ++i) {
Opnd *thisOpnd = inst->getSrc(i);
VarOpnd *varOpnd = needToDeSsaOpnd(thisOpnd);
if (varOpnd) {
inst->setSrc(i, varOpnd);
}
}
Opnd *dstOpnd = inst->getDst();
VarOpnd *varDstOpnd = needToDeSsaOpnd(dstOpnd);
if (varDstOpnd) {
inst->setDst(varDstOpnd);
}
}
};
// check for new Vars which are not in SSA form; if there are any, fix up all occurrences of
// that var, put into SSA form.
bool SSABuilder::fixupVars(ControlFlowGraph*fg, MethodDesc& methodDesc) {
// clear out all Phi args
MemoryManager localMM("SSABuilder::fixupVars::memManager");
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
Log::out() << "Starting fixupVars" << ::std::endl;
FlowGraph::printDotFile(*fg, methodDesc, "midfixupvars-0");
}
#endif
StlVectorSet<VarOpnd *> usedOutOfSsa(localMM);
ScanSsaVarsWalker firstWalker(this, usedOutOfSsa);
typedef Inst2NodeWalker<true, ScanSsaVarsWalker> ScanSsaVarsNodeWalker;
ScanSsaVarsNodeWalker scanSsaVarsNodeWalker(firstWalker);
NodeWalk<ScanSsaVarsNodeWalker>(*fg, scanSsaVarsNodeWalker);
// now usedOutOfSsa has vars that need to be fixed up
if (!usedOutOfSsa.empty()) {
// cleanup in case there are any other uses still.
DeSsaVarsWalker cleanupWalker(this, usedOutOfSsa);
typedef Inst2NodeWalker<true, DeSsaVarsWalker> DeSsaVarsNodeWalker;
DeSsaVarsNodeWalker cleanupNodeWalker(cleanupWalker);
NodeWalk<DeSsaVarsNodeWalker>(*fg, cleanupNodeWalker);
// now all vars in usedOutOfSsa are not used in SSA form, and have no Phis.
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
FlowGraph::printDotFile(*fg,methodDesc, "midfixupvars-1");
}
#endif
// just run the conversion pass
convertSSA(methodDesc);
}
#ifdef DEBUG_SSA
if (Log::isEnabled()) {
FlowGraph::printDotFile(*fg, methodDesc, "midfixupvars-3");
Log::out() << "Finished fixupVars" << ::std::endl;
}
#endif
return !usedOutOfSsa.empty();
}
/* END OF CONVERTER ********************************************************************************/
/* the second part of this file contains the de-converter.
* CAUTION: the de-converter is currently very simple, assumes that the
* live ranges of SSA variables never overlap, which could be caused
* by optimizations such as copy propagation and code motion.
*/
void SSABuilder::deconvertSSA(ControlFlowGraph* fg,OpndManager& opndManager) {
const Nodes& nodes = fg->getNodes();
Nodes::const_iterator niter;
for(niter = nodes.begin(); niter != nodes.end(); ++niter) {
Node* node = *niter;
Inst *headInst = (Inst*)node->getFirstInst();
for (Inst *inst = headInst->getNextInst(); inst != NULL; ) {
Inst *nextInst = inst->getNextInst();
if (inst->isPhi()) {
inst->unlink();
} else {
for (U_32 i = 0; i < inst->getNumSrcOperands(); i++) {
Opnd *opnd = inst->getSrc(i);
if (opnd->isSsaVarOpnd()) {
SsaVarOpnd *ssa = (SsaVarOpnd *)opnd;
VarOpnd *var = ssa->getVar();
inst->setSrc(i,var);
} else if (opnd->isVarOpnd()) {
}
}
Opnd *dst = inst->getDst();
if (dst->isSsaVarOpnd()) {
SsaVarOpnd *ssa = (SsaVarOpnd *)dst;
inst->setDst(ssa->getVar());
}
}
inst = nextInst;
}
}
}
struct SsaVarClique : private UnionFind {
VarOpnd *var;
SsaVarClique(VarOpnd *var0) : var(var0) {};
SsaVarClique() : var(0) {};
SsaVarClique *getRoot() {
UnionFind *root = find();
return (SsaVarClique *) root;
}
void link(SsaVarClique *other) {
UnionFind::link(other);
}
};
// rename vars to make un-overlapping live ranges of a variable into
// different variables.
void SSABuilder::splitSsaWebs(ControlFlowGraph* fg,OpndManager& opndManager) {
U_32 numSsaOpnds = opndManager.getNumSsaOpnds();
MemoryManager localMM("SSABuilder::splitSsaWebs::memManager");
SsaVarClique *cliques = new (localMM) SsaVarClique[numSsaOpnds];
const Nodes& nodes = fg->getNodes();
Nodes::const_iterator niter;
for(niter = nodes.begin(); niter != nodes.end(); ++niter) {
Node* node = *niter;
Inst *headInst = (Inst*)node->getFirstInst();
for (Inst *inst = headInst->getNextInst(); inst != NULL; ) {
Inst *nextInst = inst->getNextInst();
if (inst->isPhi()) {
// do something
VarOpnd *var0 = 0;
SsaVarClique *clique = 0;
for (U_32 i = 0; i < inst->getNumSrcOperands(); i++) {
Opnd *opnd = inst->getSrc(i);
if (opnd->isSsaVarOpnd()) {
SsaVarOpnd *ssa = (SsaVarOpnd *)opnd;
U_32 id = ssa->getId();
if (var0) {
assert(ssa->getVar()==var0);
cliques[id].link(clique);
} else {
var0 = ssa->getVar();
clique = &cliques[id];
}
}
}
Opnd *dst = inst->getDst();
if (dst->isSsaVarOpnd()) {
SsaVarOpnd *ssa = (SsaVarOpnd *)dst;
ssa->getVar();
U_32 id = ssa->getId();
if (var0) {
assert(ssa->getVar()==var0);
cliques[id].link(clique);
} else {
var0 = ssa->getVar();
clique = &cliques[id];
}
}
}
inst = nextInst;
}
}
U_32 numvars = opndManager.getNumVarOpnds();
bool *used = new (localMM) bool[numvars];
for (U_32 i=0; i<numvars; i++) {
used[i] = false;
}
for(niter = nodes.begin(); niter != nodes.end(); ++niter) {
Node* node = *niter;
Inst *headInst = (Inst*)node->getFirstInst();
for (Inst *inst = headInst->getNextInst(); inst != NULL; ) {
Inst *nextInst = inst->getNextInst();
for (U_32 i = 0; i < inst->getNumSrcOperands(); i++) {
Opnd *opnd = inst->getSrc(i);
if (opnd->isSsaVarOpnd()) {
SsaVarOpnd *ssa = (SsaVarOpnd *)opnd;
VarOpnd *var = ssa->getVar();
U_32 id=ssa->getId();
SsaVarClique *clique = &cliques[id];
clique = clique->getRoot();
VarOpnd *cvar = clique->var;
if (cvar == 0) {
U_32 varId = var->getId();
if (used[varId]) {
cvar = opndManager.createVarOpnd(var->getType(),
var->isPinned());
} else {
cvar = var;
used[varId] = true;
}
clique->var = cvar;
}
if (cvar != var) {
ssa->setVar(cvar);
}
}
}
Opnd *dst = inst->getDst();
if (dst->isSsaVarOpnd()) {
SsaVarOpnd *ssa = (SsaVarOpnd *)dst;
VarOpnd *var = ssa->getVar();
U_32 id=ssa->getId();
SsaVarClique *clique = &cliques[id];
clique = clique->getRoot();
VarOpnd *cvar = clique->var;
if (cvar == 0) {
U_32 varId = var->getId();
if (used[varId]) {
cvar = opndManager.createVarOpnd(var->getType(),
var->isPinned());
} else {
cvar = var;
used[varId] = true;
}
clique->var = cvar;
}
if (cvar != var) {
ssa->setVar(cvar);
}
}
inst = nextInst;
}
}
}
} //namespace Jitrino