Index: /trunk/src/kmk/expand.c
===================================================================
--- /trunk/src/kmk/expand.c	(revision 2770)
+++ /trunk/src/kmk/expand.c	(revision 2771)
@@ -190,5 +190,17 @@
 #else  /* CONFIG_WITH_VALUE_LENGTH */
   if (!v->append)
-    value = allocated_variable_expand_2 (v->value, v->value_length, value_lenp);
+    {
+      if (!IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR (v))
+        value = allocated_variable_expand_2 (v->value, v->value_length, value_lenp);
+      else
+        {
+          unsigned int len = v->value_length;
+          value = xmalloc (len + 2);
+          memcpy (value, v->value, len + 1);
+          value[len + 1] = '\0'; /* Extra terminator like allocated_variable_expand_2 returns. Why? */
+          if (value_lenp)
+            *value_lenp = len;
+        }
+    }
   else
     {
@@ -312,5 +324,5 @@
 #ifdef CONFIG_WITH_VALUE_LENGTH
   assert (v->value_length == strlen (v->value));
-  if (!v->recursive)
+  if (!v->recursive || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR (v))
     o = variable_buffer_output (o, v->value, v->value_length);
   else
@@ -1010,5 +1022,5 @@
 
   /* Either expand it or copy it, depending.  */
-  if (! v->recursive)
+  if (! v->recursive || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR (v))
 #ifdef CONFIG_WITH_VALUE_LENGTH
     return variable_buffer_output (buf, v->value, v->value_length);
Index: /trunk/src/kmk/function.c
===================================================================
--- /trunk/src/kmk/function.c	(revision 2770)
+++ /trunk/src/kmk/function.c	(revision 2771)
@@ -2168,8 +2168,8 @@
           /* Compile the variable for evalval, evalctx and expansion. */
 
-          if (!v->evalprog)
-            kmk_cc_compile_variable_for_eval (v);
-          if (!v->expandprog)
-            kmk_cc_compile_variable_for_expand (v);
+          if (   v->recursive
+              && !IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR (v))
+                kmk_cc_compile_variable_for_expand (v);
+          kmk_cc_compile_variable_for_eval (v);
 # endif
         }
@@ -4122,5 +4122,6 @@
   if (var1->value == var2->value)
     return variable_buffer_output (o, "", 0);       /* eq */
