Commit 94dfccf0 by Alan Mishchenko

Updating new mapper.

parent fb65bf9e
...@@ -47,6 +47,7 @@ typedef struct Mig_Obj_t_ Mig_Obj_t; ...@@ -47,6 +47,7 @@ typedef struct Mig_Obj_t_ Mig_Obj_t;
struct Mig_Obj_t_ struct Mig_Obj_t_
{ {
Mig_Fan_t pFans[4]; Mig_Fan_t pFans[4];
int Data[8];
}; };
typedef struct Mig_Man_t_ Mig_Man_t; typedef struct Mig_Man_t_ Mig_Man_t;
...@@ -67,6 +68,7 @@ struct Mig_Man_t_ ...@@ -67,6 +68,7 @@ struct Mig_Man_t_
Vec_Int_t vCopies; // copies Vec_Int_t vCopies; // copies
Vec_Int_t vLevels; // levels Vec_Int_t vLevels; // levels
Vec_Int_t vRefs; // ref counters Vec_Int_t vRefs; // ref counters
Vec_Int_t vRefs2; // ref counters
Vec_Int_t vSibls; // choice nodes Vec_Int_t vSibls; // choice nodes
}; };
...@@ -175,7 +177,9 @@ static inline void Mig_ObjSetFaninLit( Mig_Obj_t * p, int i, int l ) { ...@@ -175,7 +177,9 @@ static inline void Mig_ObjSetFaninLit( Mig_Obj_t * p, int i, int l ) {
static inline int Mig_ObjSiblId( Mig_Obj_t * p ) { return Vec_IntSize(&Mig_ObjMan(p)->vSibls) == 0 ? 0: Vec_IntEntry(&Mig_ObjMan(p)->vSibls, Mig_ObjId(p)); } static inline int Mig_ObjSiblId( Mig_Obj_t * p ) { return Vec_IntSize(&Mig_ObjMan(p)->vSibls) == 0 ? 0: Vec_IntEntry(&Mig_ObjMan(p)->vSibls, Mig_ObjId(p)); }
static inline Mig_Obj_t * Mig_ObjSibl( Mig_Obj_t * p ) { return Mig_ObjSiblId(p) == 0 ? NULL: Mig_ObjObj(p, Mig_ObjSiblId(p)); } static inline Mig_Obj_t * Mig_ObjSibl( Mig_Obj_t * p ) { return Mig_ObjSiblId(p) == 0 ? NULL: Mig_ObjObj(p, Mig_ObjSiblId(p)); }
static inline int Mig_ObjRefNum( Mig_Obj_t * p ) { return Vec_IntSize(&Mig_ObjMan(p)->vRefs) == 0 ? -1: Vec_IntEntry(&Mig_ObjMan(p)->vRefs, Mig_ObjId(p)); } static inline int Mig_ObjRefNum( Mig_Obj_t * p ) { return Vec_IntEntry(&Mig_ObjMan(p)->vRefs, Mig_ObjId(p)); }
static inline int Mig_ObjRef2Num( Mig_Obj_t * p ) { return Vec_IntEntry(&Mig_ObjMan(p)->vRefs2, Mig_ObjId(p)); }
static inline int Mig_ObjRef2Dec( Mig_Obj_t * p ) { return Vec_IntAddToEntry(&Mig_ObjMan(p)->vRefs2, Mig_ObjId(p), -1); }
static inline void Mig_ManCleanCopy( Mig_Man_t * p ) { if ( p->vCopies.pArray == NULL ) Vec_IntFill( &p->vCopies, Mig_ManObjNum(p), -1 ); } static inline void Mig_ManCleanCopy( Mig_Man_t * p ) { if ( p->vCopies.pArray == NULL ) Vec_IntFill( &p->vCopies, Mig_ManObjNum(p), -1 ); }
static inline int Mig_ObjCopy( Mig_Obj_t * p ) { return Vec_IntSize(&Mig_ObjMan(p)->vCopies) == 0 ? -1: Vec_IntEntry(&Mig_ObjMan(p)->vCopies, Mig_ObjId(p)); } static inline int Mig_ObjCopy( Mig_Obj_t * p ) { return Vec_IntSize(&Mig_ObjMan(p)->vCopies) == 0 ? -1: Vec_IntEntry(&Mig_ObjMan(p)->vCopies, Mig_ObjId(p)); }
...@@ -249,15 +253,15 @@ static inline Mig_Obj_t * Mig_ManAppendObj( Mig_Man_t * p ) ...@@ -249,15 +253,15 @@ static inline Mig_Obj_t * Mig_ManAppendObj( Mig_Man_t * p )
assert( p->nObjs < MIG_NONE ); assert( p->nObjs < MIG_NONE );
if ( p->nObjs >= (Vec_PtrSize(&p->vPages) << MIG_BASE) ) if ( p->nObjs >= (Vec_PtrSize(&p->vPages) << MIG_BASE) )
{ {
Mig_Obj_t * pPage; int i; Mig_Obj_t * pPage;// int i;
assert( p->nObjs == (Vec_PtrSize(&p->vPages) << MIG_BASE) ); assert( p->nObjs == (Vec_PtrSize(&p->vPages) << MIG_BASE) );
pPage = ABC_FALLOC( Mig_Obj_t, MIG_MASK + 3 ); // 1 for mask, 1 for prefix, 1 for sentinel pPage = ABC_FALLOC( Mig_Obj_t, MIG_MASK + 3 ); // 1 for mask, 1 for prefix, 1 for sentinel
*((void **)pPage) = p; *((void **)pPage) = p;
*((void ***)(pPage + 1) - 1) = Vec_PtrArray(&p->vPages); // *((void ***)(pPage + 1) - 1) = Vec_PtrArray(&p->vPages);
Vec_PtrPush( &p->vPages, pPage + 1 ); Vec_PtrPush( &p->vPages, pPage + 1 );
if ( *((void ***)(pPage + 1) - 1) != Vec_PtrArray(&p->vPages) ) // if ( *((void ***)(pPage + 1) - 1) != Vec_PtrArray(&p->vPages) )
Vec_PtrForEachEntry( Mig_Obj_t *, &p->vPages, pPage, i ) // Vec_PtrForEachEntry( Mig_Obj_t *, &p->vPages, pPage, i )
*((void ***)pPage - 1) = Vec_PtrArray(&p->vPages); // *((void ***)pPage - 1) = Vec_PtrArray(&p->vPages);
} }
pObj = Mig_ManObj( p, p->nObjs++ ); pObj = Mig_ManObj( p, p->nObjs++ );
assert( Mig_ObjIsNone(pObj) ); assert( Mig_ObjIsNone(pObj) );
...@@ -339,7 +343,7 @@ static inline int Mig_ManAppendMaj( Mig_Man_t * p, int iLit0, int iLit1, int iLi ...@@ -339,7 +343,7 @@ static inline int Mig_ManAppendMaj( Mig_Man_t * p, int iLit0, int iLit1, int iLi
static inline Mig_Man_t * Mig_ManStart() static inline Mig_Man_t * Mig_ManStart()
{ {
Mig_Man_t * p; Mig_Man_t * p;
assert( sizeof(Mig_Obj_t) == 16 ); // assert( sizeof(Mig_Obj_t) == 16 );
assert( (1 << MIG_BASE) == MIG_MASK + 1 ); assert( (1 << MIG_BASE) == MIG_MASK + 1 );
p = ABC_CALLOC( Mig_Man_t, 1 ); p = ABC_CALLOC( Mig_Man_t, 1 );
Vec_IntGrow( &p->vCis, 1024 ); Vec_IntGrow( &p->vCis, 1024 );
...@@ -358,6 +362,7 @@ static inline void Mig_ManStop( Mig_Man_t * p ) ...@@ -358,6 +362,7 @@ static inline void Mig_ManStop( Mig_Man_t * p )
ABC_FREE( p->vCopies.pArray ); ABC_FREE( p->vCopies.pArray );
ABC_FREE( p->vLevels.pArray ); ABC_FREE( p->vLevels.pArray );
ABC_FREE( p->vRefs.pArray ); ABC_FREE( p->vRefs.pArray );
ABC_FREE( p->vRefs2.pArray );
ABC_FREE( p->vSibls.pArray ); ABC_FREE( p->vSibls.pArray );
// pages // pages
Vec_PtrForEachEntry( Mig_Obj_t *, &p->vPages, p->pPage, p->iPage ) Vec_PtrForEachEntry( Mig_Obj_t *, &p->vPages, p->pPage, p->iPage )
...@@ -389,10 +394,9 @@ void Mig_ManSetRefs( Mig_Man_t * p, int fSkipCos ) ...@@ -389,10 +394,9 @@ void Mig_ManSetRefs( Mig_Man_t * p, int fSkipCos )
{ {
Mig_Obj_t * pObj; Mig_Obj_t * pObj;
int i, iFanin; int i, iFanin;
// abctime clk = Abc_Clock();
// increment references // increment references
Vec_IntFill( &p->vRefs, Mig_ManObjNum(p), 0 ); Vec_IntFill( &p->vRefs, Mig_ManObjNum(p), 0 );
Mig_ManForEachCand( p, pObj ) Mig_ManForEachNode( p, pObj )
Mig_ObjForEachFaninId( pObj, iFanin, i ) Mig_ObjForEachFaninId( pObj, iFanin, i )
Vec_IntAddToEntry( &p->vRefs, iFanin, 1 ); Vec_IntAddToEntry( &p->vRefs, iFanin, 1 );
if ( !fSkipCos ) if ( !fSkipCos )
...@@ -404,7 +408,12 @@ void Mig_ManSetRefs( Mig_Man_t * p, int fSkipCos ) ...@@ -404,7 +408,12 @@ void Mig_ManSetRefs( Mig_Man_t * p, int fSkipCos )
Mig_ManForEachNode( p, pObj ) Mig_ManForEachNode( p, pObj )
assert( Vec_IntEntry(&p->vRefs, Mig_ObjId(pObj)) > 0 ); assert( Vec_IntEntry(&p->vRefs, Mig_ObjId(pObj)) > 0 );
} }
// Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); }
void Mig_ManSetRefs2( Mig_Man_t * p )
{
Vec_IntGrow( &p->vRefs2, Mig_ManObjNum(p) );
memcpy( Vec_IntArray(&p->vRefs2), Vec_IntArray(&p->vRefs), sizeof(int) * Mig_ManObjNum(p) );
p->vRefs2.nSize = Mig_ManObjNum(p);
} }
/**Function************************************************************* /**Function*************************************************************
...@@ -558,7 +567,6 @@ struct Mpm_Uni_t_ ...@@ -558,7 +567,6 @@ struct Mpm_Uni_t_
typedef struct Mpm_Obj_t_ Mpm_Obj_t; // 32 bytes typedef struct Mpm_Obj_t_ Mpm_Obj_t; // 32 bytes
struct Mpm_Obj_t_ struct Mpm_Obj_t_
{ {
int nMigRefs; // original references
int nMapRefs; // exact mapping references int nMapRefs; // exact mapping references
int nEstRefs; // estimated mapping references int nEstRefs; // estimated mapping references
int mRequired; // required time int mRequired; // required time
...@@ -566,6 +574,7 @@ struct Mpm_Obj_t_ ...@@ -566,6 +574,7 @@ struct Mpm_Obj_t_
int mArea; // area int mArea; // area
int mEdge; // edge int mEdge; // edge
int hCutList; // cut list int hCutList; // cut list
int hCutBest; // cut best
}; };
typedef struct Mpm_LibLut_t_ Mpm_LibLut_t; typedef struct Mpm_LibLut_t_ Mpm_LibLut_t;
...@@ -605,6 +614,7 @@ struct Mpm_Man_t_ ...@@ -605,6 +614,7 @@ struct Mpm_Man_t_
Vec_Str_t vObjShared; // object presence Vec_Str_t vObjShared; // object presence
// cut comparison // cut comparison
int (* pCutCmp) (Mpm_Inf_t *, Mpm_Inf_t *); int (* pCutCmp) (Mpm_Inf_t *, Mpm_Inf_t *);
int fMainRun; // after preprocessing is finished
// fanin cuts/signatures // fanin cuts/signatures
int nCuts[3]; // fanin cut counts int nCuts[3]; // fanin cut counts
Mpm_Cut_t * pCuts[3][MPM_CUT_MAX+1]; // fanin cuts Mpm_Cut_t * pCuts[3][MPM_CUT_MAX+1]; // fanin cuts
...@@ -679,11 +689,11 @@ static inline Mpm_Cut_t * Mpm_CutFetch( Mpm_Man_t * p, int h ) ...@@ -679,11 +689,11 @@ static inline Mpm_Cut_t * Mpm_CutFetch( Mpm_Man_t * p, int h )
} }
static inline Mpm_Cut_t * Mpm_ObjCutBestObj( Mpm_Man_t * p, Mpm_Obj_t * pObj ) static inline Mpm_Cut_t * Mpm_ObjCutBestObj( Mpm_Man_t * p, Mpm_Obj_t * pObj )
{ {
return Mpm_CutFetch( p, pObj->hCutList ); return Mpm_CutFetch( p, pObj->hCutBest );
} }
static inline Mpm_Cut_t * Mpm_ObjCutBest( Mpm_Man_t * p, Mig_Obj_t * pObj ) static inline Mpm_Cut_t * Mpm_ObjCutBest( Mpm_Man_t * p, Mig_Obj_t * pObj )
{ {
return Mpm_CutFetch( p, Mpm_ManObj(p, pObj)->hCutList ); return Mpm_CutFetch( p, Mpm_ManObj(p, pObj)->hCutBest );
} }
static inline int Mpm_CutCreateZero( Mpm_Man_t * p, Mig_Obj_t * pObj ) static inline int Mpm_CutCreateZero( Mpm_Man_t * p, Mig_Obj_t * pObj )
{ {
...@@ -762,6 +772,175 @@ static inline void Mpm_CutPrintAll( Mpm_Man_t * p ) ...@@ -762,6 +772,175 @@ static inline void Mpm_CutPrintAll( Mpm_Man_t * p )
/**Function************************************************************* /**Function*************************************************************
Synopsis [Recursively derives the local AIG for the cut.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static inline unsigned Mpm_CutDataInt( Mpm_Cut_t * pCut ) { return pCut->hNext; }
static inline void Mpm_CutSetDataInt( Mpm_Cut_t * pCut, unsigned Data ) { pCut->hNext = Data; }
int Mpm_ManNodeIfToGia_rec( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj, Vec_Ptr_t * vVisited, int fHash )
{
Mig_Obj_t * pTemp;
Mpm_Cut_t * pCut;
int iFunc, iFunc0, iFunc1;
// get the best cut
pCut = Mpm_ObjCutBest( pMan, pObj );
// if the cut is visited, return the result
if ( Mpm_CutDataInt(pCut) )
return Mpm_CutDataInt(pCut);
// mark the node as visited
Vec_PtrPush( vVisited, pCut );
// insert the worst case
Mpm_CutSetDataInt( pCut, ~0 );
// skip in case of primary input
if ( Mig_ObjIsCi(pObj) )
return Mpm_CutDataInt(pCut);
// compute the functions of the children
for ( pTemp = pObj; pTemp; pTemp = Mig_ObjSibl(pTemp) )
{
iFunc0 = Mpm_ManNodeIfToGia_rec( pNew, pMan, Mig_ObjFanin0(pTemp), vVisited, fHash );
if ( iFunc0 == ~0 )
continue;
iFunc1 = Mpm_ManNodeIfToGia_rec( pNew, pMan, Mig_ObjFanin1(pTemp), vVisited, fHash );
if ( iFunc1 == ~0 )
continue;
// both branches are solved
if ( fHash )
iFunc = Gia_ManHashAnd( pNew, Abc_LitNotCond(iFunc0, Mig_ObjFaninC0(pTemp)), Abc_LitNotCond(iFunc1, Mig_ObjFaninC1(pTemp)) );
else
iFunc = Gia_ManAppendAnd( pNew, Abc_LitNotCond(iFunc0, Mig_ObjFaninC0(pTemp)), Abc_LitNotCond(iFunc1, Mig_ObjFaninC1(pTemp)) );
if ( Mig_ObjPhase(pTemp) != Mig_ObjPhase(pObj) )
iFunc = Abc_LitNot(iFunc);
Mpm_CutSetDataInt( pCut, iFunc );
break;
}
return Mpm_CutDataInt(pCut);
}
int Mpm_ManNodeIfToGia( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj, Vec_Int_t * vLeaves, int fHash )
{
Mpm_Cut_t * pCut;
Mig_Obj_t * pFanin;
int i, iRes;
// get the best cut
pCut = Mpm_ObjCutBest( pMan, pObj );
assert( pCut->nLeaves > 1 );
// set the leaf variables
Mpm_CutForEachLeaf( pMan->pMig, pCut, pFanin, i )
Mpm_CutSetDataInt( Mpm_ObjCutBest(pMan, pFanin), Vec_IntEntry(vLeaves, i) );
// recursively compute the function while collecting visited cuts
Vec_PtrClear( pMan->vTemp );
iRes = Mpm_ManNodeIfToGia_rec( pNew, pMan, pObj, pMan->vTemp, fHash );
if ( iRes == ~0 )
{
Abc_Print( -1, "Mpm_ManNodeIfToGia(): Computing local AIG has failed.\n" );
return ~0;
}
// clean the cuts
Mpm_CutForEachLeaf( pMan->pMig, pCut, pFanin, i )
Mpm_CutSetDataInt( Mpm_ObjCutBest(pMan, pFanin), 0 );
Vec_PtrForEachEntry( Mpm_Cut_t *, pMan->vTemp, pCut, i )
Mpm_CutSetDataInt( pCut, 0 );
return iRes;
}
Gia_Man_t * Mpm_ManFromIfLogic( Mpm_Man_t * pMan )
{
Gia_Man_t * pNew;
Mpm_Cut_t * pCutBest;
Mig_Obj_t * pObj, * pFanin;
Vec_Int_t * vMapping, * vMapping2, * vPacking = NULL;
Vec_Int_t * vLeaves, * vLeaves2, * vCover;
int i, k, Entry, iLitNew;
// assert( !pMan->pPars->fDeriveLuts || pMan->pPars->fTruth );
// start mapping and packing
vMapping = Vec_IntStart( Mig_ManObjNum(pMan->pMig) );
vMapping2 = Vec_IntStart( 1 );
if ( 0 ) // pMan->pPars->fDeriveLuts && pMan->pPars->pLutStruct )
{
vPacking = Vec_IntAlloc( 1000 );
Vec_IntPush( vPacking, 0 );
}
// create new manager
pNew = Gia_ManStart( Mig_ManObjNum(pMan->pMig) );
// iterate through nodes used in the mapping
vCover = Vec_IntAlloc( 1 << 16 );
vLeaves = Vec_IntAlloc( 16 );
vLeaves2 = Vec_IntAlloc( 16 );
Mig_ManCleanCopy( pMan->pMig );
Mig_ManForEachObj( pMan->pMig, pObj )
{
if ( Mpm_ManObj(pMan, pObj)->nMapRefs == 0 && !Mig_ObjIsTerm(pObj) )
continue;
if ( Mig_ObjIsNode(pObj) )
{
// collect leaves of the best cut
Vec_IntClear( vLeaves );
pCutBest = Mpm_ObjCutBest( pMan, pObj );
Mpm_CutForEachLeaf( pMan->pMig, pCutBest, pFanin, k )
Vec_IntPush( vLeaves, Mig_ObjCopy(pFanin) );
// perform one of the two types of mapping: with and without structures
iLitNew = Mpm_ManNodeIfToGia( pNew, pMan, pObj, vLeaves, 0 );
// write mapping
Vec_IntSetEntry( vMapping, Abc_Lit2Var(iLitNew), Vec_IntSize(vMapping2) );
Vec_IntPush( vMapping2, Vec_IntSize(vLeaves) );
Vec_IntForEachEntry( vLeaves, Entry, k )
assert( Abc_Lit2Var(Entry) < Abc_Lit2Var(iLitNew) );
Vec_IntForEachEntry( vLeaves, Entry, k )
Vec_IntPush( vMapping2, Abc_Lit2Var(Entry) );
Vec_IntPush( vMapping2, Abc_Lit2Var(iLitNew) );
}
else if ( Mig_ObjIsCi(pObj) )
iLitNew = Gia_ManAppendCi(pNew);
else if ( Mig_ObjIsCo(pObj) )
iLitNew = Gia_ManAppendCo( pNew, Abc_LitNotCond(Mig_ObjCopy(Mig_ObjFanin0(pObj)), Mig_ObjFaninC0(pObj)) );
else if ( Mig_ObjIsConst0(pObj) )
{
iLitNew = 0;
// create const LUT
Vec_IntWriteEntry( vMapping, 0, Vec_IntSize(vMapping2) );
Vec_IntPush( vMapping2, 0 );
Vec_IntPush( vMapping2, 0 );
}
else assert( 0 );
Mig_ObjSetCopy( pObj, iLitNew );
}
Vec_IntFree( vCover );
Vec_IntFree( vLeaves );
Vec_IntFree( vLeaves2 );
// printf( "Mapping array size: IfMan = %d. Gia = %d. Increase = %.2f\n",
// Mpm_ManObjNum(pMan), Gia_ManObjNum(pNew), 1.0 * Gia_ManObjNum(pNew) / Mpm_ManObjNum(pMan) );
// finish mapping
if ( Vec_IntSize(vMapping) > Gia_ManObjNum(pNew) )
Vec_IntShrink( vMapping, Gia_ManObjNum(pNew) );
else
Vec_IntFillExtra( vMapping, Gia_ManObjNum(pNew), 0 );
assert( Vec_IntSize(vMapping) == Gia_ManObjNum(pNew) );
Vec_IntForEachEntry( vMapping, Entry, i )
if ( Entry > 0 )
Vec_IntAddToEntry( vMapping, i, Gia_ManObjNum(pNew) );
Vec_IntAppend( vMapping, vMapping2 );
Vec_IntFree( vMapping2 );
// attach mapping and packing
assert( pNew->vMapping == NULL );
assert( pNew->vPacking == NULL );
pNew->vMapping = vMapping;
pNew->vPacking = vPacking;
// verify that COs have mapping
{
Gia_Obj_t * pObj;
Gia_ManForEachCo( pNew, pObj, i )
assert( !Gia_ObjIsAnd(Gia_ObjFanin0(pObj)) || Gia_ObjIsLut(pNew, Gia_ObjFaninId0p(pNew, pObj)) );
}
return pNew;
}
/**Function*************************************************************
Synopsis [Cut merging.] Synopsis [Cut merging.]
Description [] Description []
...@@ -885,7 +1064,7 @@ static inline void Mpm_CutSetupInfo( Mpm_Man_t * p, Mpm_Cut_t * pCut, int ArrTim ...@@ -885,7 +1064,7 @@ static inline void Mpm_CutSetupInfo( Mpm_Man_t * p, Mpm_Cut_t * pCut, int ArrTim
pInfo->mEdge = MPM_UNIT_EDGE * pCut->nLeaves; pInfo->mEdge = MPM_UNIT_EDGE * pCut->nLeaves;
Mpm_CutForEachLeafMap( p, pCut, pLeaf, i ) Mpm_CutForEachLeafMap( p, pCut, pLeaf, i )
{ {
if ( pLeaf->nMapRefs == 0 ) if ( p->fMainRun && pLeaf->nMapRefs == 0 ) // not used in the mapping
{ {
pInfo->mArea += pLeaf->mArea; pInfo->mArea += pLeaf->mArea;
pInfo->mEdge += pLeaf->mEdge; pInfo->mEdge += pLeaf->mEdge;
...@@ -895,11 +1074,11 @@ static inline void Mpm_CutSetupInfo( Mpm_Man_t * p, Mpm_Cut_t * pCut, int ArrTim ...@@ -895,11 +1074,11 @@ static inline void Mpm_CutSetupInfo( Mpm_Man_t * p, Mpm_Cut_t * pCut, int ArrTim
assert( pLeaf->nEstRefs > 0 ); assert( pLeaf->nEstRefs > 0 );
pInfo->mArea += MPM_UNIT_REFS * pLeaf->mArea / pLeaf->nEstRefs; pInfo->mArea += MPM_UNIT_REFS * pLeaf->mArea / pLeaf->nEstRefs;
pInfo->mEdge += MPM_UNIT_REFS * pLeaf->mEdge / pLeaf->nEstRefs; pInfo->mEdge += MPM_UNIT_REFS * pLeaf->mEdge / pLeaf->nEstRefs;
pInfo->mAveRefs += MPM_UNIT_EDGE * pLeaf->nMapRefs; // pInfo->mAveRefs += MPM_UNIT_EDGE * pLeaf->nMapRefs;
} }
pInfo->uSign |= ((word)1 << (Abc_Lit2Var(pCut->pLeaves[i]) & 0x3F)); pInfo->uSign |= ((word)1 << (Abc_Lit2Var(pCut->pLeaves[i]) & 0x3F));
} }
pInfo->mAveRefs /= pCut->nLeaves; // pInfo->mAveRefs /= pCut->nLeaves;
} }
/**Function************************************************************* /**Function*************************************************************
...@@ -1095,12 +1274,6 @@ void Mpm_ObjTranslateCutsFromStore( Mpm_Man_t * p, Mig_Obj_t * pObj, int fAddUni ...@@ -1095,12 +1274,6 @@ void Mpm_ObjTranslateCutsFromStore( Mpm_Man_t * p, Mig_Obj_t * pObj, int fAddUni
Mpm_Obj_t * pMapObj = Mpm_ManObj(p, pObj); Mpm_Obj_t * pMapObj = Mpm_ManObj(p, pObj);
int i, *pList = &pMapObj->hCutList; int i, *pList = &pMapObj->hCutList;
assert( p->nCutStore > 0 && p->nCutStore <= p->nNumCuts ); assert( p->nCutStore > 0 && p->nCutStore <= p->nNumCuts );
// save statistics
pUnit = p->pCutStore[0];
pMapObj->mArea = pUnit->Inf.mArea;
pMapObj->mEdge = pUnit->Inf.mEdge;
pMapObj->mTime = pUnit->Inf.mTime;
Mpm_CutRef( p, pUnit->pLeaves, pUnit->nLeaves );
// translate cuts // translate cuts
*pList = 0; *pList = 0;
for ( i = 0; i < p->nCutStore; i++ ) for ( i = 0; i < p->nCutStore; i++ )
...@@ -1150,6 +1323,9 @@ static inline void Mpm_ManFinalizeRound( Mpm_Man_t * p ) ...@@ -1150,6 +1323,9 @@ static inline void Mpm_ManFinalizeRound( Mpm_Man_t * p )
p->GloEdge = 0; p->GloEdge = 0;
p->GloRequired = Mpm_ManFindArrivalMax(p); p->GloRequired = Mpm_ManFindArrivalMax(p);
Mpm_ManResetRequired( p ); Mpm_ManResetRequired( p );
Mig_ManForEachObj( p->pMig, pObj )
// assert( Mpm_ManObj(p, pObj)->nMapRefs == 0 );
Mpm_ManObj(p, pObj)->nMapRefs = 0;
Mig_ManForEachObjReverse( p->pMig, pObj ) Mig_ManForEachObjReverse( p->pMig, pObj )
{ {
if ( Mig_ObjIsCo(pObj) ) if ( Mig_ObjIsCo(pObj) )
...@@ -1166,7 +1342,10 @@ static inline void Mpm_ManFinalizeRound( Mpm_Man_t * p ) ...@@ -1166,7 +1342,10 @@ static inline void Mpm_ManFinalizeRound( Mpm_Man_t * p )
pDelays = p->pLibLut->pLutDelays[pCut->nLeaves]; pDelays = p->pLibLut->pLutDelays[pCut->nLeaves];
Required = Mpm_ManObj(p,pObj)->mRequired; Required = Mpm_ManObj(p,pObj)->mRequired;
Mpm_CutForEachLeafMap( p, pCut, pFanin, i ) Mpm_CutForEachLeafMap( p, pCut, pFanin, i )
{
pFanin->mRequired = Abc_MinInt( pFanin->mRequired, Required - pDelays[i] ); pFanin->mRequired = Abc_MinInt( pFanin->mRequired, Required - pDelays[i] );
pFanin->nMapRefs++;
}
p->GloArea += p->pLibLut->pLutAreas[pCut->nLeaves]; p->GloArea += p->pLibLut->pLutAreas[pCut->nLeaves];
p->GloEdge += pCut->nLeaves; p->GloEdge += pCut->nLeaves;
} }
...@@ -1175,7 +1354,8 @@ static inline void Mpm_ManFinalizeRound( Mpm_Man_t * p ) ...@@ -1175,7 +1354,8 @@ static inline void Mpm_ManFinalizeRound( Mpm_Man_t * p )
{ {
} }
// pObj->EstRefs = (float)((2.0 * pObj->EstRefs + pObj->nRefs) / 3.0); // pObj->EstRefs = (float)((2.0 * pObj->EstRefs + pObj->nRefs) / 3.0);
Mpm_ManObj(p, pObj)->nEstRefs = (2 * Mpm_ManObj(p, pObj)->nEstRefs + MPM_UNIT_REFS * Mpm_ManObj(p, pObj)->nMapRefs) / 3; if ( p->fMainRun )
Mpm_ManObj(p, pObj)->nEstRefs = (2 * Mpm_ManObj(p, pObj)->nEstRefs + MPM_UNIT_REFS * Mpm_ManObj(p, pObj)->nMapRefs) / 3;
} }
p->GloArea /= MPM_UNIT_AREA; p->GloArea /= MPM_UNIT_AREA;
} }
...@@ -1314,20 +1494,23 @@ void Mpm_ObjRecycleCuts( Mpm_Man_t * p, Mig_Obj_t * pObj ) ...@@ -1314,20 +1494,23 @@ void Mpm_ObjRecycleCuts( Mpm_Man_t * p, Mig_Obj_t * pObj )
int hCut, hNext, hList = Mpm_ObjCutList(p, pObj); int hCut, hNext, hList = Mpm_ObjCutList(p, pObj);
// printf( "Recycling cuts of node %d.\n", Mig_ObjId(pObj) ); // printf( "Recycling cuts of node %d.\n", Mig_ObjId(pObj) );
Mpm_ObjForEachCutSafe( p, pObj, hCut, pCut, hNext ) Mpm_ObjForEachCutSafe( p, pObj, hCut, pCut, hNext )
if ( hCut == hList ) // if ( hCut == hList )
pCut->hNext = 0; // pCut->hNext = 0;
else // else
Mmr_StepRecycle( p->pManCuts, hCut ); Mmr_StepRecycle( p->pManCuts, hCut );
Mpm_ManObj(p, pObj)->hCutList = 0;
} }
void Mpm_ObjDerefFaninCuts( Mpm_Man_t * p, Mig_Obj_t * pObj ) void Mpm_ObjDerefFaninCuts( Mpm_Man_t * p, Mig_Obj_t * pObj )
{ {
Mig_Obj_t * pFanin; Mig_Obj_t * pFanin;
int i; int i;
Mig_ObjForEachFanin( pObj, pFanin, i ) Mig_ObjForEachFanin( pObj, pFanin, i )
if ( Mig_ObjIsNode(pFanin) && --Mpm_ManObj(p, pFanin)->nMigRefs == 0 ) if ( Mig_ObjIsNode(pFanin) && Mig_ObjRef2Dec(pFanin) == 0 )
Mpm_ObjRecycleCuts( p, pFanin ); Mpm_ObjRecycleCuts( p, pFanin );
if ( Mig_ObjSiblId(pObj) ) if ( Mig_ObjSiblId(pObj) )
Mpm_ObjRecycleCuts( p, Mig_ObjSibl(pObj) ); Mpm_ObjRecycleCuts( p, Mig_ObjSibl(pObj) );
if ( Mig_ObjRef2Num(pObj) == 0 )
Mpm_ObjRecycleCuts( p, pObj );
} }
void Mpm_ObjCollectFaninsAndSigns( Mpm_Man_t * p, Mig_Obj_t * pObj, int i ) void Mpm_ObjCollectFaninsAndSigns( Mpm_Man_t * p, Mig_Obj_t * pObj, int i )
{ {
...@@ -1433,6 +1616,7 @@ static inline int Mpm_ManDeriveCutNew( Mpm_Man_t * p, Mpm_Cut_t ** pCuts, int Re ...@@ -1433,6 +1616,7 @@ static inline int Mpm_ManDeriveCutNew( Mpm_Man_t * p, Mpm_Cut_t ** pCuts, int Re
#ifdef MIG_RUNTIME #ifdef MIG_RUNTIME
abctime clk = clock(); abctime clk = clock();
#endif #endif
Mpm_ManObjPresClean( p ); Mpm_ManObjPresClean( p );
if ( !Mpm_ObjDeriveCut( p, pCuts, pCut ) ) if ( !Mpm_ObjDeriveCut( p, pCuts, pCut ) )
{ {
...@@ -1449,7 +1633,8 @@ clk = clock(); ...@@ -1449,7 +1633,8 @@ clk = clock();
#ifdef MIG_RUNTIME #ifdef MIG_RUNTIME
p->timeEval += clock() - clk; p->timeEval += clock() - clk;
#endif #endif
if ( ArrTime > Required )
if ( p->fMainRun && ArrTime > Required )
return 1; return 1;
#ifdef MIG_RUNTIME #ifdef MIG_RUNTIME
clk = Abc_Clock(); clk = Abc_Clock();
...@@ -1466,6 +1651,7 @@ int Mpm_ManDeriveCuts( Mpm_Man_t * p, Mig_Obj_t * pObj ) ...@@ -1466,6 +1651,7 @@ int Mpm_ManDeriveCuts( Mpm_Man_t * p, Mig_Obj_t * pObj )
// static int Flag = 0; // static int Flag = 0;
Mpm_Obj_t * pMapObj = Mpm_ManObj(p, pObj); Mpm_Obj_t * pMapObj = Mpm_ManObj(p, pObj);
Mpm_Cut_t * pCuts[3]; Mpm_Cut_t * pCuts[3];
Mpm_Uni_t * pUnit;
int c0, c1, c2; int c0, c1, c2;
#ifdef MIG_RUNTIME #ifdef MIG_RUNTIME
abctime clk; abctime clk;
...@@ -1473,15 +1659,15 @@ int Mpm_ManDeriveCuts( Mpm_Man_t * p, Mig_Obj_t * pObj ) ...@@ -1473,15 +1659,15 @@ int Mpm_ManDeriveCuts( Mpm_Man_t * p, Mig_Obj_t * pObj )
Mpm_ManPrepareCutStore( p ); Mpm_ManPrepareCutStore( p );
// check that the best cut is ok // check that the best cut is ok
pMapObj = Mpm_ManObj(p, pObj); pMapObj = Mpm_ManObj(p, pObj);
if ( pMapObj->hCutList > 0 ) // cut list is assigned assert( pMapObj->hCutList == 0 );
if ( pMapObj->hCutBest > 0 ) // cut list is assigned
{ {
Mpm_Cut_t * pCut = Mpm_ObjCutBest( p, pObj ); assert( pCut->hNext == 0 ); Mpm_Cut_t * pCut = Mpm_ObjCutBest( p, pObj ); assert( pCut->hNext == 0 );
pMapObj->mTime = Mpm_CutGetArrTime(p, pCut); pMapObj->mTime = Mpm_CutGetArrTime(p, pCut);
if ( pMapObj->mTime > pMapObj->mRequired ) if ( pMapObj->mTime > pMapObj->mRequired )
printf( "Arrival time (%d) exceeds required time (%d) at object %d.\n", pMapObj->mTime, pMapObj->mRequired, Mig_ObjId(pObj) ); printf( "Arrival time (%d) exceeds required time (%d) at object %d.\n", pMapObj->mTime, pMapObj->mRequired, Mig_ObjId(pObj) );
Mpm_ObjAddCutToStore( p, pCut, pMapObj->mTime ); if ( p->fMainRun )
Mpm_CutDeref( p, pCut->pLeaves, pCut->nLeaves ); Mpm_ObjAddCutToStore( p, pCut, pMapObj->mTime );
Mmr_StepRecycle( p->pManCuts, pMapObj->hCutList );
} }
// start storage with choice cuts // start storage with choice cuts
if ( p->pMig->vSibls.nSize && Mig_ObjSiblId(pObj) ) if ( p->pMig->vSibls.nSize && Mig_ObjSiblId(pObj) )
...@@ -1527,187 +1713,30 @@ finish: ...@@ -1527,187 +1713,30 @@ finish:
// if ( Flag == 0 && p->nCutStore == p->nNumCuts - 1 ) // if ( Flag == 0 && p->nCutStore == p->nNumCuts - 1 )
// Flag = 1, Mpm_CutPrintAll( p ); // Flag = 1, Mpm_CutPrintAll( p );
// printf( "%d ", p->nCutStore ); // printf( "%d ", p->nCutStore );
// save best cut
assert( p->nCutStore > 0 );
pUnit = p->pCutStore[0];
if ( pUnit->Inf.mTime <= pMapObj->mRequired )
{
Mpm_Cut_t * pCut;
if ( pMapObj->hCutBest )
Mmr_StepRecycle( p->pManCuts, pMapObj->hCutBest );
pMapObj->hCutBest = Mpm_CutCreate( p, pUnit->pLeaves, pUnit->nLeaves, pUnit->fUseless, &pCut );
pMapObj->mTime = pUnit->Inf.mTime;
pMapObj->mArea = pUnit->Inf.mArea;
pMapObj->mEdge = pUnit->Inf.mEdge;
}
else assert( !p->fMainRun );
// transform internal storage into regular cuts
Mpm_ObjTranslateCutsFromStore( p, pObj, Mig_ObjRefNum(pObj) > 0 ); Mpm_ObjTranslateCutsFromStore( p, pObj, Mig_ObjRefNum(pObj) > 0 );
// dereference fanin cuts and reference node // dereference fanin cuts and reference node
Mpm_ObjDerefFaninCuts( p, pObj ); Mpm_ObjDerefFaninCuts( p, pObj );
Mpm_ManObj(p, pObj)->nMigRefs = Mig_ObjRefNum(pObj);
if ( Mpm_ManObj(p, pObj)->nMigRefs == 0 )
Mpm_ObjRecycleCuts( p, pObj );
return 1; return 1;
} }
/**Function************************************************************* /**Function*************************************************************
Synopsis [Recursively derives the local AIG for the cut.]
Description []
SideEffects []
SeeAlso []
***********************************************************************/
static inline unsigned Mpm_CutDataInt( Mpm_Cut_t * pCut ) { return pCut->hNext; }
static inline void Mpm_CutSetDataInt( Mpm_Cut_t * pCut, unsigned Data ) { pCut->hNext = Data; }
int Mpm_ManNodeIfToGia_rec( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj, Vec_Ptr_t * vVisited, int fHash )
{
Mig_Obj_t * pTemp;
Mpm_Cut_t * pCut;
int iFunc, iFunc0, iFunc1;
// get the best cut
pCut = Mpm_ObjCutBest( pMan, pObj );
// if the cut is visited, return the result
if ( Mpm_CutDataInt(pCut) )
return Mpm_CutDataInt(pCut);
// mark the node as visited
Vec_PtrPush( vVisited, pCut );
// insert the worst case
Mpm_CutSetDataInt( pCut, ~0 );
// skip in case of primary input
if ( Mig_ObjIsCi(pObj) )
return Mpm_CutDataInt(pCut);
// compute the functions of the children
for ( pTemp = pObj; pTemp; pTemp = Mig_ObjSibl(pTemp) )
{
iFunc0 = Mpm_ManNodeIfToGia_rec( pNew, pMan, Mig_ObjFanin0(pTemp), vVisited, fHash );
if ( iFunc0 == ~0 )
continue;
iFunc1 = Mpm_ManNodeIfToGia_rec( pNew, pMan, Mig_ObjFanin1(pTemp), vVisited, fHash );
if ( iFunc1 == ~0 )
continue;
// both branches are solved
if ( fHash )
iFunc = Gia_ManHashAnd( pNew, Abc_LitNotCond(iFunc0, Mig_ObjFaninC0(pTemp)), Abc_LitNotCond(iFunc1, Mig_ObjFaninC1(pTemp)) );
else
iFunc = Gia_ManAppendAnd( pNew, Abc_LitNotCond(iFunc0, Mig_ObjFaninC0(pTemp)), Abc_LitNotCond(iFunc1, Mig_ObjFaninC1(pTemp)) );
if ( Mig_ObjPhase(pTemp) != Mig_ObjPhase(pObj) )
iFunc = Abc_LitNot(iFunc);
Mpm_CutSetDataInt( pCut, iFunc );
break;
}
return Mpm_CutDataInt(pCut);
}
int Mpm_ManNodeIfToGia( Gia_Man_t * pNew, Mpm_Man_t * pMan, Mig_Obj_t * pObj, Vec_Int_t * vLeaves, int fHash )
{
Mpm_Cut_t * pCut;
Mig_Obj_t * pFanin;
int i, iRes;
// get the best cut
pCut = Mpm_ObjCutBest( pMan, pObj );
assert( pCut->nLeaves > 1 );
// set the leaf variables
Mpm_CutForEachLeaf( pMan->pMig, pCut, pFanin, i )
Mpm_CutSetDataInt( Mpm_ObjCutBest(pMan, pFanin), Vec_IntEntry(vLeaves, i) );
// recursively compute the function while collecting visited cuts
Vec_PtrClear( pMan->vTemp );
iRes = Mpm_ManNodeIfToGia_rec( pNew, pMan, pObj, pMan->vTemp, fHash );
if ( iRes == ~0 )
{
Abc_Print( -1, "Mpm_ManNodeIfToGia(): Computing local AIG has failed.\n" );
return ~0;
}
// clean the cuts
Mpm_CutForEachLeaf( pMan->pMig, pCut, pFanin, i )
Mpm_CutSetDataInt( Mpm_ObjCutBest(pMan, pFanin), 0 );
Vec_PtrForEachEntry( Mpm_Cut_t *, pMan->vTemp, pCut, i )
Mpm_CutSetDataInt( pCut, 0 );
return iRes;
}
Gia_Man_t * Mpm_ManFromIfLogic( Mpm_Man_t * pMan )
{
Gia_Man_t * pNew;
Mpm_Cut_t * pCutBest;
Mig_Obj_t * pObj, * pFanin;
Vec_Int_t * vMapping, * vMapping2, * vPacking = NULL;
Vec_Int_t * vLeaves, * vLeaves2, * vCover;
int i, k, Entry, iLitNew;
// assert( !pMan->pPars->fDeriveLuts || pMan->pPars->fTruth );
// start mapping and packing
vMapping = Vec_IntStart( Mig_ManObjNum(pMan->pMig) );
vMapping2 = Vec_IntStart( 1 );
if ( 0 ) // pMan->pPars->fDeriveLuts && pMan->pPars->pLutStruct )
{
vPacking = Vec_IntAlloc( 1000 );
Vec_IntPush( vPacking, 0 );
}
// create new manager
pNew = Gia_ManStart( Mig_ManObjNum(pMan->pMig) );
// iterate through nodes used in the mapping
vCover = Vec_IntAlloc( 1 << 16 );
vLeaves = Vec_IntAlloc( 16 );
vLeaves2 = Vec_IntAlloc( 16 );
Mig_ManCleanCopy( pMan->pMig );
Mig_ManForEachObj( pMan->pMig, pObj )
{
if ( Mpm_ManObj(pMan, pObj)->nMapRefs == 0 && !Mig_ObjIsTerm(pObj) )
continue;
if ( Mig_ObjIsNode(pObj) )
{
// collect leaves of the best cut
Vec_IntClear( vLeaves );
pCutBest = Mpm_ObjCutBest( pMan, pObj );
Mpm_CutForEachLeaf( pMan->pMig, pCutBest, pFanin, k )
Vec_IntPush( vLeaves, Mig_ObjCopy(pFanin) );
// perform one of the two types of mapping: with and without structures
iLitNew = Mpm_ManNodeIfToGia( pNew, pMan, pObj, vLeaves, 0 );
// write mapping
Vec_IntSetEntry( vMapping, Abc_Lit2Var(iLitNew), Vec_IntSize(vMapping2) );
Vec_IntPush( vMapping2, Vec_IntSize(vLeaves) );
Vec_IntForEachEntry( vLeaves, Entry, k )
assert( Abc_Lit2Var(Entry) < Abc_Lit2Var(iLitNew) );
Vec_IntForEachEntry( vLeaves, Entry, k )
Vec_IntPush( vMapping2, Abc_Lit2Var(Entry) );
Vec_IntPush( vMapping2, Abc_Lit2Var(iLitNew) );
}
else if ( Mig_ObjIsCi(pObj) )
iLitNew = Gia_ManAppendCi(pNew);
else if ( Mig_ObjIsCo(pObj) )
iLitNew = Gia_ManAppendCo( pNew, Abc_LitNotCond(Mig_ObjCopy(Mig_ObjFanin0(pObj)), Mig_ObjFaninC0(pObj)) );
else if ( Mig_ObjIsConst0(pObj) )
{
iLitNew = 0;
// create const LUT
Vec_IntWriteEntry( vMapping, 0, Vec_IntSize(vMapping2) );
Vec_IntPush( vMapping2, 0 );
Vec_IntPush( vMapping2, 0 );
}
else assert( 0 );
Mig_ObjSetCopy( pObj, iLitNew );
}
Vec_IntFree( vCover );
Vec_IntFree( vLeaves );
Vec_IntFree( vLeaves2 );
// printf( "Mapping array size: IfMan = %d. Gia = %d. Increase = %.2f\n",
// Mpm_ManObjNum(pMan), Gia_ManObjNum(pNew), 1.0 * Gia_ManObjNum(pNew) / Mpm_ManObjNum(pMan) );
// finish mapping
if ( Vec_IntSize(vMapping) > Gia_ManObjNum(pNew) )
Vec_IntShrink( vMapping, Gia_ManObjNum(pNew) );
else
Vec_IntFillExtra( vMapping, Gia_ManObjNum(pNew), 0 );
assert( Vec_IntSize(vMapping) == Gia_ManObjNum(pNew) );
Vec_IntForEachEntry( vMapping, Entry, i )
if ( Entry > 0 )
Vec_IntAddToEntry( vMapping, i, Gia_ManObjNum(pNew) );
Vec_IntAppend( vMapping, vMapping2 );
Vec_IntFree( vMapping2 );
// attach mapping and packing
assert( pNew->vMapping == NULL );
assert( pNew->vPacking == NULL );
pNew->vMapping = vMapping;
pNew->vPacking = vPacking;
// verify that COs have mapping
{
Gia_Obj_t * pObj;
Gia_ManForEachCo( pNew, pObj, i )
assert( !Gia_ObjIsAnd(Gia_ObjFanin0(pObj)) || Gia_ObjIsLut(pNew, Gia_ObjFaninId0p(pNew, pObj)) );
}
return pNew;
}
/**Function*************************************************************
Synopsis [Cut computation experiment.] Synopsis [Cut computation experiment.]
Description [] Description []
...@@ -1722,6 +1751,7 @@ void Mpm_ManPerformRound( Mpm_Man_t * p ) ...@@ -1722,6 +1751,7 @@ void Mpm_ManPerformRound( Mpm_Man_t * p )
Mig_Obj_t * pObj; Mig_Obj_t * pObj;
abctime clk = Abc_Clock(); abctime clk = Abc_Clock();
p->nCutsMerged = 0; p->nCutsMerged = 0;
Mig_ManSetRefs2( p->pMig );
Mig_ManForEachNode( p->pMig, pObj ) Mig_ManForEachNode( p->pMig, pObj )
Mpm_ManDeriveCuts( p, pObj ); Mpm_ManDeriveCuts( p, pObj );
Mpm_ManFinalizeRound( p ); Mpm_ManFinalizeRound( p );
...@@ -1752,10 +1782,10 @@ Gia_Man_t * Mpm_ManPerformTest( Mig_Man_t * pMig ) ...@@ -1752,10 +1782,10 @@ Gia_Man_t * Mpm_ManPerformTest( Mig_Man_t * pMig )
p = Mpm_ManStart( pMig, pLib, 8 ); p = Mpm_ManStart( pMig, pLib, 8 );
Mpm_ManPrintStatsInit( p ); Mpm_ManPrintStatsInit( p );
Mpm_ManResetRequired( p ); Mpm_ManResetRequired( p );
// Mig_ManForEachCi( p->pMig, pObj, i )
// Mpm_ManObj(p, pObj)->nMapRefs = Mig_ObjRefNum(pObj);
Mig_ManForEachCi( p->pMig, pObj, i ) Mig_ManForEachCi( p->pMig, pObj, i )
Mpm_ManObj(p, pObj)->nMapRefs = Mig_ObjRefNum(pObj); Mpm_ManObj(p, pObj)->hCutList = Mpm_ManObj(p, pObj)->hCutBest = Mpm_CutCreateUnit( p, pObj );
Mig_ManForEachCi( p->pMig, pObj, i )
Mpm_ManObj(p, pObj)->hCutList = Mpm_CutCreateUnit( p, pObj );
Mig_ManForEachCand( p->pMig, pObj ) Mig_ManForEachCand( p->pMig, pObj )
Mpm_ManObj(p, pObj)->nEstRefs = MPM_UNIT_REFS * Mig_ObjRefNum(pObj); Mpm_ManObj(p, pObj)->nEstRefs = MPM_UNIT_REFS * Mig_ObjRefNum(pObj);
Mpm_ManPerform( p ); Mpm_ManPerform( p );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment