blob: 7085893cb43566a4a2edd5cdbfae0686eca8de99 [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
*
*/
#include "deadcodeeliminator.h"
#include "irmanager.h"
#include "Inst.h"
#include "Stl.h"
#include "BitSet.h"
#include "MemoryManager.h"
#include "Log.h"
#include "constantfolder.h"
#include "optimizer.h"
#include "XTimer.h"
#include "PMFAction.h"
#include "Dominator.h"
#include "FlowGraph.h"
namespace Jitrino {
// Timer.h includes windows.h, which has a broken #def of min:
#undef min
DEFINE_SESSION_ACTION(DeadCodeEliminationPass, dce, "Dead Code Elimination")
void
DeadCodeEliminationPass::_run(IRManager& irm){
DeadCodeEliminator dce(irm);
{
//create loops mapping to be able to fix infinite loops after the optimization
MemoryManager tmpMM("HashValueNumberingPass::_run");
InfiniteLoopsInfo loopsMapping(tmpMM);
DeadCodeEliminator::createLoop2DispatchMapping(irm.getFlowGraph(), loopsMapping);
//perform optimization
dce.eliminateDeadCode(false);
//fix infinite loops if found
DeadCodeEliminator::fixInfiniteLoops(irm, loopsMapping);
}
}
DEFINE_SESSION_ACTION(UnreachableCodeEliminationPass, uce, "Unreachable Code Elimination");
void
UnreachableCodeEliminationPass::_run(IRManager& irm) {
DeadCodeEliminator dce(irm);
{
dce.eliminateUnreachableCode();
}
bool fixup_ssa = irm.getOptimizerFlags().fixup_ssa;
if (irm.getInSsa() && fixup_ssa) {
OptPass::fixupSsa(irm);
}
}
DEFINE_SESSION_ACTION(ExtraPseudoThrowRemovalPass, rept, "Removal of Extra PseudoThrow Instructions");
void
ExtraPseudoThrowRemovalPass::_run(IRManager& irm) {
DeadCodeEliminator dce(irm);
dce.removeExtraPseudoThrow();
}
//Empty Node Removal
class PurgeEmptyNodesPass: public SessionAction {
public:
void run();
};
ActionFactory<PurgeEmptyNodesPass> _purge("purge");
void
PurgeEmptyNodesPass::run() {
IRManager& irm = *getCompilationContext()->getHIRManager();
irm.getFlowGraph().purgeEmptyNodes(false);
}
//
// Returns true if an instruction is a non-essential instruction
// Essential instructions are exception throwing instructions, calls,
// returns, stores, and branches. Once we have control dependence
// information branches are non-essential.
//
static bool
isNonEssential(Inst* inst) {
Operation operation = inst->getOperation();
if (operation.isNonEssential()) {
return true;
} else {
Opcode opCode = inst->getOpcode();
if (opCode == Op_StVar) {
// StVar is non-essential if the variable to which it stores is in SSA form
return inst->getDst()->isSsaVarOpnd();
} else if (opCode == Op_NewObj) {
Type* dstType = inst->getDst()->getType();
assert(!dstType->isUnresolvedObject());
// Objects that have finalizers must not be swept
return !dstType->asNamedType()->isFinalizable();
}
return false;
}
}
static U_8
getBitWidth(Type::Tag tag)
{
switch (tag) {
case Type::Void: return 0;
case Type::Boolean: return 1;
case Type::Char: return 16;
case Type::Int8: return 8;
case Type::Int16: return 16;
case Type::Int32: return 32;
case Type::Int64: return 64;
case Type::UInt8: return 8;
case Type::UInt16: return 16;
case Type::UInt32: return 32;
case Type::UInt64: return 64;
default: return 0xff; // uses all;
}
}
// returns the number of bits of the given opnd_num which is used,
// given that dst_bits of the dst are used
static U_8
usesBitsOfOpnd(U_8 dst_bits, Inst* inst, U_32 opnd_num) {
U_8 opwidth = getBitWidth(inst->getType());
Opnd *opnd = inst->getSrc(opnd_num);
U_8 opndwidth = getBitWidth(opnd->getType()->tag);
U_8 res = opndwidth;
switch (inst->getOpcode()) {
case Op_Add: case Op_Mul: case Op_Sub:
res = ::std::min(dst_bits, opwidth); break;
case Op_TauDiv: case Op_TauRem:
res = dst_bits; break;
case Op_Neg:
res = ::std::min(dst_bits, opwidth); break;
case Op_MulHi:
res = opwidth; break;
case Op_Min: case Op_Max: case Op_Abs:
case Op_And: case Op_Or: case Op_Xor: case Op_Not:
res = ::std::min(dst_bits, opwidth); break;
case Op_Select:
if (opnd_num == 0) res = opndwidth;
else res = dst_bits;
break;
case Op_Conv:
res = ::std::min(dst_bits, opwidth); break;
case Op_Shladd:
{
switch (opnd_num) {
case 0:
{
Opnd *shiftAmount = inst->getSrc(1);
I_32 shiftby;
bool isconst =
ConstantFolder::isConstant(shiftAmount->getInst(),
shiftby);
if( !(isconst && (shiftby > 0)) ) assert(0);
res = ::std::min(dst_bits, opwidth)-(U_8)shiftby;
break;
}
case 1:
{
res = opndwidth;
break;
}
case 2:
{
res = ::std::min(dst_bits, opwidth);
break;
}
default:
assert(0);
}
break;
}
case Op_Shl:
{
switch (opnd_num) {
case 0:
{
Opnd *shiftAmount = inst->getSrc(1);
I_32 shiftby;
bool isconst
= ConstantFolder::isConstant(shiftAmount->getInst(),
shiftby);
if (isconst && shiftby > 0) {
res = ::std::min(dst_bits, opwidth)-(U_8)shiftby;
break;
}
}
case 1:
{
res = opndwidth;
}
break;
default:
assert(0);
};
break;
}
case Op_Shr:
res = opndwidth; break;
case Op_Cmp: case Op_Cmp3:
case Op_Copy:
res = ::std::min(dst_bits, opwidth); break;
case Op_LdVar:
res = dst_bits; break;
case Op_Phi:
res = dst_bits; break;
case Op_StVar:
if (inst->getDst()->isSsaVarOpnd()) {
res = dst_bits;
} else {
res = opndwidth; // can't track value accurately, be conservative
}
break;
case Op_TauStElem:
case Op_TauStRef:
{
if (opnd_num == 0) { // value to be stored, trim to opwidth
res = opwidth;
}
}
break;
case Op_TauStInd:
case Op_TauStField:
{
if (opnd_num == 0) { // value to be stored, trim to opwidth
res = opwidth;
}
}
break;
case Op_TauStStatic:
{
if (opnd_num == 0) { // value to be stored, trim to opwidth
res = opwidth;
}
}
break;
default:
{
res = opndwidth;
}
}
U_8 res1 = ::std::min(res, opndwidth);
return res1;
}
DeadCodeEliminator::DeadCodeEliminator(IRManager& irm)
: irManager(irm), flowGraph(irm.getFlowGraph()), returnOpnd(irm.getReturnOpnd())
{
preserveCriticalEdges = irManager.getOptimizerFlags().preserve_critical_edges;
}
Opnd*
DeadCodeEliminator::findDefiningTemp(Opnd* var) {
SsaVarOpnd* ssaVarOpnd = var->asSsaVarOpnd();
if(ssaVarOpnd == NULL) {
Log::out() << "Nothing found: Not SSA" << ::std::endl;
return NULL; // not an SsaVarOpnd
}
Inst* inst = ssaVarOpnd->getInst();
assert(inst->getNode());
if(inst->getOpcode() == Op_StVar) {
// propagate src of copy recursively
copyPropagate(inst);
if(Log::isEnabled()) {
Log::out() << "Found: ";
Inst* def = inst->getSrc(0)->getInst();
if(def != NULL) {
def->print(Log::out());
}
else {
Log::out() << "Dangling operand "; inst->getSrc(0)->print(Log::out());
}
Log::out() << ::std::endl;
}
return inst->getSrc(0);
} else if(inst->getOpcode() == Op_Phi) {
Opnd* tmp = NULL;
U_32 n = inst->getNumSrcOperands();
for(U_32 j=0; j < n; ++j) {
Opnd* src = inst->getSrc(j);
Inst* srcInst = src->getInst();
assert(srcInst->getNode());
if(srcInst == NULL || srcInst->getOpcode() != Op_StVar) {
tmp = NULL;
break;
}
assert(srcInst->getOpcode() == Op_StVar);
if(tmp == NULL) {
assert(j == 0);
tmp = srcInst->getSrc(0);
} else if(tmp != srcInst->getSrc(0)) {
tmp = NULL;
break;
}
}
return tmp;
}
Log::out() << "Nothing found: Not stVar or phi instruction" << ::std::endl;
return NULL;
}
Opnd*
DeadCodeEliminator::copyPropagate(Opnd* opnd) {
SsaOpnd* src = opnd->asSsaOpnd();
if (src == NULL)
return opnd; // src was not a SsaOpnd so it cannot be propagated
Inst* srcInst = src->getInst();
assert(srcInst->getNode());
if (srcInst == NULL)
return opnd;
if (srcInst->getOpcode() == Op_Copy) {
// propagate src of copy recursively
copyPropagate(srcInst);
return srcInst->getSrc(0);
} else if (srcInst->getOpcode() == Op_LdVar) {
// propagate src of LdVar if src SsaVarOpnd comes from a StVar
Opnd* tmp = findDefiningTemp(srcInst->getSrc(0));
if(tmp != NULL) {
return tmp;
} else {
return opnd;
}
} else if (srcInst->getOpcode() == Op_Phi) {
// propagate src of degenerate phi (phi with only one arg).
if(srcInst->getNumSrcOperands() != 1)
return opnd;
// propagate src of copy recursively
copyPropagate(srcInst);
// reset inst's source to propagates src
return srcInst->getSrc(0);
}
return opnd;
}
void
DeadCodeEliminator::copyPropagate(Inst* inst) {
U_32 numSrcs = inst->getNumSrcOperands();
for (U_32 i=0; i<numSrcs; i++) {
Opnd* opnd = inst->getSrc(i);
Opnd* propagated = copyPropagate(opnd);
if (opnd != propagated) {
if (Log::isEnabled()) {
Log::out() << " Operand "; opnd->print(Log::out());
Log::out() << " replaced by "; propagated->print(Log::out());
Log::out() << std::endl;
}
inst->setSrc(i, propagated);
}
}
}
//
// marks an instruction as live and add its sources to the work set
//
static void
markLiveInst1(Inst* inst,
InstDeque& workSet,
BitSet& usefulInstSet,
BitSet& usefulVarSet,
U_32 minInstId,
U_32 maxInstId) {
//
// add instruction's sources to the work list
//
assert(inst);
assert(inst->getNode());
U_8 dstWidth = 255;
if (Log::isEnabled()) {
Log::out() << "Found dstwidth of " << (int) dstWidth
<< " for inst ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
for (U_32 i=0; i<inst->getNumSrcOperands(); i++) {
Opnd* src = inst->getSrc(i);
// only follow ssa use-def links
SsaOpnd* srcSsaOpnd = src->asSsaOpnd();
if (srcSsaOpnd != NULL) {
// use of an ssa operand
Inst* def = srcSsaOpnd->getInst();
if(def == NULL)
continue;
assert(def != NULL);
assert(def->getNode());
U_8 opndWidth = 255;
U_32 defId = def->getId();
if (usefulInstSet.setBit(defId-minInstId, true)) {
// was already marked as live, check old width.
U_8 oldOpndWidth = 255;
if (Log::isEnabled()) {
Log::out() << "Found dstwidth of " << (int) oldOpndWidth
<< " for inst ";
def->print(Log::out());
Log::out() << ::std::endl;
}
if (opndWidth > oldOpndWidth) {
if (Log::isEnabled()) {
Log::out() << "Setting dstwidth to " << (int) opndWidth
<< " for inst ";
def->print(Log::out());
Log::out() << ::std::endl;
}
workSet.pushFront(def);
}
} else {
if (Log::isEnabled()) {
Log::out() << "Setting dstwidth to " << (int) opndWidth
<< " for inst ";
def->print(Log::out());
Log::out() << ::std::endl;
}
workSet.pushFront(def);
}
// mark ssavaropnds as not dead
SsaVarOpnd* ssaVarOpnd = src->asSsaVarOpnd();
if (ssaVarOpnd != NULL) {
// mark var as useful
usefulVarSet.setBit(ssaVarOpnd->getVar()->getId(),true);
}
} else {
VarOpnd* srcVarOpnd = src->asVarOpnd();
if (srcVarOpnd != NULL) {
// use of a var opnd (ldvar or ldvara)
// mark var as useful
usefulVarSet.setBit(srcVarOpnd->getId(),true);
}
}
}
}
//
// Deletes instructions that are not marked as useful
//
void
DeadCodeEliminator::sweepInst1(Node* node,
Inst* inst,
BitSet& usefulInstSet,
BitSet& usefulVarSet,
U_32 minInstId,
U_32 maxInstId,
bool canRemoveStvars) {
assert(inst);
assert(inst->getNode());
U_32 instId = inst->getId();
if (inst->isMethodMarker()) {
// don't remove a method marker, but remove opcode if it is dead
if (inst->getNumSrcOperands() > 0) {
Opnd *opnd = inst->getSrc(0);
if (opnd && !opnd->isNull()) {
Inst *srcInst = opnd->getInst();
assert(srcInst);
assert(srcInst->getNode());
U_32 srcId = srcInst->getId();
// instructions are only added during analysis for
// live instructions, so if it's not in the map,
// assume it is live
if ((minInstId <= srcId) &&
(srcId < maxInstId) &&
(usefulInstSet.getBit(srcId-minInstId) == false)) {
MethodMarkerInst *mmi = inst->asMethodMarkerInst();
// Method marker could contain retOpnd
if (!mmi->getMethodDesc()->isStatic()) {
mmi->removeOpnd();
}
}
}
}
} else if ((minInstId <= instId) && (instId < maxInstId) &&
usefulInstSet.getBit(instId-minInstId) == false) {
// inst is a useless instruction
if (Log::isEnabled()) {
Log::out() << "Useless inst found, removing: ";
inst->print(Log::out());
Log::out() << ::std::endl;
Log::out() << " instId=" << (int)instId;
Log::out() << ", minInstId=" << (int)minInstId;
Log::out() << ", maxInstId=" << (int)maxInstId;
Log::out() << ::std::endl;
}
inst->unlink();
if (inst->isBranch()) {
assert(0);
} else if (inst->isSwitch()) {
assert(0);
} else if (inst->getOperation().canThrow()) {
flowGraph.removeEdge(node->getExceptionEdge());
}
return;
}
//
// delete stores to useless variables
//
if (canRemoveStvars && inst->getOpcode() == Op_StVar) {
VarOpnd* dstVarOpnd = inst->getDst()->asVarOpnd();
if (dstVarOpnd == NULL)
return;
if (usefulVarSet.getBit(dstVarOpnd->getId()) == false) {
//
// store to a useless variable
//
if (Log::isEnabled()) {
Log::out() << "Removing store to useless var: ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
inst->unlink();
return;
}
}
}
//
// marks an instruction as live and add its sources to the work set
//
static void
markLiveInst(Inst* inst,
InstDeque& workSet,
BitSet& usefulInstSet,
BitSet& usefulVarSet,
U_8 *usedInstWidth,
U_32 minInstId,
U_32 maxInstId) {
//
// add instruction's sources to the work list
//
assert(inst);
assert(inst->getNode());
U_32 instId = inst->getId();
assert(usedInstWidth);
assert((instId >= minInstId) && (instId < maxInstId));
U_8 dstWidth = usedInstWidth[instId-minInstId];
if (Log::isEnabled()) {
Log::out() << "Found dstwidth of " << (int) dstWidth
<< " for inst ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
for (U_32 i=0; i<inst->getNumSrcOperands(); i++) {
Opnd* src = inst->getSrc(i);
// only follow ssa use-def links
SsaOpnd* srcSsaOpnd = src->asSsaOpnd();
if (srcSsaOpnd != NULL) {
// use of an ssa operand
Inst* def = srcSsaOpnd->getInst();
if(def == NULL)
continue;
assert(def != NULL);
U_8 opndWidth = usesBitsOfOpnd(dstWidth, inst, i);
assert(def);
assert(def->getNode());
U_32 defId = def->getId();
if (!((minInstId <= defId) && (defId < maxInstId))) {
// this instruction is out of the region, skip it.
if (Log::isEnabled()) {
Log::out() << "Skipping outside-region inst ";
def->print(Log::out());
Log::out() << ::std::endl;
}
} else if (usefulInstSet.setBit(defId-minInstId, true)) {
// was already marked as live, check old width.
U_8 oldOpndWidth = usedInstWidth[defId-minInstId];
if (Log::isEnabled()) {
Log::out() << "Found dstwidth of " << (int) oldOpndWidth
<< " for inst ";
def->print(Log::out());
Log::out() << ::std::endl;
}
if (opndWidth > oldOpndWidth) {
if (Log::isEnabled()) {
Log::out() << "Setting dstwidth to " << (int) opndWidth
<< " for inst ";
def->print(Log::out());
Log::out() << ::std::endl;
}
usedInstWidth[defId-minInstId] = opndWidth;
workSet.pushFront(def);
}
} else {
if (Log::isEnabled()) {
Log::out() << "Setting dstwidth to " << (int) opndWidth
<< " for inst ";
def->print(Log::out());
Log::out() << ::std::endl;
}
usedInstWidth[defId-minInstId] = opndWidth;
workSet.pushFront(def);
}
// mark ssavaropnds as not dead
SsaVarOpnd* ssaVarOpnd = src->asSsaVarOpnd();
if (ssaVarOpnd != NULL) {
// mark var as useful
usefulVarSet.setBit(ssaVarOpnd->getVar()->getId(),true);
}
} else {
VarOpnd* srcVarOpnd = src->asVarOpnd();
if (srcVarOpnd != NULL) {
// use of a var opnd (ldvar or ldvara)
// mark var as useful
usefulVarSet.setBit(srcVarOpnd->getId(),true);
}
}
}
}
//
// Deletes instructions that are not marked as useful
//
void
DeadCodeEliminator::sweepInst(Node* node,
Inst* inst,
BitSet& usefulInstSet,
BitSet& usefulVarSet,
U_8 *usedInstWidth,
U_32 minInstId,
U_32 maxInstId,
bool canRemoveStvars) {
assert(usedInstWidth);
assert(inst);
assert(inst->getNode());
U_32 instId = inst->getId();
if (inst->isMethodMarker()) {
// don't remove a method marker, but remove opcode if it is dead
if (inst->getNumSrcOperands() > 0) {
Opnd *opnd = inst->getSrc(0);
if (opnd && !opnd->isNull()) {
Inst *srcInst = opnd->getInst();
assert(srcInst);
assert(srcInst->getNode());
U_32 srcId = srcInst->getId();
// instructions are only added during analysis for
// live instructions, so if it's not in the map,
// assume it is live
if ((minInstId <= srcId) && (srcId < maxInstId) &&
(usefulInstSet.getBit(srcId-minInstId) == false)) {
MethodMarkerInst *mmi = inst->asMethodMarkerInst();
// Method marker could contain retOpnd which should be preserved
if (!mmi->getMethodDesc()->isStatic()) {
mmi->removeOpnd();
}
}
}
}
} else if ((minInstId <= instId) && (instId < maxInstId) &&
usefulInstSet.getBit(instId-minInstId) == false) {
// inst is a useless instruction
if (Log::isEnabled()) {
Log::out() << "Useless inst found, removing: ";
inst->print(Log::out());
Log::out() << ::std::endl;
Log::out() << " instId=" << (int)instId;
Log::out() << ", minInstId=" << (int)minInstId;
Log::out() << ", maxInstId=" << (int)maxInstId;
Log::out() << ::std::endl;
}
inst->unlink();
if (inst->isBranch()) {
assert(0);
} else if (inst->isSwitch()) {
assert(0);
} else if (inst->getOperation().canThrow()) {
flowGraph.removeEdge(node->getExceptionEdge());
}
return;
} else {
switch (inst->getOpcode()) {
case Op_Conv:
{
Type::Tag instType = inst->getType();
if ((!Type::isInteger(instType)) ||
((inst->getOverflowModifier() != Overflow_None) &&
(inst->getExceptionModifier() != Exception_Never)))
break;
Type *srcType = inst->getSrc(0)->getType();
if (!srcType->isInteger())
break;
U_8 convSize = getBitWidth(instType);
U_8 srcSize = getBitWidth(srcType->tag);
U_8 dstSize = getBitWidth(inst->getDst()->getType()->tag);
if (dstSize != srcSize) {
// it's not just a sign-extension, we can't remove it
break;
}
assert((minInstId <= instId) && (instId < maxInstId));
U_8 usedDstSize = usedInstWidth[instId-minInstId];
if (Log::isEnabled()) {
Log::out() << "Found dstwidth of " << (int) usedDstSize
<< " for inst ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
if ((srcSize >= convSize) &&
(usedDstSize <= convSize)) {
// convert to a copy.
if (Log::isEnabled()) {
Log::out() << "Eliminating redundant conv ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
InstFactory &factory = irManager.getInstFactory();
Inst *copyInst = factory.makeCopy(inst->getDst(), inst->getSrc(0));
copyInst->insertAfter(inst);
inst->unlink();
// copyInst doesn't appear in bitmaps, but we won't walk
// over it, so it is only tested for a MethodMarker Inst
// operand above, and we are careful there
}
}
break;
default:
break;
}
}
//
// delete stores to useless variables
//
if (canRemoveStvars && inst->getOpcode() == Op_StVar) {
VarOpnd* dstVarOpnd = inst->getDst()->asVarOpnd();
if (dstVarOpnd == NULL)
return;
if (usefulVarSet.getBit(dstVarOpnd->getId()) == false) {
//
// store to a useless variable
//
if (Log::isEnabled()) {
Log::out() << "Removing store to useless var: ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
inst->unlink();
return;
}
}
}
//
// Eliminate unreachable code
//
bool
DeadCodeEliminator::eliminateUnreachableCode() {
MemoryManager memManager("DeadCodeEliminator::eliminateUnreachableCode");
// Purge unreachable nodes from CFG.
Nodes unreachableNodes(memManager);
irManager.getFlowGraph().purgeUnreachableNodes(unreachableNodes);
// If no unreachable nodes, return
if(unreachableNodes.empty())
return false;
// Clear the set of unreachable definitions.
StlHashSet<U_32> unreachableDefSet(memManager);
StlVector<Node*>::iterator niter;
for(niter = unreachableNodes.begin(); niter != unreachableNodes.end(); ++niter) {
Node* node = *niter;
// Find unreachable defs. Note, only SsaVarOpnds can be used
// in later reachable nodes.
Inst* headInst = (Inst*)node->getFirstInst();
assert(headInst->getNode());
for (Inst* inst = headInst->getNextInst();inst!=NULL;inst=inst->getNextInst()) {
assert(inst->getNode());
SsaVarOpnd* dst = inst->getDst()->asSsaVarOpnd();
if(dst != NULL) {
assert(inst->getOpcode() == Op_StVar || inst->getOpcode() == Op_Phi);
unreachableDefSet.insert(dst->getId());
}
}
}
// If no unreachable defs to cleanup, just return.
if(unreachableDefSet.empty())
return true;
// Cleanup up phi instructions.
const Nodes& nodes = flowGraph.getNodes();
Nodes::const_iterator niter2;
for(niter2 = nodes.begin(); niter2 != nodes.end(); ++niter2) {
Node* node = *niter2;
Inst* headInst = (Inst*)node->getFirstInst();
assert(headInst->getNode());
for (Inst* inst = headInst->getNextInst();inst!=NULL;inst=inst->getNextInst()) {
assert(inst->getNode());
if(inst->getOpcode() == Op_Phi) {
#ifndef NDEBUG
SsaVarOpnd* dst = inst->getDst()->asSsaVarOpnd();
assert(dst!=NULL);
#endif
U_32 numSrc = inst->getNumSrcOperands();
U_32 numKill = 0;
for(U_32 i = 0; i < numSrc; ++i) {
SsaVarOpnd* src = inst->getSrc(i)->asSsaVarOpnd();
assert(src != NULL);
if(unreachableDefSet.find(src->getId()) != unreachableDefSet.end()) {
// Purge this operand.
++numKill;
} else {
// Shift down over purged operands.
assert(numKill <= i);
if(numKill > 0)
inst->setSrc(i-numKill, src);
}
}
if(numKill > 0)
((PhiInst*) inst)->setNumSrcs(numSrc-numKill);
}
}
}
return true;
}
static CountTime dcePhase1Timer("opt::dce::phase1");
static CountTime dcePhase2Timer("opt::dce::phase2");
static CountTime dcePhase3Timer("opt::dce::phase3");
static CountTime dcePhase4Timer("opt::dce::phase4");
static CountTime dcePhase5Timer("opt::dce::phase5");
static CountTime dcePhase6Timer("opt::dce::phase6");
//
// Performs dead code elimination
//
void
DeadCodeEliminator::eliminateDeadCode(bool keepEmptyNodes) {
// user should call eliminateUnreachableCode() first
U_32 minInstId = irManager.getMinimumInstId();
U_32 maxInstId = irManager.getInstFactory().getNumInsts();
U_32 numInsts = maxInstId - minInstId;
U_32 numOpnds = irManager.getOpndManager().getNumVarOpnds();
MemoryManager memManager("DeadCodeEliminator::eliminateDeadCode");
InstDeque workSet(memManager);
Nodes nodes(memManager);
flowGraph.getNodesPostOrder(nodes);
Nodes::reverse_iterator niter;
// set of useful instructions & variables; initially everything is useless
BitSet usefulInstSet(memManager,numInsts+1);
BitSet usefulVarSet(memManager,numOpnds+1);
const OptimizerFlags& optimizerFlags = irManager.getOptimizerFlags();
U_8 *usedInstWidth = optimizerFlags.dce2 ? new (memManager) U_8[numInsts] : 0;
{
AutoTimer t(dcePhase1Timer);
//
// first propagate copies and initialize the work list with
// essential instructions
//
if (usedInstWidth) {
for (niter = nodes.rbegin(); niter != nodes.rend(); ++niter) {
Node* node = *niter;
Inst* headInst = (Inst*)node->getFirstInst();
assert(headInst->getNode());
for (Inst* inst = headInst->getNextInst();inst!=NULL;inst=inst->getNextInst()) {
assert(inst->getNode());
// copy propagate all sources of this instruction
copyPropagate(inst);
// For inlined methods, the last instruction of the epilog copies the return value out.
if (isNonEssential(inst) == false || (returnOpnd != NULL && returnOpnd == inst->getDst())) {
// add essential instruction to work list
assert(inst != NULL);
workSet.pushBack(inst);
assert(inst);
assert(inst->getNode());
U_32 instId = inst->getId();
usefulInstSet.setBit(instId-minInstId, true);
if (usedInstWidth) {
U_8 usedWidth = getBitWidth(inst->getType());
if (Log::isEnabled()) {
Log::out() << "Setting dstwidth to " << (int) usedWidth
<< " for inst ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
assert((minInstId <= instId) && (instId < maxInstId));
usedInstWidth[instId-minInstId]= usedWidth;
}
}
}
}
} else {
for (niter = nodes.rbegin(); niter != nodes.rend(); ++niter) {
Node* node = *niter;
Inst* headInst = (Inst*)node->getFirstInst();
assert(headInst->getNode());
for (Inst* inst = headInst->getNextInst();inst!=NULL;inst=inst->getNextInst()) {
assert(inst->getNode());
// copy propagate all sources of this instruction
copyPropagate(inst);
// For inlined methods, the last instruction of the epilog copies the return value out.
if (isNonEssential(inst) == false || (returnOpnd != NULL && returnOpnd == inst->getDst())) {
// add essential instruction to work list
assert(inst != NULL);
workSet.pushBack(inst);
assert(inst);
assert(inst->getNode());
U_32 instId = inst->getId();
usefulInstSet.setBit(instId-minInstId, true);
}
}
}
}
}
{
AutoTimer t(dcePhase2Timer);
//
// Iteratively mark all useful instructions by computing slices
//
if (usedInstWidth) {
while (!workSet.isEmpty()) {
markLiveInst(workSet.popFront(),workSet,usefulInstSet,usefulVarSet,
usedInstWidth, minInstId, maxInstId);
}
} else {
while (!workSet.isEmpty()) {
markLiveInst1(workSet.popFront(),workSet,
usefulInstSet, usefulVarSet, minInstId, maxInstId);
}
}
}
{
const bool canRemoveStVars = (irManager.getParent() == NULL); // we can remove a useless var
AutoTimer t(dcePhase3Timer);
//
// Now cleanup all the dead code
//
if (usedInstWidth) {
for (niter = nodes.rbegin(); niter != nodes.rend(); ++niter) {
Node* node = *niter;
Inst* headInst = (Inst*)node->getFirstInst();
assert(headInst->getNode());
for (Inst* inst = headInst->getNextInst(); inst != NULL; ) {
assert(inst->getNode());
Inst *nextInst = inst->getNextInst();
sweepInst(node, inst,usefulInstSet,usefulVarSet,usedInstWidth,minInstId,maxInstId,canRemoveStVars);
inst = nextInst;
}
}
} else {
for (niter = nodes.rbegin(); niter != nodes.rend(); ++niter) {
Node* node = *niter;
Inst* headInst = (Inst*)node->getFirstInst();
assert(headInst->getNode());
for (Inst* inst = headInst->getNextInst(); inst != NULL; ) {
assert(inst->getNode());
Inst *nextInst = inst->getNextInst();
sweepInst1(node, inst,usefulInstSet,usefulVarSet,minInstId,maxInstId,canRemoveStVars);
inst = nextInst;
}
}
}
}
{
const bool canUnlink = (irManager.getParent() == NULL); // we can unlink an unused var
AutoTimer t(dcePhase4Timer);
//
// delete dead variables
//
OpndManager &opndManager = irManager.getOpndManager();
VarOpnd *varOpnd0 = opndManager.getVarOpnds();
if (varOpnd0) {
VarOpnd *varOpnd = varOpnd0;
// first check whether head needs to be deleted
while (varOpnd == varOpnd0) {
VarOpnd *nextVar = varOpnd->getNextVarOpnd();
if (nextVar == varOpnd) nextVar = 0; // this should make us stop
if (usefulVarSet.getBit(varOpnd->getId()) == true){
// live variable
varOpnd->setDeadFlag(false); // mark as live
} else {
// dead variable
varOpnd->setDeadFlag(true); // mark as dead
if (canUnlink) {
if (Log::isEnabled()) {
Log::out() << "removing dead VarOpnd ";
varOpnd->print(Log::out());
Log::out() << ::std::endl;
}
opndManager.deleteVar(varOpnd); // remove from list
}
// get the new head; this should usually be nextVar, but may be 0.
varOpnd0 = opndManager.getVarOpnds();
if (!nextVar && !varOpnd0) nextVar = varOpnd; // if it is 0, make sure we still stop
}
varOpnd = nextVar;
};
// we may have deleted all vars already, be careful here.
if (varOpnd0) {
varOpnd = varOpnd0->getNextVarOpnd();
while (varOpnd != varOpnd0) {
VarOpnd *nextVar = varOpnd->getNextVarOpnd();
if (usefulVarSet.getBit(varOpnd->getId()) == true){
// live variable
varOpnd->setDeadFlag(false); // mark as live
} else {
// dead variable
varOpnd->setDeadFlag(true); // mark as dead
if (canUnlink) {
if (Log::isEnabled()) {
Log::out() << "removing dead VarOpnd ";
varOpnd->print(Log::out());
Log::out() << ::std::endl;
}
opndManager.deleteVar(varOpnd); // remove from list
}
}
varOpnd = nextVar;
}
}
}
}
{
AutoTimer t(dcePhase6Timer);
flowGraph.mergeAdjacentNodes();
}
{
AutoTimer t(dcePhase5Timer);
if (!keepEmptyNodes) {
//
// Purge empty nodes.
//
flowGraph.purgeEmptyNodes(preserveCriticalEdges);
}
}
}
//
// Leave one PseudoThrow instruction only for those loops which
// do not contain other dispatch edges exiting the loop.
//
void
DeadCodeEliminator::removeExtraPseudoThrow() {
MemoryManager memManager("DeadCodeEliminator::removeExtraPseudoThrow");
OptPass::computeLoops(irManager);
LoopTree* loopTree = irManager.getLoopTree();
assert(loopTree && loopTree->isValid());
if (Log::isLogEnabled(LogStream::DOTDUMP)) {
OptPass::printDotFile(irManager, Log::getStageId(), "rept", "after_loop_tree");
OptPass::printHIR(irManager);
}
// Nodes containing essential PseudoThrow instructions
BitSet essentialNodes(memManager, flowGraph.getMaxNodeId());
if (loopTree->hasLoops()) {
LoopNode* loopNode = ((LoopNode*)loopTree->getRoot())->getChild();
markEssentialPseudoThrows(loopNode, essentialNodes);
}
const Nodes& cfgNodes = flowGraph.getNodes();
if (Log::isEnabled()) {
Log::out() << "Removing useless PseudoThrow instructions:" << std::endl;
}
for (Nodes::const_iterator it = cfgNodes.begin(), end = cfgNodes.end(); it!=end; ++it) {
Node* node = *it;
Inst* lastInst = (Inst*)node->getLastInst();
if (!essentialNodes.getBit(node->getId()) && (lastInst->getOpcode() == Op_PseudoThrow)) {
if (Log::isEnabled()) {
Log::out() << " Removing instruction: ";
lastInst->print(Log::out());
Log::out() << std::endl;
}
lastInst->unlink();
Edge* dispatchEdge = node->getExceptionEdge();
assert(dispatchEdge != NULL);
flowGraph.removeEdge(dispatchEdge);
}
}
if (Log::isEnabled()) {
Log::out() << "Done." << std::endl;
}
eliminateUnreachableCode();
}
void
DeadCodeEliminator::markEssentialPseudoThrows(LoopNode* loopNode, BitSet& essentialNodes) {
LoopNode* childNode = loopNode->getChild();
if (childNode != NULL)
markEssentialPseudoThrows(childNode, essentialNodes);
LoopNode* siblingNode = loopNode->getSiblings();
if (siblingNode != NULL)
markEssentialPseudoThrows(siblingNode, essentialNodes);
if (Log::isEnabled()) {
Log::out() << "Analyzing loop nodes with the loop header ID: "
<< loopNode->getHeader()->getId() << std::endl;
}
Node* mbEssentialNode = NULL;
const Nodes& loopNodes = loopNode->getNodesInLoop();
for (Nodes::const_iterator it = loopNodes.begin(), end = loopNodes.end(); it!=end; ++it) {
Node* node = *it;
if (Log::isEnabled()) {
Log::out() << "Analyzing Node ID: " << node->getId() << std::endl;
}
if (!node->isBlockNode()) continue;
Edge* exceptionEdge = node->getExceptionEdge();
while (exceptionEdge != NULL) {
Node* targetNode = exceptionEdge->getTargetNode();
if (Log::isEnabled()) {
Log::out() << " inspecting dispatch edge to node: " << targetNode->getId() << std::endl;
}
if (!loopNode->inLoop(targetNode)) break;
exceptionEdge = targetNode->getExceptionEdge();
}
if (exceptionEdge != NULL) {
if (Log::isEnabled()) {
Log::out() << " Loop exit exception edge detected: ";
}
if (((Inst*)node->getLastInst())->getOpcode() == Op_PseudoThrow) {
if (essentialNodes.getBit(node->getId())) {
// There is an essential PseudoThrow instruction in this loop
if (Log::isEnabled()) {
Log::out() << " essential PseudoThrow inst" << std::endl;
}
return;
} else {
// A candidate to essential nodes
if (Log::isEnabled()) {
Log::out() << " essential candidate" << std::endl;
}
mbEssentialNode = node;
}
} else {
// No essential PseudoThrow insts in this loop
if (Log::isEnabled()) {
Log::out() << " PseudoThrow killer inst" << std::endl;
}
return;
}
}
}
if (mbEssentialNode != NULL) {
essentialNodes.setBit(mbEssentialNode->getId());
if (Log::isEnabled()) {
Log::out() << "Found essential PseudoThrow in node ID: "
<< mbEssentialNode->getId() << std::endl;
}
}
return;
}
void DeadCodeEliminator::createLoop2DispatchMapping(ControlFlowGraph& cfg, InfiniteLoopsInfo& info) {
LoopTree* lt = cfg.getLoopTree();
if (!lt->isValid()) {
lt->rebuild(false, true);
}
info.hasLoops = lt->hasLoops();
if (!info.hasLoops) {
return;
}
//algorithm:
//for every loop find ANY node in a loop with dispatch edge that leads out of the loop
//add the dispatch node to the mapping. Add nothing if dispatch is unwind
//ANY dispatch is suitable to be used for infinite loop, because the point of interruption of infinite loop is undefined.
const Nodes& nodes = cfg.getNodes();
for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) {
Node* node = *it;
if (lt->getLoopDepth(node) == 0) {
continue;
}
Edge* exceptionEdge = node->getExceptionEdge();
if (exceptionEdge == NULL) {
continue;
}
Node* dispatch = exceptionEdge->getTargetNode();
Node* loopHead = lt->getLoopHeader(node, false);
while (dispatch && lt->getLoopHeader(dispatch, false) == loopHead) {
dispatch = dispatch->getExceptionEdgeTarget();
}
if (dispatch) {
info.map[loopHead] = dispatch;
}
}
}
void DeadCodeEliminator::fixInfiniteLoops(IRManager& irm, const InfiniteLoopsInfo& info) {
if (!info.hasLoops) {
return;
}
//Find infinite loops and use mapping provided to add pseudoThrows.
//To find infinite loops use dominator trees:
// LoopHead == dominates on incoming edge source.
// Infinite loop == LoopHead with no paths to Exit node: when Exit node does not postdominate on LoopHead
if (Log::isEnabled()) {
Log::out()<<"Performing infinite loops check "<<std::endl;
}
ControlFlowGraph& cfg = irm.getFlowGraph();
MemoryManager tmpMM("fixInfiniteLoops");
OptPass::computeDominators(irm);
DominatorTree* dom = irm.getDominatorTree();
DominatorBuilder db;
DominatorTree* postDom = db.computeDominators(tmpMM, &cfg, true, true);
Node* exitNode = cfg.getExitNode();
Nodes infiniteLoopHeads(tmpMM);
//searching for infinite loops
Nodes nodes(tmpMM);
cfg.getNodes(nodes);
for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) {
Node* node = *it;
if (postDom->dominates(exitNode, node)) { //the node is reachable from Exit node
continue;
}
//ok, this is dead code or endless loop -> check if this is a loop
const Edges& inEdges = node->getInEdges();
for (Edges::const_iterator eit = inEdges.begin(), eend = inEdges.end(); eit !=eend; eit++) {
Edge* edge = *eit;
Node* srcNode = edge->getSourceNode();
if (dom->dominates(node, srcNode)) { //ok, this is loop head
infiniteLoopHeads.push_back(node);
break;
}
}
}
//fixing infinite loops
for (Nodes::const_iterator it = infiniteLoopHeads.begin(), end = infiniteLoopHeads.end(); it!=end; ++it) {
Node* node = *it;
Node* dispatch = NULL;
StlMap<Node*, Node*>::const_iterator mit = info.map.find(node);
if (mit!=info.map.end()) {
dispatch = mit->second;
} else {
dispatch = cfg.getUnwindNode();
if (dispatch == NULL) {
dispatch = cfg.createDispatchNode(irm.getInstFactory().makeLabel());
cfg.addEdge(dispatch, exitNode);
cfg.setUnwindNode(dispatch);
}
}
assert(dispatch!=NULL);
cfg.splitNodeAtInstruction(node->getFirstInst(), true, false, irm.getInstFactory().makeLabel());
node->appendInst(irm.getInstFactory().makePseudoThrow());
cfg.addEdge(node, dispatch);
if (Log::isEnabled()) {
Log::out() <<" Found infinite loop: node:";FlowGraph::print(Log::out(), node); Log::out()<<std::endl;
Log::out() <<" connecting loop to :";FlowGraph::print(Log::out(), dispatch); Log::out()<<std::endl;
}
}
if (Log::isEnabled()) {
Log::out()<<"Infinite loops check finished."<<std::endl;
}
}
} //namespace Jitrino