-  if (!var1->recursive && !var2->recursive)
+  if (   (!var1->recursive || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR (var1))
+      && (!var2->recursive || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR (var2)) )
   {
     if (    var1->value_length == var2->value_length
@@ -6005,5 +6006,5 @@
       if (v && v->value_length)
         {
-          if (v->recursive)
+          if (v->recursive && !IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR (v))
             {
               v->exp_count = EXP_COUNT_MAX;
Index: /trunk/src/kmk/kbuild.c
===================================================================
--- /trunk/src/kmk/kbuild.c	(revision 2770)
+++ /trunk/src/kmk/kbuild.c	(revision 2771)
@@ -1575,6 +1575,6 @@
         { \
             paVars[iVar].pVar = pVar; \
-            if (    !pVar->recursive \
-                ||  !memchr(pVar->value, '$', pVar->value_length)) \
+            if (   !pVar->recursive \
+                || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar)) \
             { \
                 paVars[iVar].pszExp = pVar->value; \
Index: /trunk/src/kmk/kmk_cc_exec.c
===================================================================
--- /trunk/src/kmk/kmk_cc_exec.c	(revision 2770)
+++ /trunk/src/kmk/kmk_cc_exec.c	(revision 2771)
@@ -42,4 +42,35 @@
 #include <stdarg.h>
 #include <assert.h>
+
+/*******************************************************************************
+*   Defined Constants And Macros                                               *
+*******************************************************************************/
+/** @def KMK_CC_WITH_STATS
+ * Enables the collection of extra statistics. */
+#ifndef KMK_CC_WITH_STATS
+# ifdef CONFIG_WITH_MAKE_STATS
+#  define KMK_CC_WITH_STATS
+# endif
+#endif
+
+/** @def KMK_CC_STRICT
+ * Indicates whether assertions and other checks are enabled. */
+#ifndef KMK_CC_STRICT
+# ifndef NDEBUG
+#  define KMK_CC_STRICT
+# endif
+#endif
+
+#ifdef KMK_CC_STRICT
+# ifdef _MSC_VER
+#  define KMK_CC_ASSERT(a_TrueExpr)         do { if (!(a_TrueExpr)) __debugbreak(); } while (0)
+# else
+#  define KMK_CC_ASSERT(a_TrueExpr)         assert(a_TrueExpr)
+# endif
+#else
+# define KMK_CC_ASSERT(a_TrueExpr)          do {} while (0)
+#endif
+#define KMK_CC_ASSERT_ALIGNED(a_uValue, a_uAlignment) \
+    KMK_CC_ASSERT( ((a_uValue) & ((a_uAlignment) - 1)) == 0 )
 
 
@@ -264,5 +295,5 @@
 /** Calculates the size of an KMKCCEXPPLAINFUNC with a_cArgs. */
 #define KMKCCEXPDYNFUNC_SIZE(a_cArgs)  (  sizeof(KMKCCEXPFUNCCORE) \
-                                          + (a_cArgs) * sizeof(((PKMKCCEXPDYNFUNC)(uintptr_t)42)->aArgs[0]) )
+                                        + (a_cArgs) * sizeof(((PKMKCCEXPDYNFUNC)(uintptr_t)42)->aArgs[0]) )
 
 /**
@@ -289,4 +320,9 @@
     /** Statistics. */
     KMKCCEXPSTATS           Stats;
+#ifdef KMK_CC_STRICT
+    /** The hash of the input string.  Used to check that we get all the change
+     * notifications we require. */
+    uint32_t                uInputHash;
+#endif
 } KMKCCEXPPROG;
 /** Pointer to a string expansion program. */
@@ -295,22 +331,18 @@
 
 /*******************************************************************************
-*   Defined Constants And Macros                                               *
-*******************************************************************************/
-#ifndef NDEBUG
-# ifdef _MSC_VER
-#  define KMK_CC_ASSERT(a_TrueExpr)         do { if (!(a_TrueExpr)) __debugbreak(); } while (0)
-# else
-#  define KMK_CC_ASSERT(a_TrueExpr)         assert(a_TrueExpr)
-# endif
-#else
-# define KMK_CC_ASSERT(a_TrueExpr)          do {} while (0)
-#endif
-#define KMK_CC_ASSERT_ALIGNED(a_uValue, a_uAlignment) \
-    KMK_CC_ASSERT( ((a_uValue) & ((a_uAlignment) - 1)) == 0 )
-
-
-/*******************************************************************************
 *   Global Variables                                                           *
 *******************************************************************************/
+static uint32_t g_cVarForExpandCompilations = 0;
+static uint32_t g_cVarForExpandExecs = 0;
+#ifdef KMK_CC_WITH_STATS
+static uint32_t g_cBlockAllocated = 0;
+static uint32_t g_cbAllocated = 0;
+static uint32_t g_cBlocksAllocatedExpProgs = 0;
+static uint32_t g_cbAllocatedExpProgs = 0;
+static uint32_t g_cSingleBlockExpProgs = 0;
+static uint32_t g_cTwoBlockExpProgs = 0;
+static uint32_t g_cMultiBlockExpProgs = 0;
+static uint32_t g_cbUnusedMemExpProgs = 0;
+#endif
 
 
@@ -331,4 +363,115 @@
 
 /**
+ * Prints stats (for kmk -p).
+ */
+void kmk_cc_print_stats(void)
+{
+    puts(_("\n# The kmk 'compiler' and kmk 'program executor':\n"));
+
+    printf(_("# Variables compiled for string expansion: %6u\n"), g_cVarForExpandCompilations);
+    printf(_("# Variables string expansion runs:         %6u\n"), g_cVarForExpandExecs);
+    printf(_("# String expansion runs per compile:       %6u\n"), g_cVarForExpandExecs / g_cVarForExpandExecs);
+#ifdef KMK_CC_WITH_STATS
+    printf(_("#          Single alloc block exp progs:   %6u (%u%%)\n"
+             "#             Two alloc block exp progs:   %6u (%u%%)\n"
+             "#   Three or more alloc block exp progs:   %6u (%u%%)\n"
+             ),
+           g_cSingleBlockExpProgs, (uint32_t)((uint64_t)g_cSingleBlockExpProgs * 100 / g_cVarForExpandCompilations),
+           g_cTwoBlockExpProgs,    (uint32_t)((uint64_t)g_cTwoBlockExpProgs    * 100 / g_cVarForExpandCompilations),
+           g_cMultiBlockExpProgs,  (uint32_t)((uint64_t)g_cMultiBlockExpProgs  * 100 / g_cVarForExpandCompilations));
+    printf(_("#  Total amount of memory for exp progs: %8u bytes\n"
+             "#                                    in:   %6u blocks\n"
+             "#                        avg block size:   %6u bytes\n"
+             "#                         unused memory: %8u bytes (%u%%)\n"
+             "#           avg unused memory per block:   %6u bytes\n"
+             "\n"),
+           g_cbAllocatedExpProgs, g_cBlocksAllocatedExpProgs, g_cbAllocatedExpProgs / g_cBlocksAllocatedExpProgs,
+           g_cbUnusedMemExpProgs, (uint32_t)((uint64_t)g_cbUnusedMemExpProgs * 100 / g_cbAllocatedExpProgs),
+           g_cbUnusedMemExpProgs / g_cBlocksAllocatedExpProgs);
+
+    printf(_("#   Total amount of block mem allocated: %8u bytes\n"), g_cbAllocated);
+    printf(_("#       Total number of block allocated: %8u\n"), g_cBlockAllocated);
+    printf(_("#                    Average block size: %8u byte\n"), g_cbAllocated / g_cBlockAllocated);
+#endif
+
+    puts("");
+}
+
+
+/*
+ *
+ * Various utility functions.
+ * Various utility functions.
+ * Various utility functions.
+ *
+ */
+
+/**
+ * Counts the number of dollar chars in the string.
+ *
+ * @returns Number of dollar chars.
+ * @param   pchStr      The string to search (does not need to be zero
+ *                      terminated).
+ * @param   cchStr      The length of the string.
+ */
+static uint32_t kmk_cc_count_dollars(const char *pchStr, uint32_t cchStr)
+{
+    uint32_t cDollars = 0;
+    const char *pch;
+    while ((pch = memchr(pchStr, '$', cchStr)) != NULL)
+    {
+        cDollars++;
+        cchStr -= pch - pchStr + 1;
+        pchStr  = pch + 1;
+    }
+    return cDollars;
+}
+
+#ifdef KMK_CC_STRICT
+/**
+ * Used to check that function arguments are left alone.
+ * @returns Updated hash.
+ * @param   uHash       The current hash value.
+ * @param   psz         The string to hash.
+ */
+static uint32_t kmk_cc_debug_string_hash(uint32_t uHash, const char *psz)
+{
+    unsigned char ch;
+    while ((ch = *(unsigned char const *)psz++) != '\0')
+        uHash = (uHash << 6) + (uHash << 16) - uHash + (unsigned char)ch;
+    return uHash;
+}
+
+/**
+ * Used to check that function arguments are left alone.
+ * @returns Updated hash.
+ * @param   uHash       The current hash value.
+ * @param   pch         The string to hash, not terminated.
+ * @param   cch         The number of chars to hash.
+ */
+static uint32_t kmk_cc_debug_string_hash_n(uint32_t uHash, const char *pch, uint32_t cch)
+{
+    while (cch-- > 0)
+    {
+        unsigned char ch = *(unsigned char const *)pch++;
+        uHash = (uHash << 6) + (uHash << 16) - uHash + (unsigned char)ch;
+    }
+    return uHash;
+}
+
+#endif
+
+
+
+/*
+ *
+ * The allocator.
+ * The allocator.
+ * The allocator.
+ *
+ */
+
+
+/**
  * For the first allocation using the block allocator.
  *
@@ -349,5 +492,10 @@
      */
     if (cbHint <= 512)
-        cbBlock = 512;
+    {
+        if (cbHint <= 256)
+            cbBlock = 128;
+        else
+            cbBlock = 256;
+    }
     else if (cbHint < 2048)
         cbBlock = 1024;
@@ -366,4 +514,9 @@
     *ppBlockTail = pNewBlock;
 
+#ifdef KMK_CC_WITH_STATS
+    g_cBlockAllocated++;
+    g_cbAllocated += cbBlock;
+#endif
+
     return pNewBlock + 1;
 }
@@ -439,4 +592,9 @@
     *ppBlockTail = pNewBlock;
 
+#ifdef KMK_CC_WITH_STATS
+    g_cBlockAllocated++;
+    g_cbAllocated += cbBlock;
+#endif
+
     return pNewBlock + 1;
 }
