mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1098 lines
20 KiB
1098 lines
20 KiB
/***********************************************************************
|
|
* Microsoft (R) 32-Bit Incremental Linker
|
|
*
|
|
* Copyright (C) Microsoft Corp 1992-1996. All rights reserved.
|
|
*
|
|
* File: tce.cpp
|
|
*
|
|
* File Comments:
|
|
*
|
|
* Transitive Comdat Elimination (TCE).
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "link.h"
|
|
|
|
#define FPcodeSym(sym) ((sym).Type & IMAGE_SYM_TYPE_PCODE)
|
|
|
|
PCON pconHeadGraph;
|
|
PENT pentHeadImage;
|
|
|
|
STATIC LHEAP lheap; // local heap for TCE allocation
|
|
|
|
#define PvNew(cblk, cb) PvAllocLheap(&lheap, (cblk) * (cb))
|
|
|
|
char *strdup_TCE(const char *szIn)
|
|
{
|
|
char *szOut = (char *) PvNew(1, strlen(szIn) + 1);
|
|
|
|
strcpy(szOut, szIn);
|
|
return szOut;
|
|
}
|
|
|
|
|
|
void
|
|
Init_TCE(VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the TCE engine.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
// Initialize the local heap
|
|
|
|
InitLheap(&lheap);
|
|
}
|
|
|
|
|
|
void
|
|
Cleanup_TCE(VOID)
|
|
{
|
|
FreeLheap(&lheap);
|
|
}
|
|
|
|
|
|
void
|
|
InitNodPmod(
|
|
IN PMOD pmod)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create NOD array for a module
|
|
|
|
Arguments:
|
|
|
|
pmod - module node in image/driver map
|
|
|
|
Return Value:
|
|
|
|
pointer to a new TOD
|
|
|
|
--*/
|
|
|
|
{
|
|
pmod->rgnod = (PNOD) PvNew(pmod->ccon, sizeof(NOD));
|
|
}
|
|
|
|
|
|
__inline PNOD
|
|
PnodPcon(
|
|
PCON pcon)
|
|
{
|
|
PMOD pmod;
|
|
PCON rgcon;
|
|
PNOD pnod;
|
|
|
|
assert(pcon);
|
|
|
|
pmod = PmodPCON(pcon);
|
|
rgcon = RgconPMOD(pmod);
|
|
|
|
if ((pcon < rgcon) || (pcon > (rgcon + pmod->ccon))) {
|
|
// Individually allocated CONs are followed by their NODs
|
|
|
|
pnod = (PNOD) (pcon+1);
|
|
} else {
|
|
WORD icon;
|
|
|
|
icon = (WORD) (pcon - rgcon);
|
|
|
|
assert(pcon == RgconPMOD(pmod) + icon);
|
|
|
|
pnod = RgnodPMOD(pmod) + icon;
|
|
}
|
|
|
|
return(pnod);
|
|
}
|
|
|
|
|
|
void
|
|
InitNodPcon(
|
|
PCON pcon,
|
|
const char *sz,
|
|
BOOL fReferenced)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a NOD, which is a node in the TCE graph.
|
|
|
|
Arguments:
|
|
|
|
pcon - contribution node in image/driver map corresponding to the NOD
|
|
to be created
|
|
|
|
sz - comdat name
|
|
|
|
--*/
|
|
|
|
{
|
|
PNOD pnod;
|
|
|
|
pnod = PnodPcon(pcon);
|
|
|
|
if (PmodPCON(pcon) != pmodLinkerDefined) {
|
|
// UNDONE: Is support for >= 64K relocs needed here?
|
|
|
|
pnod->cedg = CRelocSrcPCON(pcon);
|
|
|
|
if (pnod->cedg != 0) {
|
|
pnod->rgedg = (PEDG) PvNew(pnod->cedg, sizeof(EDG));
|
|
}
|
|
}
|
|
|
|
if (sz) {
|
|
pnod->sz = strdup_TCE(sz);
|
|
}
|
|
|
|
if (fReferenced) {
|
|
pcon->flags |= TCE_Referenced;
|
|
}
|
|
|
|
if (pconHeadGraph) {
|
|
pnod->pconNext = pconHeadGraph;
|
|
}
|
|
|
|
pconHeadGraph = pcon;
|
|
}
|
|
|
|
|
|
PEDG
|
|
PedgNew_TCE(
|
|
DWORD isym,
|
|
PCON pcon,
|
|
PCON pconTarget)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a EDG, which is an edge in the TCE graph. If an edge is created
|
|
from an external reference, then pcon will be NULL and sz will refer to
|
|
the symbol name of the external. If the edge is not created from an
|
|
external then the target of the reloc is assumed to be a seciton and
|
|
sz will be NULL and pcon will be the pcon to resolve with.
|
|
|
|
Arguments:
|
|
|
|
sz - symbolic name of edge
|
|
|
|
pcon - CON to add edge to
|
|
|
|
pconTarget - contribution in image/driver map to resolve edge with
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PNOD pnod;
|
|
PEDG pedg;
|
|
|
|
pnod = PnodPcon(pcon);
|
|
|
|
while (pnod->iedg == pnod->cedg) {
|
|
if (pnod->cedg == 0) {
|
|
assert(pnod->rgedg == NULL);
|
|
|
|
// Allocate 8 EDGs
|
|
|
|
pnod->cedg = 8;
|
|
pnod->rgedg = (PEDG) PvNew(8, sizeof(EDG));
|
|
break;
|
|
}
|
|
|
|
if (pnod->pnodNext == NULL) {
|
|
// Allocate a new NOD and link to end of list
|
|
|
|
pnod->pnodNext = (PNOD) PvNew(1, sizeof(NOD));
|
|
}
|
|
|
|
pnod = pnod->pnodNext;
|
|
}
|
|
|
|
pedg = &pnod->rgedg[pnod->iedg];
|
|
pnod->iedg++;
|
|
|
|
if (pconTarget != NULL) {
|
|
pedg->pcon = pconTarget;
|
|
} else {
|
|
pedg->Sym.isym = isym;
|
|
}
|
|
|
|
pedg->NEPInfo.pconPcodeNEP = NULL;
|
|
|
|
return(pedg);
|
|
}
|
|
|
|
|
|
void
|
|
ProcessRelocForTCE(
|
|
PIMAGE pimage,
|
|
PCON pcon,
|
|
PIMAGE_SYMBOL rgsym,
|
|
PIMAGE_RELOCATION prel)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process a relocation for transitive comdat elimination. This involves
|
|
creating an edge in the TCE graph for each relocation.
|
|
|
|
Arguments:
|
|
|
|
pcon - contribution node in image/driver map
|
|
|
|
rgsym - COFF symbol table
|
|
|
|
prel - COFF relocation to process
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMOD pmod;
|
|
DWORD isym;
|
|
PIMAGE_SYMBOL psym;
|
|
PEDG pedg;
|
|
SHORT isec;
|
|
|
|
pmod = PmodPCON(pcon);
|
|
|
|
isym = prel->SymbolTableIndex;
|
|
psym = &rgsym[isym];
|
|
|
|
if (psym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL ||
|
|
psym->StorageClass == IMAGE_SYM_CLASS_FAR_EXTERNAL ||
|
|
psym->StorageClass == IMAGE_SYM_CLASS_WEAK_EXTERNAL) {
|
|
|
|
pedg = PedgNew_TCE(isym, pcon, NULL);
|
|
|
|
if ((fM68K && !fRelFromMac68KPcode(prel->Type)) ||
|
|
(fPowerMac && !FRelFromPpcPcode(prel->Type))) {
|
|
pedg->NEPInfo.fFromNative = TRUE;
|
|
}
|
|
} else if ((isec = psym->SectionNumber) > 0) {
|
|
PCON pconTarget = PconPMOD(pmod, isec);
|
|
|
|
// If reloc is to a static pcode sym from native code, remember the NEP
|
|
// comdat since it must be colored.
|
|
|
|
if (FPcodeSym(*psym) &&
|
|
((fM68K && !fRelFromMac68KPcode(prel->Type)) ||
|
|
(fPowerMac && !FRelFromPpcPcode(prel->Type)))) {
|
|
PIMAGE_SYMBOL psymPcodeNEP = PsymAlternateStaticPcodeSym(pimage, pcon, TRUE, psym, FALSE);
|
|
PCON pconPcodeNEP = PconPMOD(pmod, psymPcodeNEP->SectionNumber);
|
|
|
|
// UNDONE: Why not create an edge to pconPcodeNEP and allow
|
|
// UNDONE: whatever mechanism that maps from the NEP to the
|
|
// UNDONE: pcode to drag in the pcode. If there is no such
|
|
// UNDONE: mechanism, something should be done to create an
|
|
// UNDONE: edge from the NEP to the pcode. This would allow
|
|
// UNDONE: the EDG structure to be shrunk by a DWORD.
|
|
|
|
pedg = PedgNew_TCE(0, pcon, pconTarget);
|
|
|
|
StorepconPcodeNEP(pedg, pconPcodeNEP);
|
|
} else if (pcon != pconTarget) {
|
|
PedgNew_TCE(0, pcon, pconTarget);
|
|
}
|
|
} else {
|
|
if (psym->StorageClass != IMAGE_SYM_CLASS_SECTION) {
|
|
// Unrecognized fixup (might be erroneous). Ignore here, handle
|
|
// elsewhere.
|
|
|
|
return;
|
|
}
|
|
|
|
// Fixup to an undefined section symbol. This is un-COFF-like
|
|
// (normally section symbols map 1-1 to the section headers, so
|
|
// they always are defined)
|
|
// but it can occur with imports because of the merging of
|
|
// objects with the same name. For this case we ignore the
|
|
// fixup because the import section will never be eliminated
|
|
// by TCE.
|
|
|
|
assert(psym->N.Name.Short &&
|
|
strncmp(SzNameSymPst(*psym, pimage->pst), ".idata", 6) == 0);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
MakeEdgePextFromISym(
|
|
PMOD pmod)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the EDG structure by validating the sz field of the sym union.
|
|
Before this function is called, the isym field is valid, which is the
|
|
index of the symbol in the symbol table. By storing the index and
|
|
later updating the string pointer we don't have multiple copies of
|
|
all the symbol strings lying around. For macword in pcode (when every
|
|
function generates three comdats) this will save and estimated 4 or 5
|
|
Meg of RAM.
|
|
|
|
Arguments:
|
|
|
|
pmod - module whose contributors' edges are to be updated
|
|
|
|
pst - external symbol table hash table
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENM_SRC enm_src;
|
|
|
|
InitEnmSrc(&enm_src, pmod);
|
|
|
|
// For all cons in this module...
|
|
while (FNextEnmSrc(&enm_src)) {
|
|
PCON pcon;
|
|
PNOD pnod;
|
|
ENM_EDG enm_edg;
|
|
|
|
pcon = enm_src.pcon;
|
|
|
|
if (pcon->flags & IMAGE_SCN_LNK_REMOVE) {
|
|
// Ignore removed sections
|
|
|
|
continue;
|
|
}
|
|
|
|
pnod = PnodPcon(pcon);
|
|
|
|
// And for all edges in each con, validate the pext field of the
|
|
// Sym union.
|
|
|
|
InitEnmEdg(&enm_edg, pnod);
|
|
while (FNextEnmEdg(&enm_edg)) {
|
|
PEDG pedg;
|
|
|
|
pedg = enm_edg.pedg;
|
|
|
|
if (pedg->pcon == NULL) {
|
|
pedg->Sym.pext = rgpExternObj[pedg->Sym.isym];
|
|
|
|
assert(pedg->Sym.pext);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PENT
|
|
PentNew_TCE(
|
|
const char *sz,
|
|
PEXTERNAL pext,
|
|
PCON pcon,
|
|
PPENT ppentHead)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a ENT, which is an entry point to the TCE graph. One and only one
|
|
of {sz, pext, pcon} must be !NULL.
|
|
|
|
Arguments:
|
|
|
|
sz - name of entry point to TCE graph
|
|
|
|
pext - external for entry point
|
|
|
|
pcon - contribution of entry point to graph
|
|
|
|
ppentHead - head entry point in list
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PENT pent;
|
|
|
|
assert(( sz && !pext && !pcon) ||
|
|
(!sz && pext && !pcon) ||
|
|
(!sz && !pext && pcon));
|
|
|
|
assert(ppentHead);
|
|
|
|
pent = (PENT) PvNew(1, sizeof(ENT));
|
|
|
|
if (sz) {
|
|
pent->sz = sz;
|
|
pent->e = TCE_sz;
|
|
} else if (pext) {
|
|
pent->pext = pext;
|
|
pent->e = TCE_ext;
|
|
} else if (pcon) {
|
|
pent->pcon = pcon;
|
|
pent->e = TCE_con;
|
|
}
|
|
|
|
if (*ppentHead) {
|
|
pent->pentNext = *ppentHead;
|
|
}
|
|
|
|
*ppentHead = pent;
|
|
|
|
return(pent);
|
|
}
|
|
|
|
void
|
|
CreateGraph_TCE(
|
|
PST pst)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walk the NOD adjacency list and resolve symbolic edges.
|
|
|
|
Arguments:
|
|
|
|
pst - external symbol table hash table
|
|
|
|
pcodHead - head CON in TCE graph
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENM_NOD enm_nod;
|
|
|
|
InitEnmNod(&enm_nod, pconHeadGraph);
|
|
while (FNextEnmNod(&enm_nod)) {
|
|
PNOD pnod;
|
|
ENM_EDG enm_edg;
|
|
|
|
pnod = enm_nod.pnod;
|
|
|
|
InitEnmEdg(&enm_edg, pnod);
|
|
while (FNextEnmEdg(&enm_edg)) {
|
|
PEDG pedg;
|
|
|
|
pedg = enm_edg.pedg;
|
|
|
|
if (pedg->pcon == NULL) {
|
|
PEXTERNAL pext;
|
|
|
|
pext = pedg->Sym.pext;
|
|
assert(pext != NULL);
|
|
|
|
if ((pext->Flags & EXTERN_DEFINED) == 0) {
|
|
pedg->pcon = NULL;
|
|
} else {
|
|
pedg->pcon = pext->pcon;
|
|
|
|
// If reloc is to a pcode sym from native code, remember
|
|
// the NEP comdat since it must be colored.
|
|
|
|
if ((fM68K || fPowerMac) && pedg->NEPInfo.fFromNative) {
|
|
if (FPcodeSym(pext->ImageSymbol)) {
|
|
PEXTERNAL pextNEP = FindExtAlternatePcodeSym(pext, pst, FALSE);
|
|
if (pextNEP == NULL && fPowerMac && READ_BIT(pext, sy_WEAKEXT)) {
|
|
PEXTERNAL extSymWeakPtr = PextWeakDefaultFind(pext);
|
|
assert (extSymWeakPtr);
|
|
pextNEP = FindExtAlternatePcodeSym(extSymWeakPtr, pst, FALSE);
|
|
}
|
|
assert(pextNEP);
|
|
assert(pextNEP->pcon);
|
|
StorepconPcodeNEP(pedg, pextNEP->pcon);
|
|
} else {
|
|
pedg->NEPInfo.fFromNative = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ColorPCON(
|
|
PCON pcon)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Color a NOD's corresponding CON referenced and recurse through the graph.
|
|
|
|
Arguments:
|
|
|
|
pcon - node in TCE graph
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(pcon);
|
|
|
|
if ((pcon->flags & TCE_Referenced) == 0) {
|
|
PNOD pnod;
|
|
ENM_EDG enm_edg;
|
|
|
|
pcon->flags |= TCE_Referenced;
|
|
|
|
pnod = PnodPcon(pcon);
|
|
|
|
InitEnmEdg(&enm_edg, pnod);
|
|
while (FNextEnmEdg(&enm_edg)) {
|
|
PEDG pedg;
|
|
|
|
pedg = enm_edg.pedg;
|
|
|
|
if (pedg->pcon != NULL) {
|
|
ColorPCON(pedg->pcon);
|
|
}
|
|
|
|
if ((fM68K || fPowerMac) && (pedg->NEPInfo.pconPcodeNEP != NULL)) {
|
|
assert(fPCodeInApp);
|
|
|
|
ColorPCON(pedg->NEPInfo.pconPcodeNEP);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
WalkGraphEntryPoints_TCE(
|
|
PENT pentHead,
|
|
PST pst)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Walk the TCE graph starting at each entry point.
|
|
|
|
Arguments:
|
|
|
|
pent - set of entry points
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENM_ENT enm_ent;
|
|
PEXTERNAL pext;
|
|
PENT pent;
|
|
PCON pconT;
|
|
|
|
InitEnmEnt(&enm_ent, pentHead);
|
|
while (FNextEnmEnt(&enm_ent)) {
|
|
pent = enm_ent.pent;
|
|
|
|
switch (pent->e) {
|
|
case TCE_con:
|
|
pconT = pent->pcon;
|
|
break;
|
|
|
|
case TCE_ext:
|
|
assert(pent->pext);
|
|
if (pent->pext->Flags & EXTERN_DEFINED) {
|
|
pconT = pent->pext->pcon;
|
|
} else {
|
|
// This only happens if -force so we are deliberately
|
|
// ignoring unresolved externals ...
|
|
|
|
pconT = NULL;
|
|
}
|
|
break;
|
|
|
|
case TCE_sz:
|
|
pext = SearchExternSz(pst, pent->sz);
|
|
assert(pext);
|
|
pconT = pext->pcon;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
if (pconT != NULL) {
|
|
ColorPCON(pconT);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
FDiscardPCON_TCE(
|
|
PCON pcon)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decide whether to discard a contribution.
|
|
|
|
Arguments:
|
|
|
|
pcon - contribution in image/driver map
|
|
|
|
Return Value:
|
|
|
|
!0 if we are to discard node, 0 otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (pcon->flags & TCE_Referenced) {
|
|
return(0);
|
|
}
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
void
|
|
DisplayDiscardedPcon(
|
|
PCON pcon,
|
|
PNOD pnod)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to standard out what code will not be included in the image that
|
|
would have been had TCE not been invoked.
|
|
Arguments:
|
|
|
|
pnod - head node of TCE graph
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
UINT MsgNumber;
|
|
const char *sz;
|
|
char *szOutput;
|
|
|
|
if (pnod == NULL) {
|
|
pnod = PnodPcon(pcon);
|
|
}
|
|
|
|
sz = pnod->sz;
|
|
|
|
if (sz != NULL) {
|
|
MsgNumber = TCESYM;
|
|
|
|
szOutput = SzOutputSymbolName(sz, TRUE);
|
|
} else if (PsecPCON(pcon) != psecDebug) {
|
|
MsgNumber = TCEGRP;
|
|
|
|
sz = szOutput = pcon->pgrpBack->szName;
|
|
} else {
|
|
szOutput = NULL;
|
|
}
|
|
|
|
if (szOutput != NULL) {
|
|
fputs(" ", stdout);
|
|
|
|
if (PmodPCON(pcon) != pmodLinkerDefined) {
|
|
char szBuf[_MAX_PATH * 2];
|
|
|
|
Message(MsgNumber, szOutput, SzComNamePMOD(PmodPCON(pcon), szBuf));
|
|
} else {
|
|
assert(MsgNumber == TCESYM);
|
|
|
|
Message(TCESYMNOMOD, sz);
|
|
}
|
|
|
|
if (szOutput != sz) {
|
|
FreePv(szOutput);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
Verbose_TCE(
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Write to standard out what code will not be included in the image that
|
|
would have been had TCE not been invoked.
|
|
Arguments:
|
|
|
|
pnod - head node of TCE graph
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENM_NOD enm_nod;
|
|
|
|
InitEnmNod(&enm_nod, pconHeadGraph);
|
|
while (FNextEnmNod(&enm_nod)) {
|
|
PCON pcon;
|
|
PNOD pnod;
|
|
|
|
pcon = enm_nod.pcon;
|
|
pnod = enm_nod.pnod;
|
|
|
|
if (FDiscardPCON_TCE(pcon)) {
|
|
DisplayDiscardedPcon(pcon, pnod);
|
|
|
|
DBEXEC(DB_TCE_DISCARD, DumpPNOD_TCE(pcon, pnod));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TCE adjacency list enumerator.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
INIT_ENM(Nod, NOD, (ENM_NOD *penm, PCON pcon)) {
|
|
penm->pcon = pcon;
|
|
penm->pnod = NULL;
|
|
}
|
|
NEXT_ENM(Nod, NOD) {
|
|
if (penm->pnod != NULL) {
|
|
penm->pcon = penm->pnod->pconNext;
|
|
}
|
|
|
|
if (penm->pcon == NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
penm->pnod = PnodPcon(penm->pcon);
|
|
|
|
return(TRUE);
|
|
}
|
|
END_ENM(Nod, NOD) {
|
|
}
|
|
DONE_ENM
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TCE graph adjacency edge enumerator.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
INIT_ENM(Edg, EDG, (ENM_EDG *penm, PNOD pnod)) {
|
|
assert(penm);
|
|
penm->pnod = pnod;
|
|
penm->pedg = NULL;
|
|
penm->iedg = 0;
|
|
}
|
|
NEXT_ENM(Edg, EDG) {
|
|
assert(penm);
|
|
assert(penm->pnod);
|
|
assert(penm->pnod->iedg <= penm->pnod->cedg);
|
|
|
|
penm->pedg = NULL;
|
|
|
|
if (penm->iedg == penm->pnod->iedg) {
|
|
if (penm->pnod->pnodNext == NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
penm->pnod = penm->pnod->pnodNext;
|
|
penm->iedg = 0;
|
|
}
|
|
|
|
if (penm->iedg < penm->pnod->iedg) {
|
|
penm->pedg = &penm->pnod->rgedg[penm->iedg];
|
|
penm->iedg++;
|
|
}
|
|
|
|
return(penm->pedg != NULL);
|
|
}
|
|
END_ENM(Edg, EDG) {
|
|
}
|
|
DONE_ENM
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
TCE graph entry points enumerator.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
INIT_ENM(Ent, ENT, (ENM_ENT *penm, PENT pent)) {
|
|
assert(penm);
|
|
penm->pentStart = pent;
|
|
penm->pent = NULL;
|
|
}
|
|
NEXT_ENM(Ent, ENT) {
|
|
assert(penm);
|
|
|
|
if (!penm->pent) {
|
|
penm->pent = penm->pentStart;
|
|
} else {
|
|
penm->pent = penm->pent->pentNext;
|
|
}
|
|
|
|
return(penm->pent != NULL);
|
|
}
|
|
END_ENM(Ent, ENT) {
|
|
}
|
|
DONE_ENM
|
|
|
|
|
|
void StorepconPcodeNEP(PEDG pedg, PCON pconPcodeNEP)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stores pconPcodeNEP in the pedg structure. This field will later be
|
|
used to color the NEP comdat if the pcode symbol is referenced from
|
|
native.
|
|
|
|
Arguments:
|
|
|
|
pedg - Points to the edg structure to be updated.
|
|
|
|
pconPcodeNEP - Points to the NEP comdat.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(pedg);
|
|
assert(pconPcodeNEP);
|
|
|
|
pedg->NEPInfo.pconPcodeNEP = pconPcodeNEP;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
const char *
|
|
SzRefPCON (
|
|
PCON pcon)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Output either 'referenced' or 'not referenced'.
|
|
|
|
Arguments:
|
|
|
|
pcon - node in TCE graph to check
|
|
|
|
Return Value:
|
|
|
|
'referenced' if pnod was referenced, 'not referenced' otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
static const char * const rgsz[2] = {
|
|
"R",
|
|
"N"};
|
|
|
|
assert(pcon);
|
|
|
|
return((pcon->flags & TCE_Referenced) ? rgsz[0] : rgsz[1]);
|
|
}
|
|
|
|
void
|
|
DumpPNOD_TCE(
|
|
PCON pcon,
|
|
PNOD pnod)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump a PNOD to standard out. This routine is not #ifndef'ed since it is
|
|
used in a release build when -verbose and TCE are on.
|
|
|
|
Arguments:
|
|
|
|
pnod - node in TCE graph to dump
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(pcon != NULL);
|
|
assert(pnod != NULL);
|
|
|
|
DBPRINT("%s", SzRefPCON(pcon));
|
|
DBPRINT("|sec=%.8s", PsecPCON(pcon)->szName);
|
|
assert(pcon->pgrpBack);
|
|
DBPRINT("|grp=%.8s", pcon->pgrpBack->szName);
|
|
DBPRINT("|con=0x%p", pcon);
|
|
assert(PsecPCON(pcon));
|
|
DBPRINT("|mod=%s", SzObjNamePCON(pcon));
|
|
if (pnod->sz) {
|
|
DBPRINT("|%s", pnod->sz);
|
|
}
|
|
DBPRINT("\n");
|
|
}
|
|
|
|
void
|
|
DumpPEDG_TCE(
|
|
PEDG pedg)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump a PEDG to standard out.
|
|
|
|
Arguments:
|
|
|
|
pedg - edge in TCE graph to dump
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
assert(pedg != NULL);
|
|
|
|
if (pedg->pcon != NULL) {
|
|
DumpPNOD_TCE(pedg->pcon, PnodPcon(pedg->pcon));
|
|
}
|
|
}
|
|
|
|
void
|
|
DumpGraph_TCE(
|
|
VOID)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dump the TCE graph to standard out.
|
|
|
|
Arguments:
|
|
|
|
pconHead - root of adjacency list representation of TCE graph
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENM_NOD enm_nod;
|
|
|
|
DBPRINT("Dump of comdat dependency graph for TCE\n");
|
|
DBPRINT("---------------------------------------\n");
|
|
|
|
InitEnmNod(&enm_nod, pconHeadGraph);
|
|
while (FNextEnmNod(&enm_nod)) {
|
|
PCON pcon;
|
|
PNOD pnod;
|
|
ENM_EDG enm_edg;
|
|
|
|
pcon = enm_nod.pcon;
|
|
pnod = enm_nod.pnod;
|
|
|
|
DBPRINT("PNOD|");
|
|
DumpPNOD_TCE(pcon, pnod);
|
|
|
|
InitEnmEdg(&enm_edg, pnod);
|
|
while (FNextEnmEdg(&enm_edg)) {
|
|
PEDG pedg;
|
|
|
|
pedg = enm_edg.pedg;
|
|
|
|
DBPRINT(" PEDG|");
|
|
DumpPEDG_TCE(pedg);
|
|
}
|
|
|
|
DBPRINT("\n");
|
|
}
|
|
|
|
fflush(stdout);
|
|
}
|
|
|
|
#endif // DBG
|