/* * Module Name: WSREDUCE.C * * Program: WSREDUCE * * * Description: * * Performs data reduction on the function reference data collected * by WST.DLL. Analyzes the WSP file information, and produces * a suggested list for the ordering of functions within the tuned * modules. An ASCII version of the reordered function list is written * to stdout. In addition, a WSR file for each reduced module is * produced for subsequent use by WSPDUMP /R. * * The reduction algorithm employed by WSREDUCE is described in detail * in WSINSTR.DOC. Briefly, each function monitored by the working set tuner * is considered to be a vertex in a graph. There is an edge from vertex * "A" to vertex "B" if the function reference strings for "A" and "B" * have any overlapping 1 bits. Likewise, there is an edge from vertex "B" * to vertex "A". The edges between vertices are weighted depending on * the relative importance of the ending vertex, and the number of * overlapping bits between the start and end vertices. The relative * importance of the end vertices, and the weighted edges between * vertices, is stored in a decision matrix. A greedy algorithm is run on * the decision matrix to determine a better ordering for the measured * functions. * * * Microsoft Confidential * * Copyright (c) Microsoft Corporation 1992 * * All Rights Reserved * * Modification History: * * Modified for NT June 13, 1992 MarkLea. * 4-23-98: QFE - Performance unacceptable on high function counts DerrickG (mdg): * - new WSP file format for large symbol counts (ULONG vs. USHORT) * - support for long file names (LFN) of input/output files * - removed buggy reference to WSDIR env. variable * - removed command-line parsing from wsReduceMain() * - based .TMI & .WSR file names exclusively on .WSP name for consistency * - removed limit on symbol name lengths - return allocated name from WsTMIReadRec() * - removed unused code and symbols * - Analyzed the code blocked off by OPTIMIZE - it doesn't produce the same * output as non-OPTIMIZEd code, and is buggy (won't build as is) - removed. * - Removed multiple module capabilities from code (shell sends one at a time) * - I addressed memory and performance issues by using a smaller allocation * for WsDecision (USHORT vs. long), using one value to mark a taken vertex * (as opposed to half the value space by using -1), and an optional * progress indicator to reassure users. Modified wsRedScaleWsDecision() * to maximize the scaled values (using some more float math). * - Added "pnEdges" and "nEdgeCount" to function structure. If the number * of set functions is < USHRT_MAX (very likely, even for very large * projects), allocate as needed a sorted index for WsRedReorder(). This * cuts dramatically the number of passes through the matrix searching for * the next edge to consider, and permits some other optimizations. The * optimized algorithm produces identical results for the important high * usage high overlap functions, but could diverge in the results for low * usage (2 or 1 hits) low overlap functions. Differences are not * significant from a results performance perspective - a better algorithm * would give marginally better results. The original algorithm is in place * bracketed with "#ifdef SLOWMO". * * */ #include "wstune.h" /* * Function prototypes. */ VOID wsRedInitialization( VOID ); VOID wsRedInitModules( VOID ); VOID wsRedInitFunctions( VOID ); VOID wsRedSetup( VOID ); VOID wsRedSetWsDecision( VOID ); VOID wsRedScaleWsDecision( VOID ); VOID wsRedWeightWsDecision( VOID ); #ifdef SLOWMO UINT wsRedChooseEdge( UINT ); #else // SLOWMO UINT wsRedChooseEdgeOpt( UINT ); // mdg 98/4 Alternate optimized edge chooser INT __cdecl wsRedChooseEdgeOptCmp ( const UINT *, const UINT * ); BOOL wsRedChooseEdgeOptAlloc( UINT uiIndex ); UINT wsRedChooseEdgeOptNextEdge( UINT uiIndex, BOOL bNoSelectOpt ); #endif // SLOWMO VOID wsRedReorder( VOID ); VOID wsRedOutput( VOID ); VOID wsRedOpenWSR( FILE **); VOID wsRedExit( UINT, USHORT, UINT, ULONG, PSZ ); VOID wsRedCleanup(VOID); /* * Type definitions and structure declarations. */ /* Data reduction per module information */ struct wsrmod_s { FILE *wsrmod_hFileWSR; // module's WSR file pointer FILE *wsrmod_hFileTMI; // module's TMI file pointer FILE *wsrmod_hFileWSP; // module's WSP file handle union { PCHAR wsrmod_pchModName;// pointer to module base name PCHAR wsrmod_pchModFile;// pointer to WSP file name } wsrmod_un; ULONG wsrmod_ulOffWSP; // offset of first function bitstring }; typedef struct wsrmod_s wsrmod_t; /* Data reduction per function information */ struct wsrfxn_s { PCHAR wsrfxn_pchFxnName; // pointer to function name ULONG wsrfxn_cbFxn; // Size of function in bytes BOOL wsrfxn_fCandidate; // Candidate flag #ifndef SLOWMO UINT nEdgesLeft; // Count of sorted edges left to consider in WsDecision for this function UINT nEdgesAlloc; // Number of items allocated in pnEdges UINT * pnEdges; // Allocated array of sorted edges for this function #endif // SLOWMO }; typedef struct wsrfxn_s wsrfxn_t; /* * Global variable declaration and initialization. */ static char *szFileWSP = NULL; // WSP file name static char *szFileTMI = NULL; // TMI file name static char *szFileWSR = NULL; // WSR file name static ULONG rc = NO_ERROR; // Return code static ULONG ulTmp; // Temp variable for Dos API returns static UINT cTmiFxns = 0; // Number of functions in tmi file static UINT cFxnsTot = 0; // Total number of functions static UINT cSnapsTot = 0; // Total number of snapshots static UINT cbBitStr = 0; // Number of bytes per fxn bitstring #ifdef DEBUG static BOOL fVerbose = FALSE; // Flag for verbose mode #endif /* DEBUG */ #ifndef TMIFILEHACK static BOOL fFxnSizePresent = FALSE; // Flag for function size availability #endif /* !TMIFILEHACK */ static wsrmod_t WsrMod; // Module information static wsrmod_t *pWsrMod = &WsrMod; // Pointer for legacy use static wsrfxn_t *WsrFxn; // Pointer to function information static ULONG *FxnBits; // Pointer to dword of bitstring static ULONG *FxnOrder; // Pointer to ordered list of // function ordinals typedef USHORT WsDecision_t; #define WSDECISION_TAKEN USHRT_MAX // Reserve highest value for special code #define WsDecision_MAX (WSDECISION_TAKEN-1) // Use fullest spread for decision matrix static WsDecision_t **WsDecision; // Decision matrix for data reduction; mdg 98/4 use small alloc for large symbol counts static ULONG ulRefHi1 = 0; // Highest diagonal value (for WsRedScaleWsDecision) static ULONG ulRefHi2 = 0; // Second highest diagonal value (for WsRedScaleWsDecision) static UINT uiSelected = 0; // Highest function ordinal selected (for WsRedReorder) static UINT cFxnOrder = 0; // Count of ordered functions #ifndef SLOWMO static UINT nFxnToSort; // To pass static value to wsRedChooseEdgeOptCmp() #endif // SLOWMO static FILE *hFileWLK = NULL; // Handle to file containing ordered HGLOBAL hMem[10]; ULONG ulFxnIndex; // Index of original TMI order of function. #ifdef TMR ULONG pqwTime0[2]; #endif /* TMR */ /* * Procedure wsReduceMain * * *** * Effects: * * Performs data reduction and analysis on the input modules' function reference * data. * * szBaseName Specifies a module WSP file name */ BOOL wsReduceMain( CHAR *szBaseName ) { size_t i; char * pSlash; szFileWSP = malloc( i = strlen( szBaseName ) + 5 ); if (szFileWSP) { szFileWSP = strcat( strcpy(szFileWSP , szBaseName ), ".WSP" ); } else { exit(1); } szFileTMI = malloc( i ); if (szFileTMI) { szFileTMI = strcat( strcpy( szFileTMI, szBaseName ), ".TMI" ); } else { free(szFileWSP); exit(1); } #ifdef DEBUG fVerbose = fDbgVerbose; #endif // DEBUG // Create output file in current directory if (NULL != (pSlash = strrchr( szBaseName, '\\' )) || NULL != (pSlash = strrchr( szBaseName, '/' )) || NULL != (pSlash = strrchr( szBaseName, ':' ))) { ++pSlash; szFileWSR = malloc(strlen( pSlash ) + 5 ); if (szFileWSR) { szFileWSR = strcat( strcpy(szFileWSR, pSlash ), ".WSR" ); } else { free(szFileTMI); free(szFileWSP); exit(1); } } else { szFileWSR = malloc( i ); if (szFileWSR) { szFileWSR = strcat( strcpy( szFileWSR, szBaseName ), ".WSR" ); } else { free(szFileTMI); free(szFileWSP); exit(1); } } #ifdef TMR DosTmrQueryTime((PQWORD)pqwTime0); printf("Top of Main, 0x%lx:0x%lx\n", pqwTime0[1], pqwTime0[0]); #endif /* TMR */ pWsrMod->wsrmod_un.wsrmod_pchModFile = szFileWSP; #ifdef DEBUG printf("\t%s\n", pWsrMod->wsrmod_un.wsrmod_pchModFile); #endif /* DEBUG */ // Initialize module and function information structures. wsRedInitialization(); // Set up weighted decision matrix. wsRedSetup(); // Perform the function reference data analysis. wsRedReorder(); // Output the analysis results. wsRedOutput(); // Cleanup memory allocations. wsRedCleanup(); free( szFileWSP ); free( szFileWSR ); free( szFileTMI ); return(NO_ERROR); } /* * ***LP wsRedInitialization * * * Effects: * - Calls wsRedInitModules to: * o Open and validate each module's WSP file. * o Open and validate each module's TMI file. * - Calls wsRedInitFunctions to: * o Set up WsrFxn[] with per function information. * o Allocate FxnBits[]. * - Allocates WsDecision[][]. * - Allocates and initializes DiagonalFxn[]. * * Returns: * * Void. If an error is encountered, exits through wsRedExit() * with ERROR. */ VOID wsRedInitialization() { UINT i; // Loop counter // Setup module information. wsRedInitModules(); // Setup function information for each module. wsRedInitFunctions(); // Allocate the decision matrix, WsDecision[cFxnsTot][cFxnsTot]. WsDecision = (WsDecision_t **) AllocAndLockMem((cFxnsTot * cFxnsTot * sizeof(WsDecision_t)) + (cFxnsTot * sizeof(WsDecision_t *)), &hMem[1]); if (WsDecision == NULL) wsRedExit(ERROR, PRINT_MSG, MSG_NO_MEM, (cFxnsTot+1)*cFxnsTot*sizeof(WsDecision_t), "WsDecision[][]"); for (i = 0; i < cFxnsTot; i++) { WsDecision[i] = (WsDecision_t *) (WsDecision+cFxnsTot)+(i*cFxnsTot); } } /* * ***LP wsRedInitModules * * * Effects: * - Opens and validates each module's WSP file. * - Opens and validates each module's TMI file. * * Returns: * * Void. If an error is encountered, exits through wsRedExit() * with ERROR. */ VOID wsRedInitModules() { wsphdr_t WspHdr; // WSP file header UINT cFxns = 0; // Number of functions for this module ULONG ulTimeStamp = 0; // Time stamp ULONG ulTDFID = 0; // TDF Identifier /* Open module's input WSP file. Read and validate * WSP file header. */ rc = WsWSPOpen(pWsrMod->wsrmod_un.wsrmod_pchModFile, &(pWsrMod->wsrmod_hFileWSP), (PFN) wsRedExit, &WspHdr, ERROR, PRINT_MSG ); if (NULL == (pWsrMod->wsrmod_un.wsrmod_pchModName = malloc( 1 + WspHdr.wsphdr_dtqo.dtqo_cbPathname ))) wsRedExit(ERROR, PRINT_MSG, MSG_NO_MEM, WspHdr.wsphdr_dtqo.dtqo_cbPathname + 1, pWsrMod->wsrmod_un.wsrmod_pchModFile); rc = fread( pWsrMod->wsrmod_un.wsrmod_pchModName, WspHdr.wsphdr_dtqo.dtqo_cbPathname, 1, pWsrMod->wsrmod_hFileWSP ); if (rc != 1) wsRedExit(ERROR, PRINT_MSG, MSG_FILE_BAD_HDR, (ULONG)-1L, pWsrMod->wsrmod_un.wsrmod_pchModFile); pWsrMod->wsrmod_un.wsrmod_pchModName[WspHdr.wsphdr_dtqo.dtqo_cbPathname] = '\0'; ulTimeStamp = WspHdr.wsphdr_ulTimeStamp; cSnapsTot = WspHdr.wsphdr_ulSnaps; cbBitStr = cSnapsTot * sizeof(ULONG); pWsrMod->wsrmod_ulOffWSP = WspHdr.wsphdr_ulOffBits; /* * Open associated TMI file. Assume it lives in same directory. * Read and validate TMI header. Increment cFxnsTot. */ cTmiFxns = WsTMIOpen(szFileTMI, &(pWsrMod->wsrmod_hFileTMI), (PFN) wsRedExit, 0, (PCHAR)0); cFxns = WspHdr.wsphdr_dtqo.dtqo_SymCnt; #ifdef DEBUG printf("%s file header: # fxns = %ld, TDF ID = 0x%x\n", szFileTMI, cFxns, (UINT) WspHdr.wsphdr_dtqo.dtqo_usID); #endif /* DEBUG */ cFxnsTot = cFxns; // If no function data to analyze, just exit without error. if (cFxnsTot == 0) wsRedExit(NO_ERROR, NO_MSG, NO_MSG, 0, NULL); } /* * ***LP wsRedInitFunctions * * * Effects: * - Sets up WsrFxn[] with per function information. * - Allocates FxnBits[]. * * Returns: * * Void. If an error is encountered, exits through wsRedExit() * with ERROR. */ VOID wsRedInitFunctions() { UINT uiFxn = 0; // Function number UINT cFxns = 0; // Number of functions for this module // Allocate memory for per function info, WsrFxn[cFxnsTot]. WsrFxn = (wsrfxn_t *) AllocAndLockMem(cFxnsTot*sizeof(wsrfxn_t), &hMem[3]); if (WsrFxn == NULL) wsRedExit(ERROR, PRINT_MSG, MSG_NO_MEM, cFxnsTot * sizeof(wsrfxn_t), "WsrFxn[]"); WsIndicator( WSINDF_NEW, "Load Functions", cFxnsTot ); // Initialize WsrFxn[cFxnsTot]. uiFxn = 0; // loop index init cFxns = cFxnsTot; // loop invariant #ifdef DEBUG if (fVerbose) { printf("Initializing WsrFxn[] for %s:\n\tstart/end fxn indices (%d/%d)\n", pWsrMod->wsrmod_un.wsrmod_pchModName, uiFxn, cFxns - 1); printf("TMI file handle: %ld\n",pWsrMod->wsrmod_hFileTMI); } #endif /* DEBUG */ for (; uiFxn < cFxns; uiFxn++) { WsIndicator( WSINDF_PROGRESS, NULL, uiFxn ); WsrFxn[uiFxn].wsrfxn_cbFxn = WsTMIReadRec(&(WsrFxn[uiFxn].wsrfxn_pchFxnName),&ulFxnIndex,&ulTmp,pWsrMod->wsrmod_hFileTMI, (PFN) wsRedExit, (PCHAR)0); #ifdef DEBUG if (fVerbose) printf("\tWsrFxn[%d] %s\n", uiFxn, WsrFxn[uiFxn].wsrfxn_pchFxnName ); #endif /* DEBUG */ WsrFxn[uiFxn].wsrfxn_fCandidate = TRUE; } // Close TMI file. fclose(pWsrMod->wsrmod_hFileTMI); WsIndicator( WSINDF_FINISH, NULL, 0 ); // Allocate space to hold 32 snapshots for each function. FxnBits = (ULONG *) AllocAndLockMem(cFxnsTot*sizeof(ULONG), &hMem[4]); if (FxnBits == NULL) wsRedExit(ERROR, PRINT_MSG, MSG_NO_MEM, cFxnsTot * sizeof(ULONG), "FxnBits[]"); } /* * ***LP wsRedSetup * * * Effects: * * Initializes the data structures used to analyze the function * reference bitstrings, including the weighted decision matrix. * * Returns: * * Void. If an error is encountered, exits through wsRedExit() * with ERROR. */ VOID wsRedSetup() { wsRedSetWsDecision(); // set up initial decision matrix wsRedScaleWsDecision(); // scale the decision matrix wsRedWeightWsDecision(); // weight the matrix "edge" entries } /* * ***LP wsRedSetWsDecision * * * Effects: * * Initializes and weights the decision matrix, WsDecision[][]. * * Returns: * * Void. If an error is encountered, exits through wsRedExit() * with ERROR. */ VOID wsRedSetWsDecision() { UINT i = 0, j = 0; // Temporary loop indexes UINT uiFxn = 0; // Function number UINT uiFBits = 0; // Loop index for bitstring dwords UINT clFBits = 0; // Count of fxn bitstring dwords ULONG ulResult = 0; // Returned from procedure call FILE *hFile; // File handle /* For each dword of snapshot bitstrings...*/ clFBits = (cbBitStr + sizeof(ULONG) - 1) / sizeof(ULONG); WsIndicator( WSINDF_NEW, "Fill In Matrix", clFBits * cFxnsTot ); for (uiFBits = 0; uiFBits < clFBits; uiFBits++) { ULONG ulOffWSP; WsIndicator( WSINDF_PROGRESS, "Reading Snaps ", 0 ); // Fill in FxnBits for this snapshot #ifdef DEBUG if (fVerbose) printf( "Setting up FxnBits snapshot %lu for %s\n", uiFBits, pWsrMod->wsrmod_un.wsrmod_pchModName ); #endif /* DEBUG */ hFile = pWsrMod->wsrmod_hFileWSP; ulOffWSP = uiFBits + pWsrMod->wsrmod_ulOffWSP; for ( uiFxn = 0; uiFxn < cFxnsTot; uiFxn++, ulOffWSP += cbBitStr) // Loop functions { // Seek to next dword of function's bitstring. if ((rc = fseek( hFile, ulOffWSP, SEEK_SET )) != NO_ERROR) wsRedExit(ERROR, PRINT_MSG, MSG_FILE_OFFSET,rc, pWsrMod->wsrmod_un.wsrmod_pchModName); // Read next dword of function's bitstring. rc = fread( &(FxnBits[uiFxn]), sizeof(ULONG), 1, hFile ); if(rc != 1) wsRedExit(ERROR, PRINT_MSG, MSG_FILE_READ, rc, pWsrMod->wsrmod_un.wsrmod_pchModName); } // for each function WsIndicator( WSINDF_PROGRESS, "Fill In Matrix", 0 ); hFile = pWsrMod->wsrmod_hFileWSP; #ifdef DEBUG if (fVerbose) printf("Setting up WsDecision[][] for %s:\n\tstart/end fxn indices (%d/%d)\n", pWsrMod->wsrmod_un.wsrmod_pchModName, uiFxn, cFxnsTot - 1); #endif /* DEBUG */ /* For each function... */ for ( uiFxn = 0; uiFxn < cFxnsTot; uiFxn++ ) { WsIndicator( WSINDF_PROGRESS, NULL, (uiFBits * cFxnsTot) + uiFxn ); // Get the current snapshot ulTmp = FxnBits[uiFxn]; #ifdef DEBUG if (fVerbose) printf("\tFxnBits[%d] = 0x%lx\n", uiFxn, ulTmp); #endif /* DEBUG */ /* If there are bits set... */ if (ulTmp != 0) { /* Sum the "on" bits and add the result * to WsDecision[uiFxn][uiFxn]. */ ulResult = 0; while (ulTmp) { ++ulResult; ulTmp &= ulTmp - 1; } ulTmp = WsDecision[uiFxn][uiFxn] += (WsDecision_t)ulResult; if (ulTmp > ulRefHi2) // Set the highest two diagonal values on the last pass if (ulTmp > ulRefHi1) { ulRefHi2 = ulRefHi1; ulRefHi1 = ulTmp; uiSelected = uiFxn; // Remember highest value's index } else ulRefHi2 = ulTmp; /* Sum the overlapping "on" bits for this * function's dword with each preceding * function's dword, and add the results to * WsDecision[][]. */ for (i = 0; i < uiFxn; i++) { ulTmp = FxnBits[i] & FxnBits[uiFxn]; if (ulTmp) // mdg 98/4 { ulResult = 0; while (ulTmp) { ++ulResult; ulTmp &= ulTmp - 1; } WsDecision[uiFxn][i] += (WsDecision_t)ulResult; WsDecision[i][uiFxn] += (WsDecision_t)ulResult; } } /* End For each previous function's dword */ } /* End If there are bits set...*/ } /* End For each function... */ } /* End For each dword of bitstrings */ WsIndicator( WSINDF_FINISH, NULL, 0 ); #ifdef DEBUG if (fVerbose) { printf("\nRAW MATRIX:\n"); for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++) { printf("row %4d:\n", uiFxn); for (i = 0; i < cFxnsTot; i++) printf("0x%lx ", (LONG)WsDecision[uiFxn][i]); printf("\n"); } } #endif /* DEBUG */ } /* * ***LP wsRedOpenWSR * * * Effects: * Opens the output WSR files, one per module. If only one module * is being reduced, also opens a WLK file, setting the WLK file handle * as a side effect. * * * Returns: * * Void. If an error is encountered, exits through wsRedExit() * with ERROR. */ VOID wsRedOpenWSR(FILE **phFileWLK) { /* Close WSP file, and open module output file. */ fclose(pWsrMod->wsrmod_hFileWSP); if ((pWsrMod->wsrmod_hFileWSR = fopen(szFileWSR, "w")) == NULL) { wsRedExit(ERROR, PRINT_MSG,MSG_FILE_OPEN,rc, szFileWSR); } /* We're only analyzing ONE module. Also open a WLK * file. This file will contain the function names in their * reordered sequence. The linker will use this file to * automatically reorder functions. Note that we reuse szFileWSR * here. */ strcpy(strstr(szFileWSR, ".WSR"), ".PRF"); if ((*phFileWLK = fopen(szFileWSR, "w")) == NULL) wsRedExit(ERROR, PRINT_MSG,MSG_FILE_OPEN,rc, szFileWSR); } /* * ***LP wsRedScaleWsDecision * * * Effects: * * If necessary, scales the diagonal values of the matrix to avoid overflow * during calculations of the weighted edges (below). Sets up DiagonalFxn[] * as a side effect. Note that we go through gyrations to set * DiagonalFxn up backwards, so that qsort() will handle ties a little better. * * Returns: * * Void. */ VOID wsRedScaleWsDecision() { UINT i = 0, j = 0; // Temporary loop indexes UINT uiFxn = 0; // Function number double fTmp; // Temporary float variable WsDecision_t lTmp; fTmp = (double)ulRefHi1 * (double)ulRefHi2; if (fTmp > WsDecision_MAX) { // Scale down the diagonal. Don't allow rescaled entries // to be zero if they were non-zero before scaling. fTmp /= WsDecision_MAX; printf("%s %s: WARNING -- Scaling back the reduction matrix by %f.\n", szProgName, pszVersion, fTmp); for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++) { lTmp = WsDecision[uiFxn][uiFxn]; if (lTmp) { lTmp = (WsDecision_t)(lTmp / fTmp); // Discard any remainders to avoid potential overflows if (lTmp == 0) WsDecision[uiFxn][uiFxn] = 1; else WsDecision[uiFxn][uiFxn] = lTmp; } } #ifdef DEBUG if (fVerbose) { printf("\nSCALED MATRIX:\n"); for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++) { printf("row %4d:\n", uiFxn); for (i = 0; i < cFxnsTot; i++) printf("0x%lx ", (LONG)WsDecision[uiFxn][i]); printf("\n"); } } #endif /* DEBUG */ } #ifdef DEBUG if (fVerbose) { printf("Got ulRefHi1 = %ld, ulRefHi2 = %ld\n", ulRefHi1, ulRefHi2); } #endif /* DEBUG */ } /* * ***LP wsRedWeightWsDecision * * * Effects: * * Weights the decision matrix edges from start vertex to end vertex, * depending on the relative importance of the end vertex. * * Returns: * * Void. */ VOID wsRedWeightWsDecision() { UINT i = 0, j = 0; // Temporary loop indexes UINT uiFxn = 0; // Function number WsIndicator( WSINDF_NEW, "Weight Matrix ", cFxnsTot ); for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++) { WsIndicator( WSINDF_PROGRESS, NULL, uiFxn ); for (i = 0; i < cFxnsTot; i++) { if (uiFxn == i) continue; if (WsDecision[uiFxn][i]) // mdg 98/4 WsDecision[uiFxn][i] *= WsDecision[i][i]; } } WsIndicator( WSINDF_FINISH, NULL, 0 ); #ifdef DEBUG if (fVerbose) { printf("\nWEIGHTED MATRIX:\n"); for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++) { printf("row %4d:\n", uiFxn); for (i = 0; i < cFxnsTot; i++) printf("0x%lx ", (LONG)WsDecision[uiFxn][i]); printf("\n"); } } #endif /* DEBUG */ } /* * ***LP wsRedReorder * * Requires: * * Effects: * * A greedy algorithm is used to determine a better ordering for the functions * whose reference patterns are represented in the decision matrix. The * algorithm is as follows: * * o Select the function whose value on the diagonal is greatest. * The selected function becomes the current starting vertex, * and is first on the list of ordered functions. Mark that it * is no longer a candidate function. Note that this does NOT mean * that its vertex is removed from the graph. * * o While there is more than one function remaining as a candidate: * * - Choose the edge of greatest weight leading from the current * starting vertex. Ties are broken as follows: If one of the * tied ending vertices is in the selected set and the other is * not, choose the edge whose ending vertex is already selected * (because we already know that vertex is "important"); further * ties are broken by choosing the end vertex whose diagonal value * is greatest. * * - If the ending vertex chosen above is still a candidate (i.e., not * already selected), then select it for the list of ordered * functions, and mark that it is no longer a candidate. * * - Set the matrix entry for the chosen edge to some invalid value, * so that edge will never be chosen again. * * - Set current starting vertex equal to the ending vertex chosen * above. * * o Select the one remaining function for the list of ordered functions. * * mdg 98/4: Added "pnEdges" and "nEdgeCount" to function structure. If the number * of set functions is < USHRT_MAX (very likely, even for very large * projects), allocate as needed a sorted index for WsRedReorder(). This * cuts dramatically the number of passes through the matrix searching for * the next edge to consider. * * Returns: * * Void. */ VOID wsRedReorder() { UINT uiFxn = 0; // Function number UINT i = 0; // Temporary loop index UINT cCandidates = 0; // Count of candidates remaining UINT uiEdge = 0; // Function ordinal edge selected /* Reuse FxnBits[] for the ordered list of functions, FxnOrder[]. */ WsIndicator( WSINDF_NEW, "Reorder Matrix", cFxnsTot ); FxnOrder = FxnBits; memset((PVOID) FxnOrder, 0, cFxnsTot * sizeof(ULONG)); cCandidates = cFxnsTot; FxnOrder[cFxnOrder++] = uiSelected; WsrFxn[uiSelected].wsrfxn_fCandidate = FALSE; --cCandidates; while (cCandidates > 1) { WsIndicator( WSINDF_PROGRESS, NULL, cFxnsTot - cCandidates ); /* Follow highest weighted edge from selected vertex. */ #ifdef SLOWMO uiEdge = wsRedChooseEdge(uiSelected); #else // SLOWMO uiEdge = wsRedChooseEdgeOpt( uiSelected ); #endif // SLOWMO #ifdef DEBUG if (fVerbose) printf("choose edge (%d->%d)\n", uiSelected, uiEdge); #endif uiSelected = uiEdge; if (WsrFxn[uiEdge].wsrfxn_fCandidate) { FxnOrder[cFxnOrder++] = uiSelected; WsrFxn[uiSelected].wsrfxn_fCandidate = FALSE; --cCandidates; } } WsIndicator( WSINDF_FINISH, NULL, 0 ); if (cCandidates == 1) { for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++) if (WsrFxn[uiFxn].wsrfxn_fCandidate) { FxnOrder[cFxnOrder++] = uiFxn; break; } } } #ifdef SLOWMO /* * ***LP wsRedChooseEdge * * * Effects: * * "Selects" a function from the candidate pool, based on weighted * edge from 'index' function to a candidate function. * * * * Returns: * * Ordinal number of selected function. * */ UINT wsRedChooseEdge(UINT uiIndex) { UINT uiFxn = 0; // Function ordinal number. WsDecision_t iMaxWt = WSDECISION_TAKEN; // Highest weighted edge encountered. UINT uiRet = 0; // Return index. for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++) { if (uiFxn == uiIndex || WsDecision[uiIndex][uiFxn] == WSDECISION_TAKEN) continue; if (WsDecision[uiIndex][uiFxn] > iMaxWt || iMaxWt == WSDECISION_TAKEN ) { iMaxWt = WsDecision[uiIndex][uiFxn]; uiRet = uiFxn; } else if (WsDecision[uiIndex][uiFxn] == iMaxWt) { /* Need tiebreak. If 'uiFxn' has already been selected, * we know it is important, so choose it. Otherwise, * and in the case where more than one of the tied * functions has already been selected, choose based * on the diagonal value. */ if ((WsrFxn[uiFxn].wsrfxn_fCandidate == FALSE) && (WsrFxn[uiRet].wsrfxn_fCandidate)) /* Choose 'uiFxn', it's been selected before */ uiRet = uiFxn; else if (WsDecision[uiFxn][uiFxn] > WsDecision[uiRet][uiRet]) uiRet = uiFxn; } } WsDecision[uiIndex][uiRet] = WsDecision[uiRet][uiIndex] = WSDECISION_TAKEN; return(uiRet); } #else // SLOWMO /* * ***LP wsRedChooseEdgeOpt * * * Effects: * * "Selects" a function from the candidate pool, based on weighted * edge from 'index' function to a candidate function. Allocates a sorted * index (highest to lowest) to each function's edges on demand. Uses the * current highest value (with a few checks) as the selection. This * optimized algorithm produces identical results for the important high * usage high overlap functions, but diverges in the results for low usage * (2 or 1 hits) low overlap functions. Differences are not significant * from a performance perspective - a better algorithm would give marginally * better results. * * * * Returns: * * Ordinal number of selected function. * */ UINT wsRedChooseEdgeOpt(UINT uiIndex) { UINT uiRet; wsrfxn_t * pWsrFxn = &WsrFxn[uiIndex]; // Allocate and sort edges list if it doesn't already exist for this function if (wsRedChooseEdgeOptAlloc( uiIndex )) { wsRedExit( ERROR, PRINT_MSG, MSG_NO_MEM, (cFxnsTot - 1) * sizeof(*pWsrFxn->pnEdges), "WsrFxn[].pnEdges" ); } // Check remaining edges uiRet = wsRedChooseEdgeOptNextEdge( uiIndex, FALSE ); if (uiRet == cFxnsTot) // What should we do here? The algorithm we're copying falls through // and arbitrarily returns 0. It seems we should pick the most overlapped // non-Candidate, or the heaviest Candidate and restart from there. { WsDecision_t iMaxWt; static UINT nFxnOrdStart = 0; // Remember last value to restart there static UINT nFxnTotStart = 0; // Remember last value to restart there UINT nSelIndex; UINT nFxn; // Search for most overlapped non-Candidate that's not uiIndex ('uiIndex' should be empty by now) iMaxWt = WSDECISION_TAKEN; for (nFxn = nFxnOrdStart; nFxn < cFxnOrder; ++nFxn) { UINT nLocalIndex = FxnOrder[nFxn]; UINT nRetCheck; if (!WsrFxn[nLocalIndex].nEdgesLeft) { if (nFxnOrdStart == nFxn) // Haven't found available edge yet? ++nFxnOrdStart; // All non-Candidates already have been allocated, so they can be skipped next time continue; } // Get the first available value remaining nRetCheck = wsRedChooseEdgeOptNextEdge( nLocalIndex, TRUE ); if (nRetCheck != cFxnsTot && nRetCheck != uiIndex) { // See if this one's heavier if (WsDecision[nLocalIndex][nRetCheck] > iMaxWt || iMaxWt == WSDECISION_TAKEN) { nSelIndex = nLocalIndex; iMaxWt = WsDecision[nSelIndex][nRetCheck]; uiRet = nRetCheck; } else if (WsDecision[nLocalIndex][nRetCheck] == iMaxWt // On tie, use heaviest function && WsDecision[nRetCheck][nRetCheck] > WsDecision[uiRet][uiRet]) // Assume uiRet != cFxnsTot by now { nSelIndex = nLocalIndex; uiRet = nRetCheck; } } } if (uiRet != cFxnsTot) // Found an overlapped non-Candidate? { WsDecision[nSelIndex][uiRet] = WsDecision[uiRet][nSelIndex] = WSDECISION_TAKEN; return uiRet; } else // Didn't find an overlapped non-Candidate? { // Search for heaviest Candidate - assume at least two are left: see wsRedReorder() iMaxWt = WSDECISION_TAKEN; for (nFxn = nFxnTotStart; nFxn < cFxnsTot; ++nFxn) { if (!WsrFxn[nFxn].wsrfxn_fCandidate) { if (nFxnTotStart == nFxn) // Haven't found unused value yet? ++nFxnTotStart; // If it's not a candidate now, it won't be again either continue; } if (nFxn == uiIndex) continue; if (WsDecision[nFxn][nFxn] > iMaxWt || iMaxWt == WSDECISION_TAKEN) { iMaxWt = WsDecision[nFxn][nFxn]; uiRet = nFxn; } } } } WsDecision[uiIndex][uiRet] = WsDecision[uiRet][uiIndex] = WSDECISION_TAKEN; return uiRet; } // Comparison function for qsort - uses external nFxnToSort for static index INT __cdecl wsRedChooseEdgeOptCmp ( const UINT *pn1, const UINT *pn2 ) { WsDecision_t Val1 = WsDecision[nFxnToSort][*pn1], Val2 = WsDecision[nFxnToSort][*pn2]; return Val1 > Val2 ? -1 // higher values preferred : Val1 < Val2 ? 1 // If the same, prefer the highest valued diagonal : (Val1 = WsDecision[*pn1][*pn1]) > (Val2 = WsDecision[*pn2][*pn2]) ? -1 : Val1 < Val2 ? 1 // Prefer prior function if no other differences : *pn1 < *pn2 ? -1 : 1; } // Allocate and sort edges list for a function if not already allocated // Return TRUE on failure to allocate, FALSE if successful (even if list is empty) // Creates sorted index list from all non-zero unused WsDecision entries for this row // except for the diagonal. Sorts from greatest to lowest: see wsRedChooseEdgeOptCmp(). // If no such entries exist, marks the function edges as allocated, but with none // left to scan; doesn't actually allocate any memory. BOOL wsRedChooseEdgeOptAlloc( UINT uiIndex ) { wsrfxn_t * pWsrFxn = &WsrFxn[uiIndex]; if (pWsrFxn->nEdgesAlloc == 0 && pWsrFxn->pnEdges == NULL) { UINT nEdgeTot, nFxn; // Allocate maximum size initially pWsrFxn->pnEdges = malloc( (cFxnsTot - 1) * sizeof(*pWsrFxn->pnEdges) ); if (pWsrFxn->pnEdges == NULL) // No more memory? return TRUE; // Fill in array for (nEdgeTot = nFxn = 0; nFxn < cFxnsTot; ++nFxn) { if (nFxn == uiIndex) // Skip diagonal continue; if (WsDecision[uiIndex][nFxn] > 0 // Edge still available? No point in considering 0 && WsDecision[uiIndex][nFxn] != WSDECISION_TAKEN) pWsrFxn->pnEdges[nEdgeTot++] = nFxn; } if (nEdgeTot > 0) // Edges available? { if (nEdgeTot != (cFxnsTot - 1)) // Extra space allocated? { // Make it smaller UINT *pNewAlloc = realloc( pWsrFxn->pnEdges, nEdgeTot * sizeof(*pWsrFxn->pnEdges) ); if (pNewAlloc != NULL) pWsrFxn->pnEdges = pNewAlloc; } // Fill in remaining structure members pWsrFxn->nEdgesAlloc = pWsrFxn->nEdgesLeft = nEdgeTot; // Sort highest to lowest nFxnToSort = uiIndex; // Set static for sort function qsort( pWsrFxn->pnEdges, nEdgeTot, sizeof(*pWsrFxn->pnEdges), (int (__cdecl *)(const void *, const void *))wsRedChooseEdgeOptCmp ); } else // pWsrFxn->nEdgesAlloc == NULL { // Set structure members to indicate nothing left pWsrFxn->nEdgesAlloc = 1; // non-zero indicates some allocation happened pWsrFxn->nEdgesLeft = 0; free( pWsrFxn->pnEdges ); // Eliminate allocation - nothing left to check pWsrFxn->pnEdges = NULL; } } return FALSE; } // Get next edge for given function; highest overlap of most-used function // Returns "cFxnsTot" if no edge exists; otherwise the function index of next edge // Side-effect: optimizes search for next pass; frees edge index if no longer needed // Since choosing an edge marks WsDecision entries as used (WSDECISION_TAKEN), we // must step over any of these entries. Once these entries and the first unused entry // have been selected, we don't need to consider them anymore. However, if // 'bNoSelectOpt' is TRUE, only optimize leading skipped entries (not the selected // entry, since it may not be taken). UINT wsRedChooseEdgeOptNextEdge( UINT uiIndex, BOOL bNoSelectOpt ) { wsrfxn_t * pWsrFxn = &WsrFxn[uiIndex]; UINT uiRet = cFxnsTot; if (pWsrFxn->nEdgesLeft > 0) { UINT nMaxIx, nNextIx = pWsrFxn->nEdgesAlloc - pWsrFxn->nEdgesLeft; WsDecision_t iMax, iNext; UINT nRetCheck; // Get the first available value remaining while ((iMax = WsDecision[uiIndex][nRetCheck = pWsrFxn->pnEdges[nMaxIx = nNextIx++]]) == WSDECISION_TAKEN && nNextIx < pWsrFxn->nEdgesAlloc); // Check next available value for equivalence if (iMax != WSDECISION_TAKEN) { UINT nMaxIxNext = nMaxIx; // Save index of next used entry uiRet = nRetCheck; for (; nNextIx < pWsrFxn->nEdgesAlloc; ++nNextIx) { nRetCheck = pWsrFxn->pnEdges[nNextIx]; iNext = WsDecision[uiIndex][nRetCheck]; if (iNext != WSDECISION_TAKEN) { if (iNext != iMax // only need to check for equality since already sorted || !WsrFxn[uiRet].wsrfxn_fCandidate) // Already selected - choose this one break; else { /* Need tiebreak. If 'nRetCheck' has already been selected, * we know it is important, so choose it. Otherwise, * and in the case where more than one of the tied * functions have already been selected, choose based * on the diagonal value (i.e. keep previous choice since * sort already accounts for diagonal if equal vertices). */ if (!WsrFxn[nRetCheck].wsrfxn_fCandidate) // Choose 'nRetCheck' - it's been selected before { uiRet = nRetCheck; nMaxIxNext = nMaxIx - 1; // First used entry will be checked again; don't skip it } } } else if (nMaxIxNext == (nNextIx - 1)) // Skip unavailable values after first used entry only ++nMaxIxNext; } if (!bNoSelectOpt && nMaxIxNext != nMaxIx) nMaxIx = nMaxIxNext; // Skip over first used entry and unused entries after it } else if (bNoSelectOpt) // This is the last one, so step over it anyway ++nMaxIx; // Adjust the optimization indexes pWsrFxn->nEdgesLeft = pWsrFxn->nEdgesAlloc - nMaxIx - (bNoSelectOpt ? 0 : 1); if (pWsrFxn->nEdgesLeft == 0) { free( pWsrFxn->pnEdges ); // Eliminate allocation - nothing left to check pWsrFxn->pnEdges = NULL; } #ifdef YOUVE_REALLY_GOT_MEMORY_PROBLEMS else if (pWsrFxn->nEdgesLeft < pWsrFxn->nEdgesAlloc / 2 // Periodically get rid of some unused memory && pWsrFxn->nEdgesLeft > 50) { // Move edges to lower part of allocation and reallocate UINT * pNewAlloc; nNextIx = pWsrFxn->nEdgesAlloc - pWsrFxn->nEdgesLeft; MoveMemory( pWsrFxn->pnEdges, &pWsrFxn->pnEdges[nNextIx], pWsrFxn->nEdgesLeft * sizeof(*pWsrFxn->pnEdges) ); pNewAlloc = realloc( pWsrFxn->pnEdges, pWsrFxn->nEdgesLeft * sizeof(*pWsrFxn->pnEdges) ); if (pNewAlloc != NULL) pWsrFxn->pnEdges = pNewAlloc; pWsrFxn->nEdgesAlloc = pWsrFxn->nEdgesLeft; } #endif // YOUVE_REALLY_GOT_MEMORY_PROBLEMS } return uiRet; } #endif // SLOWMO /* * ***LP wsRedOutput * * * Effects: * * Prints the reordered list of functions, and writes each module's * ordered list of function ordinals to the module's associated WSR file. * If only one module is being processed, then we also write the ordered * list of function names to a WLK file. * * Returns: * * Void. If an error is encountered, exits through wsRedExit() * with ERROR. */ VOID wsRedOutput() { UINT uiFxn; UINT uiFxnOrd; wsrfxn_t *pWsrFxn; // fxn names for linker reordering // Open one WSR file per module. If only one module is reduced, // then also open a WLK file. Handle to WLK file is set in // wsRedOpenWSR(). wsRedOpenWSR(&hFileWLK); WsIndicator( WSINDF_NEW, "Saving Results", cTmiFxns ); for (uiFxn = 0; uiFxn < cFxnsTot; uiFxn++) { WsIndicator( WSINDF_PROGRESS, NULL, uiFxn ); pWsrFxn = &(WsrFxn[uiFxnOrd = FxnOrder[uiFxn]]); /* Print the function information. */ #ifdef DEBUG if (fVerbose) #ifndef TMIFILEHACK if (fFxnSizePresent == FALSE) printf(" %s: %s\n", pWsrMod->wsrmod_un.wsrmod_pchModName, pWsrFxn->wsrfxn_pchFxnName); else #endif /* !TMIFILEHACK */ printf(" (0x%08lx bytes) %s: %s\n", pWsrFxn->wsrfxn_cbFxn, pWsrMod->wsrmod_un.wsrmod_pchModName, pWsrFxn->wsrfxn_pchFxnName); #endif // DEBUG /* Write the function's ordinal number to its * module's associated WSR output file. */ fprintf(pWsrMod->wsrmod_hFileWSR, "%ld\n", uiFxnOrd); /* Write the function name to the WLK file, for linker use. */ if (hFileWLK != NULL && strcmp("???", pWsrFxn->wsrfxn_pchFxnName) && strcmp("_penter", pWsrFxn->wsrfxn_pchFxnName)) fprintf(hFileWLK, "%s\n", pWsrFxn->wsrfxn_pchFxnName); } for (uiFxn = cFxnsTot; uiFxn < cTmiFxns; uiFxn++) { WsIndicator( WSINDF_PROGRESS, NULL, uiFxn ); pWsrFxn = &(WsrFxn[FxnOrder[0]]); /* Write the function's ordinal number to its * module's associated WSR output file. */ fprintf(pWsrMod->wsrmod_hFileWSR, "%ld\n", uiFxn); } /* Close the WSR files. */ fclose(pWsrMod->wsrmod_hFileWSR); pWsrMod->wsrmod_hFileWSR = NULL; WsIndicator( WSINDF_FINISH, NULL, 0 ); } /* * ***LP wsRedExit * *** * Requires: * * *** * * Effects: * * Frees up resources (as necessary). Exits with the specified * exit code, or returns void if exit code is NOEXIT. * *** * Returns: * * Void, else exits. */ VOID wsRedExit(UINT uiExitCode, USHORT fPrintMsg, UINT uiMsgCode, ULONG ulParam1, PSZ pszParam2) { /* Print message, if necessary. */ if (fPrintMsg) { printf(pchMsg[uiMsgCode], szProgName, pszVersion, ulParam1, pszParam2); } // Special case: do NOT exit if called with NOEXIT. if (uiExitCode == NOEXIT) return; wsRedCleanup(); // mdg 98/4 exit(uiExitCode); } VOID wsRedCleanup(VOID) { UINT x; free( pWsrMod->wsrmod_un.wsrmod_pchModName ); pWsrMod->wsrmod_un.wsrmod_pchModName = NULL; for (x = 0; x < cFxnsTot; x++) { free( WsrFxn[x].wsrfxn_pchFxnName ); WsrFxn[x].wsrfxn_pchFxnName = NULL; #ifndef SLOWMO if (WsrFxn[x].pnEdges != NULL) { free( WsrFxn[x].pnEdges ); WsrFxn[x].pnEdges = NULL; } #endif // SLOWMO } for (x=0;x < 5 ; x++ ) { UnlockAndFreeMem(hMem[x]); } /* Close the WLK file. */ if (NULL != hFileWLK) fclose(hFileWLK); }