@@ -506,5 +664,5 @@
 
     /* Figure the block size. */
-    uint32_t cbBlock = pOldBlock->cbBlock;
+    uint32_t cbBlock = !pOldBlock->pNext ? 128 : pOldBlock->cbBlock;
     while (cbBlock - sizeof(KMKCCEXPJUMP) - sizeof(*pNewBlock) < cb)
         cbBlock *= 2;
@@ -517,4 +675,9 @@
     *ppBlockTail = pNewBlock;
 
+#ifdef KMK_CC_WITH_STATS
+    g_cBlockAllocated++;
+    g_cbAllocated += cbBlock;
+#endif
+
     pRet = (PKMKCCEXPCORE)(pNewBlock + 1);
 
@@ -571,24 +734,11 @@
 
 
-/**
- * Counts the number of dollar chars in the string.
- *
- * @returns Number of dollar chars.
- * @param   pchStr      The string to search (does not need to be zero
- *                      terminated).
- * @param   cchStr      The length of the string.
- */
-static uint32_t kmk_cc_count_dollars(const char *pchStr, uint32_t cchStr)
-{
-    uint32_t cDollars = 0;
-    const char *pch;
-    while ((pch = memchr(pchStr, '$', cchStr)) != NULL)
-    {
-        cDollars++;
-        cchStr -= pch - pchStr + 1;
-        pchStr  = pch + 1;
-    }
-    return cDollars;
-}
+/*
+ *
+ * The string expansion compiler.
+ * The string expansion compiler.
+ * The string expansion compiler.
+ *
+ */
 
 
@@ -622,13 +772,21 @@
         default:
             return 0;
