]> git.sur5r.net Git - cc65/commitdiff
Added optimization statistics
authorcuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Sat, 29 Sep 2001 12:17:36 +0000 (12:17 +0000)
committercuz <cuz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Sat, 29 Sep 2001 12:17:36 +0000 (12:17 +0000)
git-svn-id: svn://svn.cc65.org/cc65/trunk@982 b7a2c559-68d2-44c3-8de9-860c34a00d81

src/cc65/codeopt.c

index bfd7029d76ed14dc864ae5548cf3ef96332aa0fd..e90e47454835c6740a8be2f8e767b0958897a2b0 100644 (file)
@@ -33,6 +33,7 @@
 
 
 
+#include <stdlib.h>
 #include <string.h>
 
 /* common */
 
 
 
-/*****************************************************************************/
-/*                            Helper functions                              */
-/*****************************************************************************/
-
-
-
 /*****************************************************************************/
 /*                             Optimize shifts                              */
 /*****************************************************************************/
@@ -1370,22 +1365,26 @@ static unsigned OptPtrLoad6 (CodeSeg* S)
 
 
 /*****************************************************************************/
-/*                             struct OptFunc                               */
+/*                             struct OptFunc                               */
 /*****************************************************************************/
 
 
 
 typedef struct OptFunc OptFunc;
 struct OptFunc {
-    unsigned     (*Func) (CodeSeg*);    /* Optimizer function */
-    const char*  Name;                  /* Name of the function/group */
-    char         Disabled;              /* True if function disabled */
+    unsigned       (*Func) (CodeSeg*);  /* Optimizer function */
+    const char*    Name;                /* Name of the function/group */
+    unsigned long  TotalRuns;          /* Total number of runs */
+    unsigned long  LastRuns;            /* Last number of runs */
+    unsigned long  TotalChanges;        /* Total number of changes */
+    unsigned long  LastChanges;         /* Last number of changes */
+    char           Disabled;            /* True if function disabled */
 };
 
 
 
 /*****************************************************************************/
-/*                                          Code                                    */
+/*                                          Code                                    */
 /*****************************************************************************/
 
 
