blob: 6c2060878799fec9b7e062faf2067882a80a3b3d [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 "Log.h"
#include "gcmanagedpointeranalyzer.h"
#include "irmanager.h"
#include "deadcodeeliminator.h"
#include "FlowGraph.h"
namespace Jitrino {
DEFINE_SESSION_ACTION(GCManagedPointerAnalysisPass, opt_gcmap, "GC Managed Pointer to Base Map Construction")
void
GCManagedPointerAnalysisPass::_run(IRManager& irm) {
GCManagedPointerAnalyzer analyzer(irm.getNestedMemoryManager(), irm);
analyzer.analyzeManagedPointers();
}
GCManagedPointerAnalyzer::GCManagedPointerAnalyzer(MemoryManager& memoryManager, IRManager& irManager)
: _memoryManager(memoryManager),
_irManager(irManager),
_pBaseMap(new (memoryManager) BaseMap(memoryManager)),
_baseMap(*_pBaseMap),
_pVarMap(&irManager.getGCBasePointerMap()),
_varMap(*_pVarMap),
_mapsComputed(false),
_rematerializeMode(!irManager.getOptimizerFlags().gc_build_var_map)
{
}
SsaVarOpnd*
GCManagedPointerAnalyzer::createVarMapping(Type* baseType, SsaVarOpnd* ptr)
{
VarOpnd* ptrVar = ptr->getVar();
VarOpnd* baseVar;
if(_varMap.find(ptrVar) != _varMap.end()) {
baseVar = _varMap[ptrVar];
assert(baseVar->getType() == baseType);
} else {
baseVar = _irManager.getOpndManager().createVarOpnd(baseType, false);
_varMap[ptrVar] = baseVar;
}
SsaVarOpnd* base = _irManager.getOpndManager().createSsaVarOpnd(baseVar);
_baseMap[ptr] = base;
return base;
}
void
GCManagedPointerAnalyzer::computeBaseMaps()
{
assert(_mapsComputed == false);
ControlFlowGraph& fg = _irManager.getFlowGraph();
MemoryManager mm("GCManagedPointerAnalyzer::computeBaseMaps.mm");
// List of nodes in RPO
StlVector<Node*> nodes(mm);
fg.getNodesPostOrder(nodes);
StlVector<Node*>::reverse_iterator niter;
// List of known static (i.e., non-heap) managed pointers. We can ignore these.
StlHashSet<SsaOpnd*> _staticMap(mm);
#ifndef NDEBUG
U_32 iterCount = 0;
#endif
bool done = false;
while(!done) {
assert(++iterCount <= 2);
done = true;
for(niter = nodes.rbegin(); niter != nodes.rend(); ++niter) {
Node* node = *niter;
if(Log::isEnabled()) {
Log::out() << "Consider block ";
FlowGraph::printLabel(Log::out(), node);
Log::out() << ::std::endl;
}
Inst* first = (Inst*)node->getFirstInst();
for(Inst* inst = first->getNextInst(); inst != NULL; inst = inst->getNextInst()) {
Opnd* dst_ = inst->getDst();
if(!dst_->isNull() && dst_->getType()->isManagedPtr()) {
//
// Keep track of base
//
SsaOpnd* dst = dst_->asSsaOpnd();
assert(dst != NULL);
switch(inst->getOpcode()) {
case Op_LdStaticAddr:
// Static field - no GC support needed.
_staticMap.insert(dst);
break;
case Op_LdFieldAddr:
case Op_LdElemAddr:
case Op_LdArrayBaseAddr:
case Op_LdLockAddr:
case Op_AddOffset:
case Op_AddOffsetPlusHeapbase:
{
// Base is directly available.
SsaTmpOpnd* base = inst->getSrc(0)->asSsaTmpOpnd();
assert(base != NULL && base->getType()->isObject());
_baseMap[dst] = base;
break;
}
case Op_AddScaledIndex:
case Op_StVar:
case Op_LdVar:
{
// Propagate base from src operand to dst operand.
SsaOpnd* src = inst->getSrc(0)->asSsaOpnd();
assert(src != NULL && src->getType()->isManagedPtr());
if(_baseMap.find(src) != _baseMap.end()) {
_baseMap[dst] = _baseMap[src];
} else if(_staticMap.find(src) != _staticMap.end()) {
_staticMap.insert(dst);
} else {
assert(0);
}
break;
}
case Op_Phi:
{
if(Log::isEnabled()) {
Log::out() << "Consider phi ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
PhiInst* phi = inst->asPhiInst();
SsaOpnd* base = NULL;
bool isStatic = false;
bool isAmbiguous = false;
U_32 nSrcs = phi->getNumSrcOperands();
for(U_32 i = 0; i < nSrcs; ++i) {
SsaVarOpnd* src = phi->getSrc(i)->asSsaVarOpnd();
assert(src != NULL);
if(_baseMap.find(src) != _baseMap.end()) {
assert(!isStatic);
SsaOpnd* candidate = _baseMap[src];
if(base == NULL) {
base = candidate;
}
else if(base != candidate) {
assert(base->getType() == candidate->getType());
isAmbiguous = true;
}
} else if(_staticMap.find(src) != _staticMap.end()) {
assert(base == NULL && isAmbiguous == false);
isStatic = true;
_staticMap.insert(dst);
} else {
// Not recorded - uninitialized input - need another iteration
done = false;
if(Log::isEnabled()) {
Log::out() << "Undefined arg ";
src->print(Log::out());
Log::out() << " - redo" << ::std::endl;
}
}
}
assert(isStatic || base != NULL);
if(isAmbiguous) {
SsaVarOpnd* dstSsaVar = dst->asSsaVarOpnd();
assert(dstSsaVar != NULL);
createVarMapping(base->getType(), dstSsaVar);
} else if(!isStatic) {
assert(base != NULL);
_baseMap[dst] = base;
}
} // case Op_Phi
break;
default:
break;
} // switch(inst->getOpcode()) {
} // if(!dst_->isNull() && dst_->getType()->isManagedPtr())
}
}
}
_mapsComputed = true;
}
void
GCManagedPointerAnalyzer::addBaseVarDefs()
{
assert(_mapsComputed == true);
BaseMap::iterator i;
for(i = _baseMap.begin(); i != _baseMap.end(); ++i) {
if(Log::isEnabled()) {
Log::out() << "Add def for ";
i->first->print(Log::out());
Log::out() << ::std::endl;
}
SsaVarOpnd* ptr = i->first->asSsaVarOpnd();
if(ptr != NULL && _varMap.find(ptr->getVar()) != _varMap.end())
insertVarDef(ptr);
}
}
SsaVarOpnd*
GCManagedPointerAnalyzer::insertVarDef(SsaVarOpnd* ptr)
{
if(Log::isEnabled()) {
Log::out() << "Insert var def for ";
ptr->print(Log::out());
Log::out() << ::std::endl;
}
assert(ptr->getType()->isManagedPtr());
SsaOpnd* base = _baseMap[ptr];
VarOpnd* ptrVar = ptr->getVar();
assert(_varMap.find(ptrVar) != _varMap.end());
VarOpnd* baseVar = _varMap[ptrVar];
SsaVarOpnd* baseSsa = NULL;
if(base->isSsaVarOpnd() && base->asSsaVarOpnd()->getVar() == baseVar) {
baseSsa = base->asSsaVarOpnd();
if(base->getInst() != NULL) {
// Already defined!
return baseSsa;
} else {
if(ptr->getInst()->getOpcode() == Op_StVar)
// Nothing to be done. baseSsa will be defined at a phi.
return baseSsa;
}
}
else {
baseSsa = createVarMapping(baseVar->getType(), ptr);
}
assert(baseSsa != NULL && baseSsa->getVar() == baseVar && baseSsa->getInst() == NULL);
Inst* ptrDef = ptr->getInst();
Opcode ptrOp = ptrDef->getOpcode();
InstFactory& instFactory = _irManager.getInstFactory();
if(ptrOp == Op_Phi) {
PhiInst* ptrPhi = ptrDef->asPhiInst();
U_32 numOpnds = ptrPhi->getNumSrcOperands();
Opnd** newOpnds = new (_irManager.getMemoryManager()) Opnd*[numOpnds];
Inst* basePhi = instFactory.makePhi(baseSsa, numOpnds, newOpnds);
for(U_32 i = 0; i < numOpnds; ++i)
basePhi->setSrc(i, insertVarDef(ptrPhi->getSrc(i)->asSsaVarOpnd()));
basePhi->insertBefore(ptrPhi);
} else {
assert(ptrOp == Op_StVar);
VarAccessInst* ptrStVar = ptrDef->asVarAccessInst();
SsaTmpOpnd* tmp = base->asSsaTmpOpnd();
if(tmp == NULL) {
assert(base->isSsaVarOpnd() && base->asSsaVarOpnd()->getVar() != baseSsa->getVar());
tmp = _irManager.getOpndManager().createSsaTmpOpnd(base->getType());
Inst* ldVar = instFactory.makeLdVar(tmp, base->asSsaVarOpnd());
ldVar->insertBefore(ptrStVar);
}
Inst* baseStVar = instFactory.makeStVar(baseSsa, tmp);
baseStVar->insertBefore(ptrStVar);
}
assert(baseSsa->getInst() != NULL);
return baseSsa;
}
void
GCManagedPointerAnalyzer::analyzeManagedPointers()
{
if(!_rematerializeMode) {
computeBaseMaps();
addBaseVarDefs();
} else {
ControlFlowGraph& fg = _irManager.getFlowGraph();
MemoryManager mm("GCManagedPointerAnalyzer::analyzeManagedPointers.mm");
StlVector<Node*> nodes(mm);
fg.getNodesPostOrder(nodes);
StlHashSet<SsaOpnd*> _staticMap(mm);
StlVector<Node*>::reverse_iterator niter;
#ifndef NDEBUG
U_32 k = 0;
#endif
bool done = false;
while(!done) {
assert((++k) <= 3);
done = true;
for(niter = nodes.rbegin(); niter != nodes.rend(); ++niter) {
Node* node = *niter;
if(Log::isEnabled()) {
Log::out() << "Consider block ";
FlowGraph::printLabel(Log::out(), node);
Log::out() << ::std::endl;
}
Inst* first = (Inst*)node->getFirstInst();
for(Inst* inst = first->getNextInst(); inst != NULL; inst = inst->getNextInst()) {
Opnd* dst_ = inst->getDst();
if(!dst_->isNull() && dst_->getType()->isManagedPtr()) {
//
// Keep track of base
//
SsaOpnd* dst = dst_->asSsaOpnd();
assert(dst != NULL);
switch(inst->getOpcode()) {
case Op_LdStaticAddr:
// Static field - no GC support needed.
_staticMap.insert(dst);
break;
case Op_LdFieldAddr:
case Op_LdElemAddr:
case Op_LdArrayBaseAddr:
case Op_LdLockAddr:
{
SsaTmpOpnd* base = inst->getSrc(0)->asSsaTmpOpnd();
assert(base != NULL && base->getType()->isObject());
_baseMap[dst] = base;
break;
}
case Op_AddScaledIndex:
case Op_StVar:
case Op_LdVar:
{
SsaOpnd* src = inst->getSrc(0)->asSsaOpnd();
assert(src != NULL && src->getType()->isManagedPtr());
if(_baseMap.find(src) != _baseMap.end()) {
_baseMap[dst] = _baseMap[src];
} else if(_staticMap.find(src) != _staticMap.end()) {
_staticMap.insert(dst);
} else {
assert(0);
if(inst->getOpcode() == Op_AddScaledIndex) {
FlowGraph::printDotFile(fg, _irManager.getMethodDesc(), "error");
}
assert(inst->getOpcode() != Op_AddScaledIndex);
}
break;
}
case Op_Phi:
{
if(Log::isEnabled()) {
Log::out() << "Consider phi ";
inst->print(Log::out());
Log::out() << ::std::endl;
}
bool rematerialize = false;
PhiInst* phi = inst->asPhiInst();
SsaOpnd* base = NULL;
U_32 nSrcs = phi->getNumSrcOperands();
for(U_32 i = 0; i < nSrcs; ++i) {
SsaVarOpnd* src = phi->getSrc(i)->asSsaVarOpnd();
assert(src != NULL);
if(_baseMap.find(src) != _baseMap.end()) {
SsaOpnd* candidate = _baseMap[src];
if(base == NULL)
base = candidate;
else if(base != candidate)
rematerialize = true;
} else {
if(_staticMap.find(src) == _staticMap.end()) {
// Not recorded as a static
if(Log::isEnabled()) {
Log::out() << "Undefined arg ";
src->print(Log::out());
Log::out() << " - redo" << ::std::endl;
}
done = false;
} else {
_staticMap.insert(dst);
}
}
}
if(!rematerialize) {
if(base != NULL)
_baseMap[dst] = base;
} else {
if(Log::isEnabled()) {
Log::out() << "Converting " << ::std::endl;
FlowGraph::print(Log::out(), node);
Log::out() << "Rematerialize for " << ::std::endl;
phi->print(Log::out());
Log::out() << std::endl;
}
Opcode opcode = NumOpcodes;
FieldDesc* fieldDesc = NULL;
Type* elementType = NULL;
U_32 numArgs = 0;
StlVector<SsaTmpOpnd*> args(mm);
StlVector<Type*> argTypes(mm);
StlVector<SsaTmpOpnd*> defs(mm);
for(U_32 i = 0; i < nSrcs; ++i) {
SsaVarOpnd* src = phi->getSrc(i)->asSsaVarOpnd();
assert(src != NULL);
Inst* stVar = src->getInst();
assert(stVar->getOpcode() == Op_StVar);
DeadCodeEliminator::copyPropagate(stVar);
SsaTmpOpnd* def = stVar->getSrc(0)->asSsaTmpOpnd();
assert(def != NULL);
assert(defs.size() == i);
defs.push_back(def);
Inst* defInst = def->getInst();
if(Log::isEnabled()) {
Log::out() << "Process input " << ::std::endl;
defInst->print(Log::out());
Log::out() << ::std::endl;
}
if(opcode == NumOpcodes) {
opcode = defInst->getOpcode();
numArgs = defInst->getNumSrcOperands();
switch(opcode) {
case Op_LdFieldAddr:
fieldDesc = defInst->asFieldAccessInst()->getFieldDesc();
break;
case Op_LdElemAddr:
case Op_LdArrayBaseAddr:
elementType = defInst->asTypeInst()->getTypeInfo();
break;
default:
break;
}
for(U_32 j = 0; j < numArgs; ++j) {
SsaTmpOpnd* arg = defInst->getSrc(j)->asSsaTmpOpnd();
assert(arg != NULL);
if(Log::isEnabled()) {
Log::out() << "Initializing ";
arg->print(Log::out());
Log::out() << ::std::endl;
}
args.push_back(arg);
argTypes.push_back(arg->getType());
}
} else {
Opcode newOpcode = defInst->getOpcode();
if( !(opcode == newOpcode) ) assert(0);
assert(numArgs == defInst->getNumSrcOperands());
switch(opcode) {
case Op_LdFieldAddr:
assert(fieldDesc == defInst->asFieldAccessInst()->getFieldDesc());
break;
case Op_LdElemAddr:
case Op_LdArrayBaseAddr:
assert(elementType == defInst->asTypeInst()->getTypeInfo());
break;
default:
break;
}
for(U_32 j = 0; j < numArgs; ++j) {
SsaTmpOpnd* arg = defInst->getSrc(j)->asSsaTmpOpnd();
if(Log::isEnabled()) {
Log::out() << "Recording ";
arg->print(Log::out());
Log::out() << ::std::endl;
}
if(args[j] != arg) {
Log::out() << "Must create phi for arg " << (int) j << ::std::endl;
args[j] = NULL;
argTypes[j] = _irManager.getTypeManager().getCommonType(argTypes[j], arg->getType());
}
}
}
}
OpndManager& opndManager = _irManager.getOpndManager();
InstFactory& instFactory = _irManager.getInstFactory();
Inst* last = inst;
for(U_32 j = 0; j < numArgs; ++j) {
if(args[j] == NULL) {
VarOpnd* var = opndManager.createVarOpnd(argTypes[j], false);
if(Log::isEnabled()) {
Log::out() << "Creating ";
var->print(Log::out());
Log::out() << " for arg " << (int) j << ::std::endl;
}
SsaTmpOpnd* tmp = opndManager.createSsaTmpOpnd(argTypes[j]);
Opnd** phiArgs = new (_irManager.getMemoryManager()) Opnd*[nSrcs];
for(U_32 i = 0; i < nSrcs; ++i) {
SsaTmpOpnd* def = defs[i];
Inst* defInst = def->getInst();
SsaVarOpnd* ssaVar = opndManager.createSsaVarOpnd(var);
phiArgs[i] = ssaVar;
SsaTmpOpnd* src = defInst->getSrc(j)->asSsaTmpOpnd();
Inst* stVar = instFactory.makeStVar(ssaVar, src);
stVar->insertBefore(defInst);
if(src->getType()->isManagedPtr()) {
if(_baseMap.find(src) != _baseMap.end()) {
_baseMap[ssaVar] = _baseMap[src];
}
else {
if(Log::isEnabled()) {
Log::out() << "Can't find base for ";
src->print(Log::out());
Log::out() << " - redo" << ::std::endl;
}
done = false;
}
}
}
SsaVarOpnd* ssaDst = opndManager.createSsaVarOpnd(var);
Inst* phiInst = instFactory.makePhi(ssaDst, nSrcs, phiArgs);
phiInst->insertAfter(last);
last = phiInst;
Inst* ldVar = instFactory.makeLdVar(tmp, ssaDst);
ldVar->insertAfter(last);
last = ldVar;
args[j] = tmp;
}
}
SsaVarOpnd* dst = phi->getDst()->asSsaVarOpnd();
SsaTmpOpnd* tmp = opndManager.createSsaTmpOpnd(dst->getType());
Inst* redo = NULL;
switch (opcode) {
case Op_LdFieldAddr:
assert(fieldDesc != NULL);
redo = instFactory.makeLdFieldAddr(tmp, args[0], fieldDesc);
break;
case Op_LdElemAddr:
assert(elementType != NULL);
redo = instFactory.makeLdElemAddr(elementType, tmp, args[0], args[1]);
break;
case Op_LdArrayBaseAddr:
assert(elementType != NULL);
redo = instFactory.makeLdArrayBaseAddr(elementType, tmp, args[0]);
break;
case Op_AddScaledIndex:
redo = instFactory.makeAddScaledIndex(tmp, args[0], args[1]);
break;
case Op_LdLockAddr:
// this might work if this happens:
redo = instFactory.makeLdLockAddr(tmp, args[0]);
break;
default:
assert(0);
}
redo->insertAfter(last);
last = redo;
Inst* stVar = instFactory.makeStVar(dst, tmp);
stVar->insertAfter(last);
Inst* prev = inst->getPrevInst();
inst->unlink();
inst = prev;
if(Log::isEnabled()) {
Log::out() << "to " << ::std::endl;
FlowGraph::print(Log::out(), node);
}
}
break;
}
default:
assert(0);
}
}
}
}
}
}
}
} //namespace Jitrino