| /* |
| * 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 Mikhail Y. Fursov |
| */ |
| |
| #include "Ia32Inst.h" |
| #include "Ia32GCSafePoints.h" |
| #include "XTimer.h" |
| #include "BitSet.h" |
| #include "Ia32CgUtils.h" |
| |
| |
| namespace Jitrino |
| { |
| |
| #define LIVENESS_FILTER_INST_INTERVAL 128 |
| #define LIVENESS_FILTER_OBJ_INST_INTERVAL 64 |
| |
| static CountTime phase0Timer("ia32::gcpointsinfo::phase0"), |
| phase1Timer("ia32::gcpointsinfo::phase1"), |
| phase1Checker("ia32::gcpointsinfo::phase1Checker"), |
| phase2Timer("ia32::gcpointsinfo::phase2"), |
| phase2Checker("ia32::gcpointsinfo::phase2Checker"), |
| saveResultsTimer("ia32::gcpoints::saveResults"); |
| |
| namespace Ia32 { |
| |
| static ActionFactory<GCPointsBaseLiveRangeFixer> _gcpoints ("gcpoints"); |
| |
| // does nothing except allows to debugger to not loose local stack if method called is last in code |
| static void dbg_point() {} |
| |
| // Helper function, removes element idx from vector in O(1) time |
| // Moves last element in vector to idx and reduce size by 1 |
| // WARN: elements order could be changed! |
| static void removeElementAt(GCSafePointPairs& pairs, U_32 idx) { |
| assert(!pairs.empty()); |
| U_32 newSize = (U_32)pairs.size() - 1; |
| assert(idx<=newSize); |
| if (idx < newSize) { |
| pairs[idx] = pairs[newSize]; |
| } |
| pairs.resize(newSize); |
| } |
| |
| |
| void GCSafePointsInfo::removePairByMPtrOpnd(GCSafePointPairs& pairs, const Opnd* mptr) const { |
| #ifdef _DEBUG_ |
| U_32 nPairs = 0; |
| #endif |
| for (int i = (int)pairs.size(); --i>=0;) { |
| MPtrPair& p = pairs[i]; |
| if (p.mptr == mptr) { |
| removeElementAt(pairs, i); |
| #ifdef _DEBUG_ |
| nPairs++; |
| #else |
| break; //only one pair in one point for mptr is allowed ! |
| #endif |
| } |
| } |
| #ifdef _DEBUG_ |
| assert(nPairs <= 1); |
| #endif |
| } |
| |
| MPtrPair* GCSafePointsInfo::findPairByMPtrOpnd(GCSafePointPairs& pairs, const Opnd* mptr) { |
| for (GCSafePointPairs::iterator it = pairs.begin(), end = pairs.end(); it!=end; ++it) { |
| MPtrPair& p = *it; |
| if(p.getMptr() == mptr) { |
| return &p; |
| } |
| } |
| return NULL; |
| } |
| |
| GCSafePointsInfo::GCSafePointsInfo(MemoryManager& _mm, IRManager& _irm, Mode _mode) : |
| mm(_mm), irm(_irm), pairsByNode(_mm), pairsByGCSafePointInstId(mm), |
| livenessFilter(mm), ambiguityFilters(mm), staticMptrs(mm), allowMerging(false), opndsAdded(0), instsAdded(0), mode(_mode) |
| { |
| _calculate(); |
| } |
| |
| |
| bool GCSafePointsInfo::graphHasSafePoints(const IRManager& irm){ |
| const Nodes& nodes = irm.getFlowGraph()->getNodes(); |
| for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) { |
| Node* node = *it; |
| if (node->isBlockNode()) { |
| if (blockHasSafePoints((BasicBlock*)node)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| bool GCSafePointsInfo::blockHasSafePoints(const Node* b) { |
| for (Inst * inst=(Inst*)b->getFirstInst(); inst!=NULL; inst=inst->getNextInst()){ |
| if (isGCSafePoint(inst)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| U_32 GCSafePointsInfo::getNumSafePointsInBlock(const Node* b, const GCSafePointPairsMap* pairsMap ) { |
| U_32 n = 0; |
| for (Inst * inst=(Inst*)b->getFirstInst(); inst!=NULL; inst=inst->getNextInst()){ |
| if (isGCSafePoint(inst)) { |
| if (pairsMap!=NULL) { // if pairs!=NULL counts only gcpoints with non-empty pairs |
| GCSafePointPairsMap::const_iterator it = pairsMap->find(inst->getId()); |
| assert(it!=pairsMap->end()); |
| GCSafePointPairs* pairs = it->second; |
| if (pairs->empty()) { |
| continue; |
| } |
| } |
| n++; |
| } |
| } |
| return n; |
| } |
| |
| |
| void GCSafePointsInfo::_calculate() { |
| assert(pairsByNode.empty()); |
| assert(pairsByGCSafePointInstId.empty()); |
| |
| //check if there are no safepoints in cfg at all |
| if (!graphHasSafePoints(irm)) { |
| return; |
| } |
| irm.updateLivenessInfo(); |
| irm.updateLoopInfo(); //to use _hasLoops property |
| |
| insertLivenessFilters(); |
| allowMerging = true; |
| calculateMPtrs(); |
| if (mode == MODE_2_CALC_OFFSETS) { |
| assert(opndsAdded == 0); |
| return; |
| } |
| if ( opndsAdded > 0 ) { |
| irm.invalidateLivenessInfo(); |
| irm.updateLivenessInfo(); |
| } |
| allowMerging = false; |
| filterLiveMPtrsOnGCSafePoints(); |
| } |
| |
| |
| void GCSafePointsInfo::insertLivenessFilters() { |
| AutoTimer tm(phase0Timer); |
| const Nodes& nodes = irm.getFlowGraph()->getNodes(); |
| for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) { |
| Node* node = *it; |
| if (node->isBlockNode()) { |
| U_32 numInsts = node->getInstCount(); |
| if (numInsts < LIVENESS_FILTER_INST_INTERVAL) { |
| continue; |
| } |
| U_32 objOps = 0; |
| for (Inst* inst = (Inst*)node->getLastInst(); inst!=NULL; inst = inst->getPrevInst()) { |
| if ( (inst->getOpndCount() == 0 && ((inst->getKind() == Inst::Kind_MethodEndPseudoInst) |
| || (inst->getKind() == Inst::Kind_MethodEntryPseudoInst))) |
| || (inst->getMnemonic() == Mnemonic_NOP |
| || inst->getKind() == Inst::Kind_EmptyPseudoInst) ) |
| { |
| continue; |
| } |
| Opnd* opnd = inst->getOpnd(0); // VSH: 0 - ???? |
| if (opnd->getType()->isObject() || opnd->getType()->isManagedPtr()) { |
| objOps++; |
| if (objOps == LIVENESS_FILTER_OBJ_INST_INTERVAL) { |
| break; |
| } |
| } |
| } |
| if (objOps < LIVENESS_FILTER_OBJ_INST_INTERVAL) { |
| continue; |
| } |
| // insert liveness filter for every n-th inst in block |
| int nFilters = numInsts / LIVENESS_FILTER_INST_INTERVAL; |
| BitSet* ls = new (mm) BitSet(mm, irm.getOpndCount()); |
| irm.getLiveAtExit(node, *ls); |
| int nFiltersLeft = nFilters; |
| int instsInInterval = 0; |
| for (Inst* inst = (Inst*)node->getLastInst(); nFiltersLeft>0; inst = inst->getPrevInst()) { |
| assert(inst!=NULL); |
| irm.updateLiveness(inst, *ls); |
| instsInInterval++; |
| if (instsInInterval == LIVENESS_FILTER_INST_INTERVAL) { |
| nFiltersLeft--; |
| instsInInterval = 0; |
| setLivenessFilter(inst, ls); |
| if (nFiltersLeft != 0) { |
| ls = new (mm) BitSet(*ls); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void GCSafePointsInfo::setLivenessFilter(const Inst* inst, const BitSet* ls) { |
| livenessFilter[inst] = ls; |
| } |
| |
| const BitSet* GCSafePointsInfo::findLivenessFilter(const Inst* inst) const { |
| StlMap<const Inst*, const BitSet*>::const_iterator it = livenessFilter.find(inst); |
| if (it == livenessFilter.end()) { |
| return NULL; |
| } |
| return it->second; |
| } |
| |
| void GCSafePointsInfo::calculateMPtrs() { |
| AutoTimer tm(phase1Timer); |
| assert(pairsByNode.empty()); |
| ControlFlowGraph* fg = irm.getFlowGraph(); |
| LoopTree* lt =fg->getLoopTree(); |
| |
| const Nodes& postOrderedNodes = fg->getNodesPostOrder(); |
| |
| //prepare structures |
| pairsByNode.resize(fg->getNodeCount()); |
| for (Nodes::const_iterator it = postOrderedNodes.begin(), end = postOrderedNodes.end(); it!=end; ++it) { |
| Node* node = *it; |
| if (node!=fg->getExitNode() && node!=fg->getUnwindNode()) { |
| pairsByNode[node->getDfNum()] = new (mm) GCSafePointPairs(mm); |
| } |
| } |
| |
| GCSafePointPairs tmpPairs(mm); |
| U_32 nIterations = lt->getMaxLoopDepth() + 1; |
| bool changed = true; |
| bool restart = false; |
| |
| #ifdef _DEBUG |
| nIterations++; //one more extra iteration to check on debug that nothing changed |
| #endif |
| for (U_32 iteration = 0; iteration < nIterations ; ) { |
| changed = false; |
| for (Nodes::const_reverse_iterator it = postOrderedNodes.rbegin(), end = postOrderedNodes.rend(); it!=end; ++it) { |
| Node* node = *it; |
| if (node == fg->getExitNode() || node == fg->getUnwindNode()) { |
| continue; |
| } |
| tmpPairs.clear(); |
| U_32 instsBefore = instsAdded; |
| derivePairsOnEntry(node, tmpPairs); |
| if (instsBefore!=instsAdded) { |
| restart = true; |
| break; |
| } |
| GCSafePointPairs& nodePairs = *pairsByNode[node->getDfNum()]; |
| if (node->isBlockNode()) { |
| for (Inst * inst=(Inst*)node->getFirstInst(); inst!=NULL; inst=inst->getNextInst()){ |
| updatePairsOnInst(inst, tmpPairs); |
| if (isGCSafePoint(inst)) { //saving results on safepoint |
| GCSafePointPairs* instPairs = NULL; |
| instPairs = pairsByGCSafePointInstId[inst->getId()]; |
| if (instPairs == NULL) { |
| instPairs = new (mm) GCSafePointPairs(mm); |
| pairsByGCSafePointInstId[inst->getId()] = instPairs; |
| } |
| instPairs->clear(); |
| instPairs->insert(instPairs->end(), tmpPairs.begin(), tmpPairs.end()); |
| } |
| } |
| if (iteration == 0 || !hasEqualElements(nodePairs, tmpPairs)) { |
| changed = true; |
| nodePairs.swap(tmpPairs); |
| } |
| } else { |
| nodePairs.swap(tmpPairs); |
| } |
| } |
| if (restart) { //clear all cached pairs and restart all iterations |
| for (Nodes::const_iterator it2 = postOrderedNodes.begin(), end2 = postOrderedNodes.end(); it2!=end2; ++it2) { |
| Node* node = *it2; |
| GCSafePointPairs* pairs = pairsByNode[node->getDfNum()]; |
| assert(pairs!=NULL || node->isExitNode() || node == fg->getUnwindNode()); |
| if (pairs!=NULL) { |
| pairs->clear(); |
| } |
| } |
| iteration = 0; |
| restart = false; |
| continue; |
| } |
| if (!changed) { |
| break; |
| } |
| iteration++; |
| } |
| #ifdef _DEBUG |
| assert(!changed); |
| checkPairsOnNodeExits(); |
| #endif |
| } |
| |
| static inline int adjustOffsets(I_32 offsetBefore, I_32 dOffset) { |
| if (offsetBefore == MPTR_OFFSET_UNKNOWN || dOffset == MPTR_OFFSET_UNKNOWN) { |
| return MPTR_OFFSET_UNKNOWN; |
| } |
| int res = offsetBefore + dOffset; |
| assert(res>=0); |
| return res; |
| } |
| |
| static bool isHeapBase(Opnd* immOpnd) { |
| assert(immOpnd->isPlacedIn(OpndKind_Imm)); |
| #ifndef _EM64T_ |
| return false; |
| #else |
| int64 heapBase = (int64)VMInterface::getHeapBase(); |
| return immOpnd->getImmValue() == heapBase; |
| #endif |
| } |
| |
| I_32 GCSafePointsInfo::getOffsetFromImmediate(Opnd* offsetOpnd) const { |
| if (offsetOpnd->isPlacedIn(OpndKind_Immediate)) { |
| if (offsetOpnd->getImmValue() == 0 && offsetOpnd->getRuntimeInfo()!=NULL) { |
| irm.resolveRuntimeInfo(offsetOpnd); |
| } |
| assert(!isHeapBase(offsetOpnd)); |
| return (I_32)offsetOpnd->getImmValue(); |
| } |
| return MPTR_OFFSET_UNKNOWN; |
| } |
| |
| |
| void GCSafePointsInfo::runLivenessFilter(Inst* inst, GCSafePointPairs& pairs) const { |
| if (!pairs.empty()) { |
| const BitSet* livenessFilter = findLivenessFilter(inst); |
| if (livenessFilter!=NULL) { |
| for (int i = (int)pairs.size(); --i>=0;) { |
| MPtrPair& p = pairs[i]; |
| if (!livenessFilter->getBit(p.mptr->getId())) { |
| removeElementAt(pairs, i); |
| } |
| } |
| } |
| } |
| } |
| |
| Opnd* GCSafePointsInfo::getBaseAccordingMode(Opnd* opnd) const { |
| //MODE_2_CALC_OFFSETS does not have valid base info, because it does not resolves ambiguity! |
| return mode == MODE_1_FIX_BASES ? opnd : NULL; |
| } |
| |
| |
| void GCSafePointsInfo::updateMptrInfoInPairs(GCSafePointPairs& res, Opnd* newMptr, Opnd* fromOpnd, int offset, bool fromOpndIsBase) { |
| assert(!isStaticFieldMptr(newMptr)); |
| if (fromOpndIsBase) { //from is base |
| removePairByMPtrOpnd(res, newMptr); |
| MPtrPair p(newMptr, getBaseAccordingMode(fromOpnd), offset); |
| res.push_back(p); |
| } else { //fromOpnd is mptr |
| MPtrPair* fromPair = findPairByMPtrOpnd(res, fromOpnd); |
| assert(fromPair != NULL); |
| int newOffset = adjustOffsets(fromPair->offset, offset); |
| //remove old pair, add new |
| MPtrPair* pair = newMptr == fromOpnd ? fromPair : findPairByMPtrOpnd(res, newMptr); |
| if (pair != NULL) { //reuse old pair |
| pair->offset = newOffset; |
| pair->base = fromPair->base; |
| pair->mptr = newMptr; |
| } else { // new mptr, was not used before |
| MPtrPair toPair(newMptr, fromPair->base, newOffset); |
| res.push_back(toPair); |
| } |
| } |
| } |
| |
| static void add_static(Opnd* opnd, StlSet<Opnd*>& set, Opnd* cause) { |
| set.insert(opnd); |
| if (Log::isEnabled()) { |
| Log::out()<<"Registering as static opnd, firstId="<<opnd->getFirstId()<<" reason-opndid:"<<(cause?cause->getFirstId() : (U_32)-1)<<std::endl; |
| } |
| } |
| |
| void GCSafePointsInfo::filterStaticMptrs(Inst* inst) { |
| if (inst->hasKind(Inst::Kind_CallInst)) { |
| CallInst* callInst = (CallInst*)inst; |
| Opnd::RuntimeInfo * rt = callInst->getRuntimeInfo(); |
| if (rt && rt->getKind() == Opnd::RuntimeInfo::Kind_HelperAddress |
| && ((VM_RT_SUPPORT)(POINTER_SIZE_INT)rt->getValue(0) == VM_RT_GET_STATIC_FIELD_ADDR_WITHRESOLVE)) |
| { |
| Opnd* callRes = callInst->getOpnd(0); |
| add_static(callRes, staticMptrs, NULL); |
| } |
| } else if (inst->getMnemonic() == Mnemonic_MOV) { |
| Opnd* toOpnd = inst->getOpnd(0); |
| Opnd* fromOpnd = inst->getOpnd(1); |
| if (!isManaged(fromOpnd) && !isManaged(toOpnd)) { |
| return; |
| } |
| if (fromOpnd->isPlacedIn(OpndKind_Mem) && fromOpnd->getMemOpndKind() == MemOpndKind_Heap) { |
| //loading value from memory -> can be object only, not is a mptr to a static field |
| return; |
| } |
| if (staticMptrs.find(fromOpnd)!=staticMptrs.end()) { |
| add_static(toOpnd, staticMptrs, fromOpnd); |
| } else if (fromOpnd->isPlacedIn(OpndKind_Imm)) { //imms are compile-time known values -> can only be static |
| Opnd::RuntimeInfo* info = fromOpnd->getRuntimeInfo(); |
| bool isStaticFieldMptr = info!=NULL && info->getKind() == Opnd::RuntimeInfo::Kind_StaticFieldAddress; |
| if (isStaticFieldMptr) { |
| add_static(fromOpnd, staticMptrs, NULL); |
| add_static(toOpnd, staticMptrs, fromOpnd); |
| } |
| } |
| } |
| } |
| |
| |
| void GCSafePointsInfo::updatePairsOnInst(Inst* inst, GCSafePointPairs& res) { |
| runLivenessFilter(inst, res);//filter pairs with dead mptrs from list |
| |
| Inst::Opnds opnds(inst, Inst::OpndRole_Explicit | Inst::OpndRole_Auxilary |Inst::OpndRole_UseDef); |
| U_32 defIndex = opnds.begin(); |
| U_32 useIndex1 = opnds.next(defIndex); |
| |
| if (defIndex >= opnds.end() || useIndex1 >= opnds.end() |
| || (inst->getOpndRoles(defIndex) & Inst::OpndRole_Def) == 0 |
| || (inst->getOpndRoles(useIndex1) & Inst::OpndRole_Use) == 0 ) |
| { |
| return; |
| } |
| Opnd* opnd = inst->getOpnd(defIndex); |
| Type* opndType = opnd->getType(); |
| if (!opndType->isObject() && !opndType->isManagedPtr()) { |
| return; |
| } |
| filterStaticMptrs(inst); |
| if (isStaticFieldMptr(opnd)) { |
| removePairByMPtrOpnd(res, opnd); |
| return; // no more analysis required |
| } |
| if (mode == MODE_1_FIX_BASES) { //3 addr form |
| if (opndType->isObject()) { // MODE_1_FIX_BASES -> obj & mptr types are not coalesced |
| StlMap<Inst*, Opnd*>::const_iterator ait; |
| if (!ambiguityFilters.empty() && ((ait = ambiguityFilters.find(inst))!=ambiguityFilters.end())) { |
| //mptr ambiguity filter -> replace old base with new(opnd) in pairs |
| const Opnd* mptr = ait->second; |
| MPtrPair* pair = findPairByMPtrOpnd(res, mptr); |
| #ifdef _DEBUG |
| Opnd* oldBase = inst->getOpnd(useIndex1); //inst is: newBase = oldBase |
| assert(pair!=NULL); |
| assert(pair->getBase() == oldBase || pair->getBase() == opnd); // ==opnd-> already resolved in dominant block |
| #endif |
| |
| pair->base = opnd; |
| } |
| } else { //def of mptr |
| // detect which operand is base |
| Opnd* fromOpnd = inst->getOpnd(useIndex1); |
| U_32 useIndex2 = opnds.next(useIndex1); |
| Opnd* fromOpnd2 = useIndex2!=opnds.end() ? inst->getOpnd(useIndex2) : NULL; |
| if (!(fromOpnd->getType()->isObject() || fromOpnd->getType()->isManagedPtr())) { |
| assert(fromOpnd2!=NULL); |
| Opnd* tmp = fromOpnd; fromOpnd = fromOpnd2; fromOpnd2 = tmp; |
| } |
| assert(fromOpnd->getType()->isObject() || fromOpnd->getType()->isManagedPtr()); |
| I_32 offset = MPTR_OFFSET_UNKNOWN; |
| if (inst->getMnemonic() == Mnemonic_LEA) { |
| assert(fromOpnd->isPlacedIn(OpndKind_Memory)); |
| Opnd* scaleOpnd = fromOpnd->getMemOpndSubOpnd(MemOpndSubOpndKind_Scale); |
| if (scaleOpnd == NULL) { |
| Opnd* displOpnd = fromOpnd->getMemOpndSubOpnd(MemOpndSubOpndKind_Displacement); |
| assert(displOpnd!=NULL); |
| offset = (I_32)displOpnd->getImmValue(); |
| } |
| fromOpnd = fromOpnd->getMemOpndSubOpnd(MemOpndSubOpndKind_Base); |
| } else if (fromOpnd2!=NULL) { |
| Opnd* offsetOpnd = fromOpnd2; |
| offset = getOffsetFromImmediate(offsetOpnd); |
| } |
| updateMptrInfoInPairs(res, opnd, fromOpnd, offset, fromOpnd->getType()->isObject()); |
| dbg_point(); |
| } |
| } else { // mode == MODE_2_CALC_OFFSETS - 2 addr form |
| //we can't rely on base/mptr type info here. |
| //algorithm: |
| if (inst->hasKind(Inst::Kind_ControlTransferInst) || |
| inst->getMnemonic() == Mnemonic_MOVS8 || |
| inst->getMnemonic() == Mnemonic_MOVS16 || |
| inst->getMnemonic() == Mnemonic_MOVS32 || |
| inst->getMnemonic() == Mnemonic_MOVS64 || |
| inst->getMnemonic() == Mnemonic_CMPSB || |
| inst->getMnemonic() == Mnemonic_CMPSW || |
| inst->getMnemonic() == Mnemonic_CMPSD ) { |
| //Do nothing, calls return only bases |
| } else { |
| Opnd* fromOpnd = NULL; |
| I_32 offset = 0; |
| Mnemonic mn = inst->getMnemonic(); |
| |
| Mnemonic conditionalMnem = getBaseConditionMnemonic(mn); |
| if (conditionalMnem != Mnemonic_NULL) |
| mn = conditionalMnem; |
| |
| switch (mn) { |
| case Mnemonic_XOR: |
| case Mnemonic_MOV: |
| case Mnemonic_CMOVcc: |
| assert(mn != Mnemonic_XOR || inst->getOpnd(useIndex1)==opnd); |
| fromOpnd = inst->getOpnd(useIndex1); |
| break; |
| case Mnemonic_ADD: { |
| case Mnemonic_SUB: |
| fromOpnd = opnd; |
| Opnd* offsetOpnd = inst->getOpnd(useIndex1); |
| Opnd* immOffset = OpndUtils::findImmediateSource(offsetOpnd); |
| if (immOffset) { |
| if (isHeapBase(immOffset)) { |
| offset = 0; |
| } else { |
| offset = getOffsetFromImmediate(immOffset); |
| } |
| } else { |
| offset = MPTR_OFFSET_UNKNOWN; |
| } |
| } |
| break; |
| case Mnemonic_LEA: { |
| fromOpnd = inst->getOpnd(useIndex1); |
| assert(fromOpnd->isPlacedIn(OpndKind_Memory)); |
| Opnd* scaleOpnd = fromOpnd->getMemOpndSubOpnd(MemOpndSubOpndKind_Scale); |
| if (scaleOpnd == NULL) { |
| Opnd* displOpnd = fromOpnd->getMemOpndSubOpnd(MemOpndSubOpndKind_Displacement); |
| assert(displOpnd!=NULL); |
| offset = (I_32)displOpnd->getImmValue(); |
| } else { |
| offset = MPTR_OFFSET_UNKNOWN; |
| } |
| fromOpnd = fromOpnd->getMemOpndSubOpnd(MemOpndSubOpndKind_Base); |
| } |
| break; |
| default: assert(0); |
| } |
| bool fromIsMptrOrObj = fromOpnd->getType()->isObject() || fromOpnd->getType()->isManagedPtr(); |
| MPtrPair* fromPair = fromIsMptrOrObj ? findPairByMPtrOpnd(res, fromOpnd) : NULL; |
| |
| if (fromPair != NULL || offset!=0) {//opnd is mptr -> update pairs |
| updateMptrInfoInPairs(res, opnd, fromOpnd, offset, fromPair == NULL); |
| } else { |
| //new def of base -> we must remove all pairs where opnd acts as base or mptr |
| //the problem is that in MODE2 we do not save info about bases (no ambiguity resolution is done) |
| //so we can't match pairs where opnd is a base. |
| //Solution: let pairs derived from this base to live, due to the fact that |
| //such pairs must have static offset and base redefinition will not corrupt info. |
| //For pairs with unknown offset there is a live base -> MODE1 cares about it |
| removePairByMPtrOpnd(res, opnd); |
| } |
| } |
| } // else mode2 |
| } |
| |
| |
| |
| void GCSafePointsInfo::derivePairsOnEntry(const Node* node, GCSafePointPairs& res) { |
| assert(res.empty()); |
| // optimization: filter out those mptrs that are not live at entry. |
| // this situation is possible because of updatePairsOnInst() function does not track liveness |
| // and allows dead operands in pairs for block end; |
| const BitSet* ls = irm.getLiveAtEntry(node); |
| const Edges& edges=node->getInEdges(); |
| |
| assert(irm.getFlowGraph()->hasValidOrdering()); |
| |
| //step1: add all live pairs from pred edges into res |
| for (Edges::const_iterator ite = edges.begin(), ende = edges.end(); ite!=ende; ++ite) { |
| Edge* edge = *ite; |
| Node * predNode =edge->getSourceNode(); |
| const GCSafePointPairs& predPairs = *pairsByNode[predNode->getDfNum()]; |
| res.reserve(res.size() + predPairs.size()); |
| for (GCSafePointPairs::const_iterator it = predPairs.begin(), end = predPairs.end(); it!=end; ++it) { |
| const MPtrPair& predPair = *it; |
| if (ls->getBit(predPair.mptr->getId())) { |
| res.push_back(predPair);//by value |
| } |
| } |
| } |
| |
| //step 2: merge pairs with the same (mptr, base) and process ambiguos pairs |
| bool needToFilterDeadPairs = false; |
| U_32 instsAddedBefore = instsAdded; |
| std::sort(res.begin(), res.end()); |
| for (U_32 i=0, n = (U_32)res.size(); i < n; i++) { |
| MPtrPair& p1 = res[i]; |
| Opnd* newBase = NULL; //new base opnd to merge ambiguos mptrs |
| while (i+1 < n) { |
| const MPtrPair& p2 = res[i+1]; |
| if (p1.mptr == p2.mptr) { |
| if (p1.base == p2.base) { //same base & mptr -> merge offset |
| if (p1.offset != p2.offset) { |
| p1.offset = MPTR_OFFSET_UNKNOWN; |
| } |
| } else { //equal mptrs with a different bases -> add var=baseX to prevBlocks and replace pairs with new single pair |
| assert(mode == MODE_1_FIX_BASES); |
| assert(allowMerging); |
| if (newBase == NULL) { //first pass for this ambiguous mptrs fix them all |
| newBase = irm.newOpnd(p1.base->getType()); |
| opndsAdded++; |
| U_32 dInsts = 0; |
| for (Edges::const_iterator ite = edges.begin(), ende = edges.end(); ite!=ende; ++ite) { |
| Edge* edge = *ite; |
| Node * predNode =edge->getSourceNode(); |
| GCSafePointPairs& predPairs = *pairsByNode[predNode->getDfNum()]; |
| for (GCSafePointPairs::iterator it = predPairs.begin(), end = predPairs.end(); it!=end; ++it) { |
| MPtrPair& predPair = *it; |
| if (predPair.mptr == p1.mptr) { //ok remove this pair, add vardef, add new pair |
| Opnd* oldBase = predPair.base; |
| assert(predNode->isBlockNode()); |
| BasicBlock* predBlock = (BasicBlock*)predNode; |
| //add var def inst to predBlock |
| Inst* baseDefInst = irm.newCopyPseudoInst(Mnemonic_MOV, newBase, oldBase); |
| Inst* lastInst = (Inst*)predBlock->getLastInst(); |
| if (lastInst!=NULL && lastInst->hasKind(Inst::Kind_ControlTransferInst)) { |
| baseDefInst->insertBefore(lastInst); |
| } else { |
| predBlock->appendInst(baseDefInst); |
| } |
| if (predPair.offset!=p1.offset) { |
| p1.offset = MPTR_OFFSET_UNKNOWN; |
| } |
| dInsts++; |
| ambiguityFilters[baseDefInst] = p1.mptr; |
| //and now we should remove oldBase from next nodes in topological ordering |
| //oldBase could be cached and merging process could be started for oldBase and newBase in inner loops heads |
| //we will do it one time after the all predPairs is processed for the current node |
| break;//go to next node processing |
| } |
| } |
| } |
| p1.base = newBase; |
| assert(dInsts > 1); |
| instsAdded+=dInsts; |
| } |
| } |
| needToFilterDeadPairs = true; |
| i++; |
| } else { |
| break; |
| } |
| } //while |
| } |
| |
| //step3: |
| if (instsAdded==instsAddedBefore && needToFilterDeadPairs) { //if nstsAdded!=instsAddedBefore -> recalculation restarts |
| //step 3: leave only p1(see step2) pairs |
| //unique -> remove all pairs with equal mptr but the first |
| GCSafePointPairs::iterator newEnd = std::unique(res.begin(), res.end(), &MPtrPair::equalMptrs); |
| res.resize(newEnd - res.begin()); |
| } |
| |
| |
| } |
| |
| void GCSafePointsInfo::filterLiveMPtrsOnGCSafePoints() { |
| AutoTimer tm(phase2Timer); |
| assert(!pairsByNode.empty()); |
| //unoptimized impl -> analyzing all blocks -> could be moved to GCMap |
| const Nodes& nodes = irm.getFlowGraph()->getNodes(); |
| BitSet ls(mm, irm.getOpndCount()); |
| for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) { |
| Node* node = *it; |
| if (!node->isBlockNode()) { |
| continue; |
| } |
| U_32 nSafePoints = getNumSafePointsInBlock((BasicBlock*)node, &pairsByGCSafePointInstId); |
| if (nSafePoints == 0) { |
| continue; |
| } |
| // ok this basic block has safepoints |
| U_32 nSafePointsTmp = nSafePoints; |
| // remove pairs with dead mptr, use liveness to do it; |
| ls.clear(); |
| irm.getLiveAtExit(node, ls); |
| nSafePointsTmp = nSafePoints; |
| for (Inst * inst=(Inst*)node->getLastInst();;inst=inst->getPrevInst()) { |
| assert(inst!=NULL); |
| if (isGCSafePoint(inst)) { |
| GCSafePointPairs& pairs = *pairsByGCSafePointInstId[inst->getId()]; |
| if (!pairs.empty()) { |
| nSafePointsTmp--; |
| for (int i = (int)pairs.size(); --i>=0;) { |
| MPtrPair& p = pairs[i]; |
| if (!ls.getBit(p.mptr->getId())) { |
| removeElementAt(pairs, i); |
| } |
| } |
| } |
| if (nSafePointsTmp == 0) { |
| break; |
| } |
| } |
| irm.updateLiveness(inst, ls); |
| } |
| } |
| #ifdef _DEBUG |
| checkPairsOnGCSafePoints(); |
| #endif |
| } |
| |
| /// checks that sets of mptrs that come to any node from any edge are equal |
| void GCSafePointsInfo::checkPairsOnNodeExits() const { |
| AutoTimer tm(phase1Checker); |
| assert(!pairsByNode.empty()); |
| ControlFlowGraph* fg = irm.getFlowGraph(); |
| const Nodes& nodes = fg->getNodes(); |
| for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it ) { |
| Node* node = *it; |
| if (node == fg->getUnwindNode() || node==fg->getExitNode()) { |
| continue; |
| } |
| const BitSet* ls = irm.getLiveAtEntry(node); |
| if(node->hasTwoOrMorePredEdges()) { |
| const Edges& edges=node->getInEdges(); |
| Edge* edge1 = edges.front(); |
| Node * predNode1 =edge1->getSourceNode(); |
| const GCSafePointPairs& pairs1 = *pairsByNode[predNode1->getDfNum()]; |
| //ensure that first edge mptr set is equal to edge2..N set |
| for (Edges::const_iterator ite = edges.begin() + 1, ende = edges.end(); ite!=ende; ++ite) { |
| Edge* edge2 = *ite; |
| Node * predNode2 =edge2->getSourceNode(); |
| const GCSafePointPairs& pairs2 = *pairsByNode[predNode2->getDfNum()]; |
| //now check that for every mptr in pairs1 there is a pair in pairs2 with the same mptr |
| for (U_32 i1 = 0, n1 = (U_32)pairs1.size();i1<n1; i1++) { |
| const MPtrPair& p1 = pairs1[i1]; |
| if (ls->getBit(p1.mptr->getId())) { |
| for (U_32 i2 = 0; ; i2++) { |
| assert(i2 < pairs2.size()); |
| const MPtrPair& p2 = pairs2[i2]; |
| if (p1.mptr == p2.mptr) { |
| break; |
| } |
| } |
| } |
| } |
| // and vice versa |
| for (U_32 i2 = 0, n2 = (U_32)pairs2.size();i2<n2; i2++) { |
| const MPtrPair& p2 = pairs2[i2]; |
| if (ls->getBit(p2.mptr->getId())) { |
| for (U_32 i1 = 0; ; i1++) { |
| assert(i1 < pairs1.size()); |
| const MPtrPair& p1 = pairs1[i1]; |
| if (p1.mptr == p2.mptr) { |
| break; |
| } |
| } |
| } |
| } |
| } //for edge2...edge[N] |
| } |
| } |
| } |
| |
| // checks that we've collected pairs for every live mptr on safe point |
| void GCSafePointsInfo::checkPairsOnGCSafePoints() const { |
| if (mode == MODE_2_CALC_OFFSETS) { // check is done using type info. Type info is not available for mode2 |
| return; |
| } |
| AutoTimer tm(phase2Checker); |
| const Nodes& nodes = irm.getFlowGraph()->getNodes(); |
| U_32 nOpnds = irm.getOpndCount(); |
| BitSet ls(mm, nOpnds); |
| for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) { |
| Node* node = *it; |
| if (!node->isBlockNode()) { |
| continue; |
| } |
| U_32 nSafePoints = getNumSafePointsInBlock((BasicBlock*)node); |
| if (nSafePoints== 0) { |
| continue; |
| } |
| U_32 nSafePointsTmp = nSafePoints; |
| irm.getLiveAtExit(node, ls); |
| for (Inst * inst=(Inst*)node->getLastInst(); nSafePointsTmp > 0; inst=inst->getPrevInst()) { |
| assert(inst!=NULL); |
| if (isGCSafePoint(inst)) { |
| nSafePointsTmp--; |
| GCSafePointPairsMap::const_iterator mit = pairsByGCSafePointInstId.find(inst->getId()); |
| assert(mit!=pairsByGCSafePointInstId.end()); |
| const GCSafePointPairs& pairs = *mit->second; |
| BitSet::IterB liveOpnds(ls); |
| for (int i = liveOpnds.getNext(); i != -1; i = liveOpnds.getNext()) { |
| Opnd* opnd = irm.getOpnd(i); |
| if (opnd->getType()->isManagedPtr()) { |
| if (staticMptrs.find(opnd)!= staticMptrs.end()) { |
| continue; |
| } |
| for (U_32 j=0; ; j++) { |
| assert(j<pairs.size()); |
| const MPtrPair& pair = pairs[j]; |
| if (pair.mptr == opnd) { |
| break; |
| } |
| } |
| } |
| } //for opnds |
| } |
| irm.updateLiveness(inst, ls); |
| } |
| } |
| } |
| |
| static U_32 select_1st(const std::pair<U_32, GCSafePointPairs*>& p) {return p.first;} |
| |
| |
| void GCSafePointsInfo::dump(const char* stage) const { |
| Log::out()<<"========================================================================"<<std::endl; |
| Log::out()<<"__IR_DUMP_BEGIN__: pairs dump"<<std::endl; |
| Log::out()<<"========================================================================"<<std::endl; |
| //sort by inst id |
| const GCSafePointPairsMap& map = pairsByGCSafePointInstId; |
| StlVector<U_32> insts(mm, map.size()); |
| std::transform(map.begin(), map.end(), insts.begin(), select_1st); |
| std::sort(insts.begin(), insts.end()); |
| |
| //for every inst sort by mptr id and dump |
| for (size_t i = 0; i<insts.size(); i++) { |
| U_32 id = insts[i]; |
| const GCSafePointPairs* pairs = map.find(id)->second; |
| Log::out()<<"inst="<<id<<" num_pairs="<<pairs->size()<<std::endl; |
| GCSafePointPairs cloned = *pairs; |
| std::sort(cloned.begin(), cloned.end()); |
| for(size_t j=0; j<cloned.size(); j++) { |
| MPtrPair& p = cloned[j]; |
| Log::out()<<" mptr="<< p.mptr->getFirstId() |
| <<" base="<<(p.base!=NULL?(int)p.base->getFirstId():-1) |
| <<" offset="<<p.offset<<std::endl; |
| } |
| } |
| Log::out()<<"========================================================================"<<std::endl; |
| Log::out()<<"__IR_DUMP_END__: pairs dump"<<std::endl; |
| Log::out()<<"========================================================================"<<std::endl; |
| |
| } |
| |
| #define MAX(a,b) (((a) > (b)) ? (a) : (b)) |
| |
| void GCPointsBaseLiveRangeFixer::runImpl() { |
| bool disableStaticOffsets = false; |
| getArg("disable_static_offsets", disableStaticOffsets); |
| MemoryManager mm("GCSafePointsMarker"); |
| GCSafePointsInfo info(mm, *irManager, GCSafePointsInfo::MODE_1_FIX_BASES); |
| |
| if (Log::isEnabled()) { |
| info.dump(getTagName()); |
| } |
| |
| if(!info.hasPairs()) { |
| return; |
| } |
| AutoTimer tm(saveResultsTimer); |
| const Nodes& nodes = irManager->getFlowGraph()->getNodes(); |
| StlVector<Opnd*> basesAndMptrs(mm); |
| StlVector<I_32> offsets(mm); |
| for (Nodes::const_iterator it = nodes.begin(), end = nodes.end(); it!=end; ++it) { |
| Node *node = *it; |
| if (!node->isBlockNode()) { |
| continue; |
| } |
| for (Inst * inst=(Inst*)node->getFirstInst(); inst!=NULL; inst=inst->getNextInst()) { |
| if (IRManager::isGCSafePoint(inst)) { |
| const GCSafePointPairs& pairs = info.getGCSafePointPairs(inst); |
| if (pairs.empty()) { |
| continue; |
| } |
| sideEffect = SideEffect_InvalidatesLivenessInfo; |
| basesAndMptrs.clear(); |
| offsets.clear(); |
| if (!disableStaticOffsets) { |
| //bases to adjust liveness info. No bases from pairs with valid static offsets get into this set |
| for (GCSafePointPairs::const_iterator it = pairs.begin(), end = pairs.end(); it!=end; ++it) { |
| const MPtrPair& p = *it; |
| if (p.getOffset() == MPTR_OFFSET_UNKNOWN) { // adjust base live range |
| Opnd* base = p.getBase(); |
| if (std::find(basesAndMptrs.begin(), basesAndMptrs.end(), base) == basesAndMptrs.end()) { |
| basesAndMptrs.push_back(p.getBase()); |
| offsets.push_back(0); |
| } |
| } |
| |
| } |
| } else { //ignore static offsets info, adjust live range for all bases |
| std::transform(pairs.begin(), pairs.end(), basesAndMptrs.begin(), std::mem_fun_ref(&MPtrPair::getBase)); |
| std::sort(basesAndMptrs.begin(), basesAndMptrs.end()); |
| StlVector<Opnd*>::iterator newEnd = std::unique(basesAndMptrs.begin(), basesAndMptrs.end()); |
| basesAndMptrs.resize(newEnd - basesAndMptrs.begin()); |
| std::fill(offsets.begin(), offsets.end(), 0); |
| } |
| if (!basesAndMptrs.empty()) { |
| GCInfoPseudoInst* gcInst = irManager->newGCInfoPseudoInst(basesAndMptrs); |
| gcInst->desc = getTagName(); |
| gcInst->offsets.resize(offsets.size()); |
| std::copy(offsets.begin(), offsets.end(), gcInst->offsets.begin()); |
| gcInst->insertAfter(inst); |
| } |
| } //if inst is gc safe point |
| } //for insts |
| } |
| } |
| |
| }} //namespace |
| |