@@ -1394,49 +1393,49 @@ struct OptFunc {
 #define OptFuncEntry(func) static OptFuncDesc D##func = { func, #func, 0 }
 
 /* A list of all the function descriptions */
-static OptFunc DOptPtrStore1           = { OptPtrStore1,    "OptPtrStore1",    0 };
-static OptFunc DOptPtrStore2           = { OptPtrStore2,    "OptPtrStore2",    0 };
-static OptFunc DOptPtrLoad1            = { OptPtrLoad1,     "OptPtrLoad1",     0 };
-static OptFunc DOptPtrLoad2            = { OptPtrLoad2,     "OptPtrLoad2",     0 };
-static OptFunc DOptPtrLoad3            = { OptPtrLoad3,     "OptPtrLoad3",     0 };
-static OptFunc DOptPtrLoad4            = { OptPtrLoad4,     "OptPtrLoad4",     0 };
-static OptFunc DOptPtrLoad5            = { OptPtrLoad5,     "OptPtrLoad5",     0 };
-static OptFunc DOptPtrLoad6            = { OptPtrLoad6,     "OptPtrLoad6",     0 };
-static OptFunc DOptNegA1               = { OptNegA1,        "OptNegA1",        0 };
-static OptFunc DOptNegA2               = { OptNegA2,        "OptNegA2",        0 };
-static OptFunc DOptNegAX1              = { OptNegAX1,       "OptNegAX1",       0 };
-static OptFunc DOptNegAX2              = { OptNegAX2,       "OptNegAX2",       0 };
-static OptFunc DOptNegAX3              = { OptNegAX3,       "OptNegAX3",       0 };
-static OptFunc DOptNegAX4              = { OptNegAX4,       "OptNegAX4",       0 };
-static OptFunc DOptSub1                = { OptSub1,         "OptSub1",         0 };
-static OptFunc DOptSub2                = { OptSub2,         "OptSub2",         0 };
-static OptFunc DOptAdd1                = { OptAdd1,         "OptAdd1",         0 };
-static OptFunc DOptAdd2                = { OptAdd2,         "OptAdd2",         0 };
-static OptFunc DOptAdd3                = { OptAdd3,         "OptAdd3",         0 };
-static OptFunc DOptShift1              = { OptShift1,       "OptShift1",       0 };
-static OptFunc DOptJumpCascades        = { OptJumpCascades, "OptJumpCascades", 0 };
-static OptFunc DOptDeadJumps           = { OptDeadJumps,    "OptDeadJumps",    0 };
-static OptFunc DOptRTS                 = { OptRTS,          "OptRTS",          0 };
-static OptFunc DOptDeadCode            = { OptDeadCode,     "OptDeadCode",     0 };
-static OptFunc DOptJumpTarget          = { OptJumpTarget,   "OptJumpTarget",   0 };
-static OptFunc DOptCondBranches        = { OptCondBranches, "OptCondBranches", 0 };
-static OptFunc DOptRTSJumps            = { OptRTSJumps,     "OptRTSJumps",     0 };
-static OptFunc DOptBoolTrans    = { OptBoolTrans,    "OptBoolTrans",    0 };
-static OptFunc DOptCmp1                = { OptCmp1,         "OptCmp1",         0 };
-static OptFunc DOptCmp2                = { OptCmp2,         "OptCmp2",         0 };
-static OptFunc DOptCmp3                = { OptCmp3,         "OptCmp3",         0 };
-static OptFunc DOptCmp4                = { OptCmp4,         "OptCmp4",         0 };
-static OptFunc DOptCmp5                = { OptCmp5,         "OptCmp5",         0 };
-static OptFunc DOptCmp6                = { OptCmp6,         "OptCmp6",         0 };
-static OptFunc DOptCmp7                = { OptCmp7,         "OptCmp7",         0 };
-static OptFunc DOptTest1               = { OptTest1,        "OptTest1",        0 };
-static OptFunc DOptUnusedLoads         = { OptUnusedLoads,  "OptUnusedLoads",  0 };
-static OptFunc DOptUnusedStores        = { OptUnusedStores, "OptUnusedStores", 0 };
-static OptFunc DOptDupLoads     = { OptDupLoads,     "OptDupLoads",     0 };
-static OptFunc DOptStoreLoad           = { OptStoreLoad,    "OptStoreLoad",    0 };
-static OptFunc DOptTransfers           = { OptTransfers,    "OptTransfers",    0 };
-static OptFunc DOptStackOps            = { OptStackOps,     "OptStackOps",     0 };
-static OptFunc DOptBranchDist          = { OptBranchDist,   "OptBranchDist",   0 };
+static OptFunc DOptPtrStore1           = { OptPtrStore1,    "OptPtrStore1",    0, 0, 0, 0, 0 };
+static OptFunc DOptPtrStore2           = { OptPtrStore2,    "OptPtrStore2",    0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad1            = { OptPtrLoad1,     "OptPtrLoad1",     0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad2            = { OptPtrLoad2,     "OptPtrLoad2",     0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad3            = { OptPtrLoad3,     "OptPtrLoad3",     0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad4            = { OptPtrLoad4,     "OptPtrLoad4",     0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad5            = { OptPtrLoad5,     "OptPtrLoad5",     0, 0, 0, 0, 0 };
+static OptFunc DOptPtrLoad6            = { OptPtrLoad6,     "OptPtrLoad6",     0, 0, 0, 0, 0 };
+static OptFunc DOptNegA1               = { OptNegA1,        "OptNegA1",        0, 0, 0, 0, 0 };
+static OptFunc DOptNegA2               = { OptNegA2,        "OptNegA2",        0, 0, 0, 0, 0 };
+static OptFunc DOptNegAX1              = { OptNegAX1,       "OptNegAX1",       0, 0, 0, 0, 0 };
+static OptFunc DOptNegAX2              = { OptNegAX2,       "OptNegAX2",       0, 0, 0, 0, 0 };
+static OptFunc DOptNegAX3              = { OptNegAX3,       "OptNegAX3",       0, 0, 0, 0, 0 };
+static OptFunc DOptNegAX4              = { OptNegAX4,       "OptNegAX4",       0, 0, 0, 0, 0 };
+static OptFunc DOptSub1                = { OptSub1,         "OptSub1",         0, 0, 0, 0, 0 };
+static OptFunc DOptSub2                = { OptSub2,         "OptSub2",         0, 0, 0, 0, 0 };
+static OptFunc DOptAdd1                = { OptAdd1,         "OptAdd1",         0, 0, 0, 0, 0 };
+static OptFunc DOptAdd2                = { OptAdd2,         "OptAdd2",         0, 0, 0, 0, 0 };
+static OptFunc DOptAdd3                = { OptAdd3,         "OptAdd3",         0, 0, 0, 0, 0 };
+static OptFunc DOptShift1              = { OptShift1,       "OptShift1",       0, 0, 0, 0, 0 };
+static OptFunc DOptJumpCascades        = { OptJumpCascades, "OptJumpCascades", 0, 0, 0, 0, 0 };
+static OptFunc DOptDeadJumps           = { OptDeadJumps,    "OptDeadJumps",    0, 0, 0, 0, 0 };
+static OptFunc DOptRTS                 = { OptRTS,          "OptRTS",          0, 0, 0, 0, 0 };
+static OptFunc DOptDeadCode            = { OptDeadCode,     "OptDeadCode",     0, 0, 0, 0, 0 };
+static OptFunc DOptJumpTarget          = { OptJumpTarget,   "OptJumpTarget",   0, 0, 0, 0, 0 };
+static OptFunc DOptCondBranches        = { OptCondBranches, "OptCondBranches", 0, 0, 0, 0, 0 };
+static OptFunc DOptRTSJumps            = { OptRTSJumps,     "OptRTSJumps",     0, 0, 0, 0, 0 };
+static OptFunc DOptBoolTrans    = { OptBoolTrans,    "OptBoolTrans",    0, 0, 0, 0, 0 };
+static OptFunc DOptCmp1                = { OptCmp1,         "OptCmp1",         0, 0, 0, 0, 0 };
+static OptFunc DOptCmp2                = { OptCmp2,         "OptCmp2",         0, 0, 0, 0, 0 };
+static OptFunc DOptCmp3                = { OptCmp3,         "OptCmp3",         0, 0, 0, 0, 0 };
+static OptFunc DOptCmp4                = { OptCmp4,         "OptCmp4",         0, 0, 0, 0, 0 };
+static OptFunc DOptCmp5                = { OptCmp5,         "OptCmp5",         0, 0, 0, 0, 0 };
+static OptFunc DOptCmp6                = { OptCmp6,         "OptCmp6",         0, 0, 0, 0, 0 };
+static OptFunc DOptCmp7                = { OptCmp7,         "OptCmp7",         0, 0, 0, 0, 0 };
+static OptFunc DOptTest1               = { OptTest1,        "OptTest1",        0, 0, 0, 0, 0 };
+static OptFunc DOptUnusedLoads         = { OptUnusedLoads,  "OptUnusedLoads",  0, 0, 0, 0, 0 };
+static OptFunc DOptUnusedStores        = { OptUnusedStores, "OptUnusedStores", 0, 0, 0, 0, 0 };
+static OptFunc DOptDupLoads     = { OptDupLoads,     "OptDupLoads",     0, 0, 0, 0, 0 };
+static OptFunc DOptStoreLoad           = { OptStoreLoad,    "OptStoreLoad",    0, 0, 0, 0, 0 };
+static OptFunc DOptTransfers           = { OptTransfers,    "OptTransfers",    0, 0, 0, 0, 0 };
+static OptFunc DOptStackOps            = { OptStackOps,     "OptStackOps",     0, 0, 0, 0, 0 };
+static OptFunc DOptBranchDist          = { OptBranchDist,   "OptBranchDist",   0, 0, 0, 0, 0 };
 
 /* Table containing all the steps in alphabetical order */
 static OptFunc* OptFuncs[] = {
@@ -1488,47 +1487,38 @@ static OptFunc* OptFuncs[] = {
 
 
 
-static unsigned RunOptStep (CodeSeg* S, OptFunc* O, unsigned Max)
-/* Run one optimizer function Max times or until there are no more changes */
+static int CmpOptStep (const void* Key, const void* Func)
+/* Compare function for bsearch */
 {
-    unsigned Changes, C;
+    return strcmp (Key, (*(const OptFunc**)Func)->Name);
+}
 
-    /* Don't run the function if it is disabled */
-    if (O->Disabled) {
-       return 0;
-    }
 
-    /* Run this until there are no more changes */
-    Changes = 0;
-    do {
-       C = O->Func (S);
-       Changes += C;
-    } while (--Max && C > 0);
 
-    /* Return the number of changes */
-    return Changes;
+static OptFunc* FindOptFunc (const char* Name)
+/* Find an optimizer step by name in the table and return a pointer. Return
+ * NULL if no such step is found.
+ */
+{
+    /* Search for the function in the list */
+    OptFunc** O = bsearch (Name, OptFuncs, OPTFUNC_COUNT, sizeof (OptFuncs[0]), CmpOptStep);
+    return O? *O : 0;
 }
 
 
 
-static OptFunc* FindOptStep (const char* Name)
+static OptFunc* GetOptFunc (const char* Name)
 /* Find an optimizer step by name in the table and return a pointer. Print an
  * error and call AbEnd if not found.
  */
 {
-    unsigned I;
-
-    /* Run all optimization steps */
-    for (I = 0; I < OPTFUNC_COUNT; ++I) {
-               if (strcmp (OptFuncs[I]->Name, Name) == 0) {
-           /* Found */
-           return OptFuncs[I];
-       }
+    /* Search for the function in the list */
+    OptFunc* F = FindOptFunc (Name);
+    if (F == 0) {
+       /* Not found */
+       AbEnd ("Optimization step `%s' not found", Name);
     }
-
-    /* Not found */
-    AbEnd ("Optimization step `%s' not found", Name);
-    return 0;
+    return F;
 }
 
 
@@ -1542,8 +1532,7 @@ void DisableOpt (const char* Name)
                    OptFuncs[I]->Disabled = 1;
        }
     } else {
-       OptFunc* F = FindOptStep (Name);
-       F->Disabled = 1;
+       GetOptFunc(Name)->Disabled = 1;
     }
 }
 
@@ -1558,8 +1547,7 @@ void EnableOpt (const char* Name)
                    OptFuncs[I]->Disabled = 0;
        }
     } else {
-       OptFunc* F = FindOptStep (Name);
-       F->Disabled = 0;
+       GetOptFunc(Name)->Disabled = 0;
     }
 }
 
@@ -1576,6 +1564,140 @@ void ListOptSteps (FILE* F)
 
 
 
+static void ReadOptStats (const char* Name)
+/* Read the optimizer statistics file */
+{
+    char Buf [256];
+    unsigned Lines;
+
+    /* Try to open the file */
+    FILE* F = fopen (Name, "r");
+    if (F == 0) {
+       /* Ignore the error */
+       return;
+    }
+
+    /* Read and parse the lines */
+    Lines = 0;
+    while (fgets (Buf, sizeof (Buf), F) != 0) {
+
+       char* B;
+       unsigned Len;
+       OptFunc* Func;
+
+       /* Fields */
+       char Name[32];
+               unsigned long  TotalRuns;
+       unsigned long  TotalChanges;
+
+       /* Count lines */
+       ++Lines;
+
+       /* Remove trailing white space including the line terminator */
+       B = Buf;
+       Len = strlen (B);
+       while (Len > 0 && IsSpace (B[Len-1])) {
+           --Len;
+       }
+       B[Len] = '\0';
+
+       /* Remove leading whitespace */
+       while (IsSpace (*B)) {
+           ++B;
+       }
+
+       /* Check for empty and comment lines */
+       if (*B == '\0' || *B == ';' || *B == '#') {
+           continue;
+       }
+
+       /* Parse the line */
+               if (sscanf (B, "%31s %lu %*u %lu %*u", Name, &TotalRuns, &TotalChanges) != 3) {
+           /* Syntax error */
+           continue;
+       }
+
+       /* Search for the optimizer step. */
+       Func = FindOptFunc (Name);
+       if (Func == 0) {
+           /* Not found */
+           continue;
+       }
+
+       /* Found the step, set the fields */
+       Func->TotalRuns    = TotalRuns;
+       Func->TotalChanges = TotalChanges;
+
+    }
+
+    /* Close the file, ignore errors here. */
+    fclose (F);
+}
+
+
+
+static void WriteOptStats (const char* Name)
+/* Write the optimizer statistics file */
+{
+    unsigned I;
+
+    /* Try to open the file */
+    FILE* F = fopen (Name, "w");
+    if (F == 0) {
+       /* Ignore the error */
+       return;
+    }
+
+    /* Write the file */
+    for (I = 0; I < OPTFUNC_COUNT; ++I) {
+       const OptFunc* O = OptFuncs[I];
+       fprintf (F,
+                "%s %lu %lu %lu %lu\n",
+                O->Name,
+                O->TotalRuns,
+                O->LastRuns,
+                O->TotalChanges,
+                O->LastChanges);
+    }
+
+    /* Close the file, ignore errors here. */
+    fclose (F);
+}
+
+
+
+static unsigned RunOptFunc (CodeSeg* S, OptFunc* F, unsigned Max)
+/* Run one optimizer function Max times or until there are no more changes */
+{
+    unsigned Changes, C;
+
+    /* Don't run the function if it is disabled */
+    if (F->Disabled) {
+       return 0;
+    }
+
+    /* Run this until there are no more changes */
+    Changes = 0;
+    do {
+       
+       /* Run the function */
+       C = F->Func (S);
+       Changes += C;
+
+       /* Do statistics */
+       ++F->TotalRuns;
+       ++F->LastRuns;
+       F->TotalChanges += C;
+       F->LastChanges  += C;
+
+    } while (--Max && C > 0);
+
+    /* Return the number of changes */
+    return Changes;
+}
+
+
+
 static void RunOptGroup1 (CodeSeg* S)
 /* Run the first group of optimization steps. These steps translate known
  * patterns emitted by the code generator into more optimal patterns. Order
@@ -1583,20 +1705,20 @@ static void RunOptGroup1 (CodeSeg* S)
  * the same patterns as later steps as subpatterns.
  */
 {
-    RunOptStep (S, &DOptPtrStore1, 1);
-    RunOptStep (S, &DOptPtrStore2, 1);
-    RunOptStep (S, &DOptPtrLoad1, 1);
-    RunOptStep (S, &DOptPtrLoad2, 1);
-    RunOptStep (S, &DOptPtrLoad3, 1);
-    RunOptStep (S, &DOptPtrLoad4, 1);
-    RunOptStep (S, &DOptPtrLoad5, 1);
-    RunOptStep (S, &DOptNegAX1, 1);
-    RunOptStep (S, &DOptNegAX2, 1);
-    RunOptStep (S, &DOptNegAX3, 1);
-    RunOptStep (S, &DOptNegAX4, 1);
-    RunOptStep (S, &DOptAdd1, 1);
-    RunOptStep (S, &DOptAdd2, 1);
-    RunOptStep (S, &DOptShift1, 1);
+    RunOptFunc (S, &DOptPtrStore1, 1);
+    RunOptFunc (S, &DOptPtrStore2, 1);
+    RunOptFunc (S, &DOptPtrLoad1, 1);
+    RunOptFunc (S, &DOptPtrLoad2, 1);
+    RunOptFunc (S, &DOptPtrLoad3, 1);
+    RunOptFunc (S, &DOptPtrLoad4, 1);
+    RunOptFunc (S, &DOptPtrLoad5, 1);
+    RunOptFunc (S, &DOptNegAX1, 1);
+    RunOptFunc (S, &DOptNegAX2, 1);
+    RunOptFunc (S, &DOptNegAX3, 1);
+    RunOptFunc (S, &DOptNegAX4, 1);
+    RunOptFunc (S, &DOptAdd1, 1);
+    RunOptFunc (S, &DOptAdd2, 1);
+    RunOptFunc (S, &DOptShift1, 1);
 }
 
 
@@ -1612,34 +1734,34 @@ static void RunOptGroup2 (CodeSeg* S)
     do {
        Changes = 0;
 
-       Changes += RunOptStep (S, &DOptPtrLoad6, 1);
-       Changes += RunOptStep (S, &DOptNegA1, 1);
-       Changes += RunOptStep (S, &DOptNegA2, 1);
-       Changes += RunOptStep (S, &DOptSub1, 1);
-       Changes += RunOptStep (S, &DOptSub2, 1);
-       Changes += RunOptStep (S, &DOptAdd3, 1);
-       Changes += RunOptStep (S, &DOptJumpCascades, 1);
-       Changes += RunOptStep (S, &DOptDeadJumps, 1);
-       Changes += RunOptStep (S, &DOptRTS, 1);
-       Changes += RunOptStep (S, &DOptDeadCode, 1);
-       Changes += RunOptStep (S, &DOptJumpTarget, 1);
-       Changes += RunOptStep (S, &DOptCondBranches, 1);
-       Changes += RunOptStep (S, &DOptRTSJumps, 1);
-       Changes += RunOptStep (S, &DOptBoolTrans, 1);
-       Changes += RunOptStep (S, &DOptCmp1, 1);
-       Changes += RunOptStep (S, &DOptCmp2, 1);
-       Changes += RunOptStep (S, &DOptCmp3, 1);
-       Changes += RunOptStep (S, &DOptCmp4, 1);
-       Changes += RunOptStep (S, &DOptCmp5, 1);
-       Changes += RunOptStep (S, &DOptCmp6, 1);
-       Changes += RunOptStep (S, &DOptCmp7, 1);
-       Changes += RunOptStep (S, &DOptTest1, 1);
-       Changes += RunOptStep (S, &DOptUnusedLoads, 1);
-       Changes += RunOptStep (S, &DOptUnusedStores, 1);
-       Changes += RunOptStep (S, &DOptDupLoads, 1);
-       Changes += RunOptStep (S, &DOptStoreLoad, 1);
-       Changes += RunOptStep (S, &DOptTransfers, 1);
-       Changes += RunOptStep (S, &DOptStackOps, 1);
+       Changes += RunOptFunc (S, &DOptPtrLoad6, 1);
+       Changes += RunOptFunc (S, &DOptNegA1, 1);
+       Changes += RunOptFunc (S, &DOptNegA2, 1);
+       Changes += RunOptFunc (S, &DOptSub1, 1);
+       Changes += RunOptFunc (S, &DOptSub2, 1);
+       Changes += RunOptFunc (S, &DOptAdd3, 1);
+       Changes += RunOptFunc (S, &DOptJumpCascades, 1);
+       Changes += RunOptFunc (S, &DOptDeadJumps, 1);
+       Changes += RunOptFunc (S, &DOptRTS, 1);
+       Changes += RunOptFunc (S, &DOptDeadCode, 1);
+       Changes += RunOptFunc (S, &DOptJumpTarget, 1);
+       Changes += RunOptFunc (S, &DOptCondBranches, 1);
+       Changes += RunOptFunc (S, &DOptRTSJumps, 1);
+       Changes += RunOptFunc (S, &DOptBoolTrans, 1);
+       Changes += RunOptFunc (S, &DOptCmp1, 1);
+       Changes += RunOptFunc (S, &DOptCmp2, 1);
+       Changes += RunOptFunc (S, &DOptCmp3, 1);
+       Changes += RunOptFunc (S, &DOptCmp4, 1);
+       Changes += RunOptFunc (S, &DOptCmp5, 1);
+       Changes += RunOptFunc (S, &DOptCmp6, 1);
+       Changes += RunOptFunc (S, &DOptCmp7, 1);
+       Changes += RunOptFunc (S, &DOptTest1, 1);
+       Changes += RunOptFunc (S, &DOptUnusedLoads, 1);
+       Changes += RunOptFunc (S, &DOptUnusedStores, 1);
+       Changes += RunOptFunc (S, &DOptDupLoads, 1);
+       Changes += RunOptFunc (S, &DOptStoreLoad, 1);
+       Changes += RunOptFunc (S, &DOptTransfers, 1);
+       Changes += RunOptFunc (S, &DOptStackOps, 1);
 
     } while (Changes);
 }
@@ -1650,7 +1772,7 @@ static void RunOptGroup3 (CodeSeg* S)
 /* The last group of optimization steps. Adjust branches.
  */
 {
-    RunOptStep (S, &DOptBranchDist, 3);
+    RunOptFunc (S, &DOptBranchDist, 3);
 }
 
 
@@ -1658,12 +1780,19 @@ static void RunOptGroup3 (CodeSeg* S)
 void RunOpt (CodeSeg* S)
 /* Run the optimizer */
 {
+    const char* StatFileName;
 
     /* If we shouldn't run the optimizer, bail out */
     if (!Optimize) {
        return;
     }
 
+    /* Check if we are requested to write optimizer statistics */
+    StatFileName = getenv ("CC65_OPTSTATS");
+    if (StatFileName) {
+       ReadOptStats (StatFileName);
+    }
+
     /* Print the name of the function we are working on */
     if (S->Func) {
        Print (stdout, 1, "Running optimizer for function `%s'\n", S->Func->Name);
@@ -1675,6 +1804,11 @@ void RunOpt (CodeSeg* S)
     RunOptGroup1 (S);
     RunOptGroup2 (S);
     RunOptGroup3 (S);
+
+    /* Write statistics */
+    if (StatFileName) {
+       WriteOptStats (StatFileName);
+    }
 }