+
+        case 'e':
+            if (!strcmp(pszFunction, "eval"))
+                return 1;
+            if (!strcmp(pszFunction, "evalctx"))
+                return 1;
+            return 0;
+
         case 'f':
-            if (pszFunction[1] == 'i')
-            {
-                if (!strcmp(pszFunction, "filter"))
-                    return 1;
-                if (!strcmp(pszFunction, "filter-out"))
-                    return 1;
-            }
+            if (!strcmp(pszFunction, "filter"))
+                return 1;
+            if (!strcmp(pszFunction, "filter-out"))
+                return 1;
+            if (!strcmp(pszFunction, "for"))
+                return 1;
             return 0;
+
         case 's':
             if (!strcmp(pszFunction, "sort"))
@@ -1295,5 +1453,5 @@
     PKMKCCBLOCK     pBlock;
     pProg = kmk_cc_block_alloc_first(&pBlock, sizeof(*pProg),
-                                     (kmk_cc_count_dollars(pchStr, cchStr) + 8)  * 16);
+                                     (kmk_cc_count_dollars(pchStr, cchStr) + 4)  * 8);
     if (pProg)
     {
@@ -1303,4 +1461,7 @@
         pProg->pFirstInstr  = (PKMKCCEXPCORE)kmk_cc_block_get_next_ptr(pBlock);
         kmk_cc_exp_stats_init(&pProg->Stats);
+#ifdef KMK_CC_STRICT
+        pProg->uInputHash   = kmk_cc_debug_string_hash_n(0, pchStr, cchStr);
+#endif
 
         /*
@@ -1308,10 +1469,26 @@
          */
         if (kmk_cc_exp_compile_common(&pProg->pBlockTail, pchStr, cchStr) == 0)
+        {
+#ifdef KMK_CC_WITH_STATS
+            pBlock = pProg->pBlockTail;
+            if (!pBlock->pNext)
+                g_cSingleBlockExpProgs++;
+            else if (!pBlock->pNext->pNext)
+                g_cTwoBlockExpProgs++;
+            else
+                g_cMultiBlockExpProgs++;
+            for (; pBlock; pBlock = pBlock->pNext)
+            {
+                g_cBlocksAllocatedExpProgs++;
+                g_cbAllocatedExpProgs += pBlock->cbBlock;
+                g_cbUnusedMemExpProgs += pBlock->cbBlock - pBlock->offNext;
+            }
+#endif
             return pProg;
+        }
         kmk_cc_block_free_list(pProg->pBlockTail);
     }
     return NULL;
 }
-
 
 
@@ -1325,4 +1502,30 @@
 {
     return NULL;
+}
+
+
+/**
+ * Updates the recursive_without_dollar member of a variable structure.
+ *
+ * This avoid compiling string expansion programs without only a CopyString
+ * instruction.  By setting recursive_without_dollar to 1, code calling
+ * kmk_cc_compile_variable_for_expand and kmk_exec_expand_to_var_buf will
+ * instead treat start treating it as a simple variable, which is faster.
+ *
+ * @returns The updated recursive_without_dollar value.
+ * @param   pVar        Pointer to the variable.
+ */
+static int kmk_cc_update_variable_recursive_without_dollar(struct variable *pVar)
+{
+    int fValue;
+    KMK_CC_ASSERT(pVar->recursive_without_dollar == 0);
+
+    if (memchr(pVar->value, '$', pVar->value_length))
+        fValue = -1;
+    else
+        fValue = 1;
+    pVar->recursive_without_dollar = fValue;
+
+    return fValue;
 }
 
@@ -1337,31 +1540,21 @@
 struct kmk_cc_expandprog *kmk_cc_compile_variable_for_expand(struct variable *pVar)
 {
+    KMK_CC_ASSERT(strlen(pVar->value) == pVar->value_length);
     KMK_CC_ASSERT(!pVar->expandprog);
+    KMK_CC_ASSERT(pVar->recursive_without_dollar <= 0);
+
     if (   !pVar->expandprog
-        && pVar->value_length > 0
         && pVar->recursive)
     {
-        KMK_CC_ASSERT(strlen(pVar->value) == pVar->value_length);
-        pVar->expandprog = kmk_cc_exp_compile(pVar->value, pVar->value_length);
+        if (   pVar->recursive_without_dollar < 0
+            || (   pVar->recursive_without_dollar == 0
+                && kmk_cc_update_variable_recursive_without_dollar(pVar) < 0) )
+        {
+            pVar->expandprog = kmk_cc_exp_compile(pVar->value, pVar->value_length);
+            g_cVarForExpandCompilations++;
+        }
     }
     return pVar->expandprog;
 }
-
-
-#ifndef NDEBUG
-/**
- * Used to check that function arguments are left alone.
- * @returns Updated hash.
- * @param   uHash       The current hash value.
- * @param   psz         The string to hash.
- */
-static uint32_t kmk_exec_debug_string_hash(uint32_t uHash, const char *psz)
-{
-    unsigned char ch;
-    while ((ch = *(unsigned char const *)psz++) != '\0')
-        uHash = (uHash << 6) + (uHash << 16) - uHash + (unsigned char)ch;
-    return uHash;
-}
-#endif
 
 
@@ -1377,5 +1570,5 @@
     if (pVar->value_length > 0)
     {
-        if (!pVar->recursive)
+        if (!pVar->recursive || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
             pchDst = variable_buffer_output(pchDst, pVar->value, pVar->value_length);
         else
@@ -1527,18 +1720,18 @@
                 if (!pInstr->Core.fDirty)
                 {
-#ifndef NDEBUG
+#ifdef KMK_CC_STRICT
                     uint32_t uCrcBefore = 0;
                     uint32_t uCrcAfter = 0;
                     iArg = pInstr->Core.cArgs;
                     while (iArg-- > 0)
-                        uCrcBefore = kmk_exec_debug_string_hash(uCrcBefore, pInstr->apszArgs[iArg]);
+                        uCrcBefore = kmk_cc_debug_string_hash(uCrcBefore, pInstr->apszArgs[iArg]);
 #endif
 
                     pchDst = pInstr->Core.pfnFunction(pchDst, (char **)&pInstr->apszArgs[0], pInstr->Core.pszFuncName);
 
-#ifndef NDEBUG
+#ifdef KMK_CC_STRICT
                     iArg = pInstr->Core.cArgs;
                     while (iArg-- > 0)
-                        uCrcAfter = kmk_exec_debug_string_hash(uCrcAfter, pInstr->apszArgs[iArg]);
+                        uCrcAfter = kmk_cc_debug_string_hash(uCrcAfter, pInstr->apszArgs[iArg]);
                     KMK_CC_ASSERT(uCrcBefore == uCrcAfter);
 #endif
@@ -1575,5 +1768,5 @@
                 if (!pInstr->Core.fDirty)
                 {
-#ifndef NDEBUG
+#ifdef KMK_CC_STRICT
                     uint32_t    uCrcBefore = 0;
                     uint32_t    uCrcAfter = 0;
@@ -1590,6 +1783,6 @@
                         papszArgsShadow[iArg] = pszArg;
                         papszArgs[iArg]       = pszArg;
-#ifndef NDEBUG
-                        uCrcBefore = kmk_exec_debug_string_hash(uCrcBefore, pszArg);
+#ifdef KMK_CC_STRICT
+                        uCrcBefore = kmk_cc_debug_string_hash(uCrcBefore, pszArg);
 #endif
                     }
@@ -1599,7 +1792,7 @@
                     while (iArg-- > 0)
                     {
-#ifndef NDEBUG
+#ifdef KMK_CC_STRICT
                         KMK_CC_ASSERT(papszArgsShadow[iArg] == papszArgs[iArg]);
-                        uCrcAfter = kmk_exec_debug_string_hash(uCrcAfter, papszArgsShadow[iArg]);
+                        uCrcAfter = kmk_cc_debug_string_hash(uCrcAfter, papszArgsShadow[iArg]);
 #endif
                         if (!pInstr->aArgs[iArg].fPlain)
@@ -1733,4 +1926,5 @@
     cchResult -= offStart;
     kmk_cc_exp_stats_update(&pProg->Stats, cchResult);
+    g_cVarForExpandExecs++;
 
     return pchDst;
@@ -1760,4 +1954,5 @@
 {
     KMK_CC_ASSERT(pVar->expandprog);
+    KMK_CC_ASSERT(pVar->expandprog->uInputHash == kmk_cc_debug_string_hash(0, pVar->value));
     return kmk_exec_expand_prog_to_var_buf(pVar->expandprog, pchDst);
 }
Index: /trunk/src/kmk/kmk_cc_exec.h
===================================================================
--- /trunk/src/kmk/kmk_cc_exec.h	(revision 2770)
+++ /trunk/src/kmk/kmk_cc_exec.h	(revision 2771)
@@ -31,4 +31,5 @@
 
 void  kmk_cc_init(void);
+void  kmk_cc_print_stats(void);
 
 extern struct kmk_cc_evalprog   *kmk_cc_compile_variable_for_eval(struct variable *pVar);
Index: /trunk/src/kmk/kmkbuiltin/append.c
===================================================================
--- /trunk/src/kmk/kmkbuiltin/append.c	(revision 2770)
+++ /trunk/src/kmk/kmkbuiltin/append.c	(revision 2771)
@@ -218,6 +218,8 @@
             if (!pVar)
                 continue;
-            if (    pVar->recursive
-                &&  memchr(pVar->value, '$', pVar->value_length))
+            if (   !pVar->recursive
+                || IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(pVar))
+                fwrite(pVar->value, 1, pVar->value_length, pFile);
+            else
             {
                 char *pszExpanded = allocated_variable_expand(pVar->value);
@@ -225,6 +227,4 @@
                 free(pszExpanded);
             }
-            else
-                fwrite(pVar->value, 1, pVar->value_length, pFile);
         }
         else
Index: /trunk/src/kmk/main.c
===================================================================
--- /trunk/src/kmk/main.c	(revision 2770)
+++ /trunk/src/kmk/main.c	(revision 2771)
@@ -3832,4 +3832,7 @@
   alloccache_print_all ();
 #endif
+#ifdef CONFIG_WITH_COMPILER
+  kmk_cc_print_stats ();
+#endif
 
   when = time ((time_t *) 0);
Index: /trunk/src/kmk/variable.c
===================================================================
--- /trunk/src/kmk/variable.c	(revision 2770)
+++ /trunk/src/kmk/variable.c	(revision 2771)
@@ -449,4 +449,5 @@
   v->export = v_default;
 #ifdef CONFIG_WITH_COMPILER
+  v->recursive_without_dollar = 0;
   v->evalprog = 0;
   v->expandprog = 0;
@@ -617,4 +618,5 @@
       v->export = v_default;
 #ifdef CONFIG_WITH_COMPILER
+      v->recursive_without_dollar = 0;
       v->evalprog = 0;
       v->expandprog = 0;
@@ -2867,4 +2869,7 @@
 static unsigned long var_stats_expands, var_stats_expanded;
 #endif
+#ifdef CONFIG_WITH_COMPILER
+static unsigned long var_stats_expandprogs, var_stats_evalprogs;
+#endif
 #ifdef CONFIG_WITH_MAKE_STATS
 static unsigned long var_stats_changes, var_stats_changed;
@@ -2940,21 +2945,32 @@
 #if defined (CONFIG_WITH_COMPILER) || defined (CONFIG_WITH_MAKE_STATS)
   if (v->evalval_count != 0)
+    {
 # ifdef CONFIG_WITH_MAKE_STATS
-    printf (_(", %u evalvals (%llu ticks)"), v->evalval_count, v->cTicksEvalVal);
+      printf (_(", %u evalvals (%llu ticks)"), v->evalval_count, v->cTicksEvalVal);
 # else
-    printf (_(", %u evalvals"), v->evalval_count);
+      printf (_(", %u evalvals"), v->evalval_count);
 # endif
+      var_stats_evalvaled++;
+    }
   var_stats_evalvals += v->evalval_count;
-  var_stats_evalvaled += (v->evalval_count != 0);
 
   if (v->expand_count != 0)
-    printf (_(", %u expands"), v->expand_count);
+    {
+      printf (_(", %u expands"), v->expand_count);
+      var_stats_expanded++;
+    }
   var_stats_expands += v->expand_count;
-  var_stats_expanded += (v->expand_count != 0);
+
 # ifdef CONFIG_WITH_COMPILER
   if (v->evalprog != 0)
-    printf (_(", evalprog"));
+    {
+      printf (_(", evalprog"));
+      var_stats_evalprogs++;
+    }
   if (v->expandprog != 0)
-    printf (_(", expandprog"));
+    {
+      printf (_(", expandprog"));
+      var_stats_expandprogs++;
+    }
 # endif
 #endif
@@ -2962,17 +2978,23 @@
 #ifdef CONFIG_WITH_MAKE_STATS
   if (v->changes != 0)
-    printf (_(", %u changes"), v->changes);
+    {
+      printf (_(", %u changes"), v->changes);
+      var_stats_changed++;
+    }
   var_stats_changes += v->changes;
-  var_stats_changed += (v->changes != 0);
 
   if (v->reallocs != 0)
-    printf (_(", %u reallocs"), v->reallocs);
+    {
+      printf (_(", %u reallocs"), v->reallocs);
+      var_stats_realloced++;
+    }
   var_stats_reallocs += v->reallocs;
-  var_stats_realloced += (v->reallocs != 0);
 
   if (v->references != 0)
-    printf (_(", %u references"), v->references);
+    {
+      printf (_(", %u references"), v->references);
+      var_stats_referenced++;
+    }
   var_stats_references += v->references;
-  var_stats_referenced += (v->references != 0);
 
   var_stats_val_len += v->value_length;
@@ -3034,4 +3056,7 @@
     = var_stats_evalvaled = 0;
 #endif
+#ifdef CONFIG_WITH_COMPILER
+  var_stats_expandprogs = var_stats_evalprogs = 0;
+#endif
 #ifdef CONFIG_WITH_MAKE_STATS
   var_stats_changes = var_stats_changed = var_stats_reallocs
@@ -3088,4 +3113,12 @@
                var_stats_expands);
 #endif
+#ifdef CONFIG_WITH_COMPILER
+      if (var_stats_expandprogs || var_stats_evalprogs)
+        printf(_("#  eval progs %5lu (%2u%%),     expand progs %6lu (%2u%%)\n"),
+               var_stats_evalprogs,
+               (unsigned int)((100.0 * var_stats_evalprogs) / set->table.ht_fill),
+               var_stats_expandprogs,
+               (unsigned int)((100.0 * var_stats_expandprogs) / set->table.ht_fill));
+#endif
       }
 
Index: /trunk/src/kmk/variable.h
===================================================================
--- /trunk/src/kmk/variable.h	(revision 2770)
+++ /trunk/src/kmk/variable.h	(revision 2771)
@@ -18,4 +18,7 @@
 
 #include "hash.h"
+#ifdef CONFIG_WITH_COMPILER
+# include "kmk_cc_exec.h"
+#endif
 
 /* Codes in a variable definition saying where the definition came from.
@@ -109,4 +112,7 @@
 	v_default		/* Decide in target_environment.  */
       } export ENUM_BITFIELD (2);
+#ifdef CONFIG_WITH_COMPILER
+    int recursive_without_dollar : 2; /* 0 if undetermined, 1 if value has no '$' chars, -1 if it has. */
+#endif
 #ifdef CONFIG_WITH_MAKE_STATS
     unsigned int changes;      /* Variable modification count.  */
@@ -133,7 +139,16 @@
       (v)->expand_count = 0; \
       (v)->evalval_count = 0; \
+      (v)->recursive_without_dollar = 0; \
     } while (0)
 #else
 # define VARIABLE_CHANGED(v) MAKE_STATS_2((v)->changes++)
+#endif
+
+/* Macro that avoids a lot of CONFIG_WITH_COMPILER checks when
+   accessing recursive_without_dollar. */
+#ifdef CONFIG_WITH_COMPILER
+# define IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(v) ((v)->recursive_without_dollar > 0)
+#else
+# define IS_VARIABLE_RECURSIVE_WITHOUT_DOLLAR(v) 0
 #endif
 
