Index: ChangeLog
===================================================================
RCS file: /cvsroot/tcl/tcl/ChangeLog,v
retrieving revision 1.3502
diff -u -r1.3502 ChangeLog
--- ChangeLog	3 Jun 2007 13:43:24 -0000	1.3502
+++ ChangeLog	5 Jun 2007 17:42:26 -0000
@@ -1,3 +1,8 @@
+2007-06-05  Miguel Sofer  <msofer@users.sf.net>
+
+	* generic/tclInt.h:
+	* generic/tclExecute.c: Tcl-stack reform, [Patch 1701202]
+	
 2007-06-03  Daniel Steffen  <das@users.sourceforge.net>
 
 	* unix/Makefile.in: add datarootdir to silence autoconf-2.6x warning.
Index: generic/tclExecute.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclExecute.c,v
retrieving revision 1.285
diff -u -r1.285 tclExecute.c
--- generic/tclExecute.c	17 May 2007 13:45:27 -0000	1.285
+++ generic/tclExecute.c	5 Jun 2007 17:42:27 -0000
@@ -183,11 +183,10 @@
  */
 
 #define CACHE_STACK_INFO() \
-    tosPtr = eePtr->tosPtr;\
     checkInterp = 1
 
 #define DECACHE_STACK_INFO() \
-    eePtr->tosPtr = tosPtr
+    esPtr->tosPtr = tosPtr
 
 /*
  * Macros used to access items on the Tcl evaluation stack. PUSH_OBJECT
@@ -215,7 +214,7 @@
 
 #define OBJ_AT_DEPTH(n) *(tosPtr-(n))
 
-#define CURR_DEPTH     (tosPtr-eePtr->stackPtr)
+#define CURR_DEPTH     (tosPtr - initTosPtr)
 
 /*
  * Macros used to trace instruction execution. The macros TRACE,
@@ -389,7 +388,7 @@
 			    ByteCode *codePtr);
 static const char *	GetSrcInfoForPc(unsigned char *pc, ByteCode *codePtr,
 			    int *lengthPtr);
-static void		GrowEvaluationStack(ExecEnv *eePtr, int growth);
+static Tcl_Obj **	GrowEvaluationStack(ExecEnv *eePtr, int growth, int move);
 static void		IllegalExprOperandType(Tcl_Interp *interp,
 			    unsigned char *pc, Tcl_Obj *opndPtr);
 static void		InitByteCodeExecution(Tcl_Interp *interp);
@@ -400,6 +399,17 @@
 			    unsigned char *pc, int stackTop,
 			    int stackLowerBound, int checkStack);
 #endif /* TCL_COMPILE_DEBUG */
+
+static void             DeleteExecStack(ExecStack *esPtr);
+
+/* Useful elsewhere, make available in tclInt.h or stubs? */
+static Tcl_Obj **       StackAllocWords(Tcl_Interp *interp, int numWords);
+static Tcl_Obj **       StackReallocWords(Tcl_Interp *interp, int numWords);
+
+/* Move to internal stubs? For now, unused */
+extern char *           TclStackRealloc(Tcl_Interp *interp, int numBytes);
+
+
 
 /*
  *----------------------------------------------------------------------
@@ -470,28 +480,22 @@
 				 * environment is being created. */
 {
     ExecEnv *eePtr = (ExecEnv *) ckalloc(sizeof(ExecEnv));
-    Tcl_Obj **stackPtr;
-
-    stackPtr = (Tcl_Obj **)
-	    ckalloc((size_t) (TCL_STACK_INITIAL_SIZE * sizeof(Tcl_Obj *)));
-
-    /*
-     * Use the bottom pointer to keep a reference count; the execution
-     * environment holds a reference.
-     */
-
-    stackPtr++;
-    eePtr->stackPtr = stackPtr;
-    stackPtr[-1] = (Tcl_Obj *) ((char *) 1);
-
-    eePtr->tosPtr = stackPtr - 1;
-    eePtr->endPtr = stackPtr + (TCL_STACK_INITIAL_SIZE - 2);
+    ExecStack *esPtr = (ExecStack *)
+	ckalloc((size_t) (sizeof(ExecStack)
+			+ (TCL_STACK_INITIAL_SIZE -1) * sizeof(Tcl_Obj *)));
 
+    eePtr->execStackPtr = esPtr;
     TclNewBooleanObj(eePtr->constants[0], 0);
     Tcl_IncrRefCount(eePtr->constants[0]);
     TclNewBooleanObj(eePtr->constants[1], 1);
     Tcl_IncrRefCount(eePtr->constants[1]);
 
+    esPtr->prevPtr = NULL;
+    esPtr->nextPtr = NULL;
+    esPtr->markerPtr = NULL;
+    esPtr->endPtr   = &esPtr->stackWords[TCL_STACK_INITIAL_SIZE-1];
+    esPtr->tosPtr  = &esPtr->stackWords[-1];
+
     Tcl_MutexLock(&execMutex);
     if (!execInitialized) {
 	TclInitAuxDataTypeTable();
@@ -521,15 +525,42 @@
  *----------------------------------------------------------------------
  */
 
+static void
+DeleteExecStack(
+    ExecStack *esPtr)
+{
+    if (esPtr->markerPtr) {
+	Tcl_Panic("freeing an execStack which is still in use");
+    }
+
+    if (esPtr->prevPtr) {
+	esPtr->prevPtr->nextPtr = esPtr->nextPtr;
+    }
+    if (esPtr->nextPtr) {
+	esPtr->nextPtr->prevPtr = esPtr->prevPtr;	
+    }
+    ckfree((char *) esPtr);
+}
+
 void
 TclDeleteExecEnv(
     ExecEnv *eePtr)		/* Execution environment to free. */
 {
-    if (eePtr->stackPtr[-1] == (Tcl_Obj *) ((char *) 1)) {
-	ckfree((char *) (eePtr->stackPtr-1));
-    } else {
-	Tcl_Panic("freeing an execEnv whose stack is still in use");
+    ExecStack *esPtr = eePtr->execStackPtr, *tmpPtr;
+
+    /*
+     * Delete all stacks in this exec env.
+     */
+    
+    while (esPtr->nextPtr) {
+	esPtr = esPtr->nextPtr;
     }
+    while (esPtr) {
+	tmpPtr = esPtr;
+	esPtr = tmpPtr->prevPtr;
+	DeleteExecStack(tmpPtr);
+    }
+
     TclDecrRefCount(eePtr->constants[0]);
     TclDecrRefCount(eePtr->constants[1]);
     ckfree((char *) eePtr);
@@ -567,67 +598,122 @@
  *
  * GrowEvaluationStack --
  *
- *	This procedure grows a Tcl evaluation stack stored in an ExecEnv.
+ *	This procedure grows a Tcl evaluation stack stored in an ExecEnv,
+ *	copying over the words since the last mark if so requested. A mark is
+ *	set at the beginning of the new area when no copying is requested.
  *
  * Results:
- *	None.
+ *	Returns a pointer to the first usable word in the (possibly) grown
+ *      stack. 
  *
  * Side effects:
- *	The size of the evaluation stack is grown.
+ *	The size of the evaluation stack may be grown, a marker is set
  *
  *----------------------------------------------------------------------
  */
 
-static void
+static Tcl_Obj **
 GrowEvaluationStack(
-    ExecEnv *eePtr,		/* Points to the ExecEnv with an evaluation
-				 * stack to enlarge. */
-    int growth)
+    ExecEnv *eePtr, /* Points to the ExecEnv with an evaluation
+		     * stack to enlarge. */
+    int growth,     /* How much larger than the current used size */
+    int move)       /* 1 if move words since last marker */
 {
-    Tcl_Obj **newStackPtr, **oldStackPtr = eePtr->stackPtr;
-    int currElems, newBytes, newElems;
-    int needed = growth - (eePtr->endPtr - eePtr->tosPtr);
-    char *refCount;
-
-    if (needed <= 0) {
-	return;
+    ExecStack *esPtr = eePtr->execStackPtr, *oldPtr = NULL;
+    int newBytes, newElems;
+    int needed = growth - (esPtr->endPtr - esPtr->tosPtr);
+    int currElems;
+    Tcl_Obj **markerPtr = esPtr->markerPtr;
+    
+    if (move) {
+	if (!markerPtr) {
+	    Tcl_Panic("STACK: Reallocating with no previous alloc");
+	}
+	if (needed <= 0) {
+	    return (markerPtr+1);
+	}
+    } else if (needed < 0) {
+	esPtr->markerPtr = ++esPtr->tosPtr;
+	*esPtr->markerPtr = (Tcl_Obj *) markerPtr;
+	return (esPtr->markerPtr+1);
     }
 
     /*
-     * The current Tcl stack elements are stored from *(eePtr->stackPtr) to
-     * *(eePtr->endPtr) (inclusive).
+     * Reset move to hold the number of words to be moved to new stack (if
+     * any) and growth to hold the complete stack requirements.
      */
 
-    currElems = (eePtr->endPtr - eePtr->stackPtr + 1);
-    newElems = 2*currElems;
-    while (needed > newElems - currElems) {
-	newElems *= 2;
+    if (move) {
+	move = esPtr->tosPtr - markerPtr;
     }
-    newBytes = newElems * sizeof(Tcl_Obj *);
-
+    needed = growth + move + 1; /* add the marker */
+    
     /*
-     * We keep the stack reference count as a (char *), as that works nicely
-     * as a portable pointer-sized counter.
+     * Check if there is enough room in the next stack (if there is one, it
+     * should be both empty and the last one!)
      */
 
-    refCount = (char *) oldStackPtr[-1];
-    if (refCount == (char *) 1) {
-	newStackPtr = (Tcl_Obj **) ckrealloc(
-		(char *) (oldStackPtr - 1), newBytes);
-	newStackPtr++;
+    if (esPtr->nextPtr) {
+	oldPtr = esPtr;
+	esPtr = oldPtr->nextPtr;
+	currElems = esPtr->endPtr - &esPtr->stackWords[-1];
+	if (esPtr->markerPtr || (esPtr->tosPtr != &esPtr->stackWords[-1])) {
+	    Tcl_Panic("STACK: Stack after current is in use");
+	}
+	if (esPtr->nextPtr) {
+	    Tcl_Panic("STACK: Stack after current is not last");
+	}
+	if (needed <= currElems) {
+	    goto newStackReady;
+	} else {
+	    DeleteExecStack(esPtr);
+	    esPtr = oldPtr;
+	}
     } else {
-	/* Can't free oldStackPtr, so can't use ckrealloc */
-	int currBytes = currElems * sizeof(Tcl_Obj *);
-	newStackPtr = (Tcl_Obj **) ckalloc(newBytes);
-	newStackPtr++;
-	memcpy(newStackPtr, oldStackPtr, currBytes);
-	oldStackPtr[-1] = (Tcl_Obj *) (refCount-1);
-	newStackPtr[-1] = (Tcl_Obj *) ((char *) 1);
+	currElems = esPtr->endPtr - &esPtr->stackWords[-1];
+    }
+
+    /*
+     * We need to allocate a new stack! It needs to store 'growth' words,
+     * including the elements to be copied over and the new marker. 
+     */
+    
+    newElems = 2*currElems;
+    while (needed > newElems) {
+	newElems *= 2;
+    }
+    newBytes = sizeof (ExecStack) + (newElems-1) * sizeof(Tcl_Obj *);
+    
+    oldPtr = esPtr;
+    esPtr  = (ExecStack *) ckalloc(newBytes);
+
+    oldPtr->nextPtr = esPtr;
+    esPtr->prevPtr = oldPtr;
+    esPtr->nextPtr = NULL;
+    esPtr->endPtr  = &esPtr->stackWords[newElems-1];
+    
+    newStackReady:
+    eePtr->execStackPtr = esPtr;
+
+    esPtr->stackWords[0] = NULL;
+    esPtr->markerPtr = esPtr->tosPtr = &esPtr->stackWords[0];
+    
+    if (move) {
+	memcpy(&esPtr->stackWords[1], (markerPtr+1), move*sizeof(Tcl_Obj *));
+	esPtr->tosPtr += move;
+	oldPtr->markerPtr = (Tcl_Obj **) *markerPtr;
+	oldPtr->tosPtr = markerPtr-1;
     }
 
-    eePtr->stackPtr = newStackPtr;
-    eePtr->endPtr = newStackPtr + (newElems-2); /* index of last usable item */
-    eePtr->tosPtr = newStackPtr + (eePtr->tosPtr - oldStackPtr);
+    /*
+     * Free the old stack if it is now unused.
+     */
+    
+    if (!oldPtr->markerPtr) {
+	DeleteExecStack(oldPtr);
+    }
+
+    return &esPtr->stackWords[1];
 }
 
 /*
@@ -648,41 +734,30 @@
  *--------------------------------------------------------------
  */
 
-char *
-TclStackAlloc(
+static Tcl_Obj **
+StackAllocWords(
     Tcl_Interp *interp,
-    int numBytes)
+    int numWords)
 {
     Interp *iPtr = (Interp *) interp;
     ExecEnv *eePtr = iPtr->execEnvPtr;
+    Tcl_Obj **resPtr = GrowEvaluationStack(eePtr, numWords, 0);
 
-    /*
-     * Add two words to store
-     *   - a pointer to the used execution stack
-     *   - the number of words reserved
-     * These will be used later by TclStackFree.
-     */
-
-    int numWords = (numBytes + 3*sizeof(void *) - 1)/sizeof(void *);
-    Tcl_Obj **tosPtr = (GrowEvaluationStack(eePtr, numWords), eePtr->tosPtr);
-
-    /*
-     * Increase the stack's reference count, to make sure it is not freed
-     * prematurely.
-     */
-
-    char **stackRefCountPtr = (char **) (eePtr->stackPtr-1);
-    ++*stackRefCountPtr;
-
-    /*
-     * Reserve the space in the exec stack, and store the data for freeing.
-     */
+    eePtr->execStackPtr->tosPtr += numWords;
+    return resPtr;
+}
 
-    eePtr->tosPtr += numWords;
-    *(eePtr->tosPtr-1) = (Tcl_Obj *) stackRefCountPtr;
-    *(eePtr->tosPtr) = (Tcl_Obj *) INT2PTR(numWords);
+static Tcl_Obj **
+StackReallocWords(
+    Tcl_Interp *interp,
+    int numWords)
+{
+    Interp *iPtr = (Interp *) interp;
+    ExecEnv *eePtr = iPtr->execEnvPtr;
+    Tcl_Obj **resPtr = GrowEvaluationStack(eePtr, numWords, 1);
 
-    return (char *) (tosPtr+1);
+    eePtr->execStackPtr->tosPtr += numWords;
+    return resPtr;
 }
 
 void
@@ -691,16 +766,50 @@
 {
     Interp *iPtr = (Interp *) interp;
     ExecEnv *eePtr = iPtr->execEnvPtr;
-    char **stackRefCountPtr;
-
-    stackRefCountPtr = (char **) *(eePtr->tosPtr-1);
-    eePtr->tosPtr -= PTR2INT(*(eePtr->tosPtr));
-
-    --*stackRefCountPtr;
-    if (*stackRefCountPtr == (char *) 0) {
-	ckfree((char *) stackRefCountPtr);
+    ExecStack *esPtr = eePtr->execStackPtr;
+    Tcl_Obj **markerPtr = esPtr->markerPtr;
+    
+    esPtr->tosPtr = markerPtr-1;
+    esPtr->markerPtr = (Tcl_Obj **) *markerPtr;
+    if (*markerPtr) {
+ 	return;
+    }
+    
+    /*
+     * Return to previous stack.
+     */
+    
+    esPtr->tosPtr = &esPtr->stackWords[-1];
+    if (esPtr->prevPtr) {
+ 	eePtr->execStackPtr = esPtr->prevPtr;
+    }
+    if (esPtr->nextPtr) {
+ 	if (!esPtr->prevPtr) {
+ 	    eePtr->execStackPtr = esPtr->nextPtr;
+ 	}
+ 	DeleteExecStack(esPtr);
     }
 }
+ 
+char *
+TclStackAlloc(
+    Tcl_Interp *interp,
+    int numBytes)
+{
+    int numWords = (numBytes + (sizeof(Tcl_Obj *) - 1))/sizeof(Tcl_Obj *);
+    
+    return (char *) StackAllocWords(interp, numWords);
+}
+
+char * 
+TclStackRealloc(
+    Tcl_Interp *interp,
+    int numBytes)
+{
+    int numWords = (numBytes + sizeof(void *) - 1)/sizeof(void *);
+    
+    return (char *) StackReallocWords(interp, numWords);
+}
 
 /*
  *--------------------------------------------------------------
@@ -1194,18 +1303,19 @@
      * sporadically.
      */
 
-    ExecEnv *eePtr;		/* Points to the execution environment. */
-    int initStackDepth;		/* Stack top at start of execution. */
-    int initCatchTop;		/* Catch stack top at start of execution. */
+    ExecStack *esPtr;
+    Tcl_Obj **initTosPtr;	/* Stack top at start of execution. */
+    ptrdiff_t *initCatchTop;	/* Catch stack top at start of execution. */
     Var *compiledLocals;
     Namespace *namespacePtr;
     CmdFrame bcFrame;		/* TIP #280: Structure for tracking lines. */
+    Tcl_Obj **constants = &iPtr->execEnvPtr->constants[0];
 
     /*
      * Globals: variables that store state, must remain valid at all times.
      */
 
-    int catchTop;
+    ptrdiff_t  *catchTop;
     register Tcl_Obj **tosPtr;	/* Cached pointer to top of evaluation
 				 * stack. */
     register unsigned char *pc = codePtr->codeStart;
@@ -1214,7 +1324,7 @@
 				 * call Tcl_AsyncReady() */
     Tcl_Obj *expandNestList = NULL;
     int checkInterp = 0;	/* Indicates when a check of interp readyness
-				 * is necessary. Set by DECACHE_STACK_INFO() */
+				 * is necessary. Set by CACHE_STACK_INFO() */
 
     /*
      * Transfer variables - needed only between opcodes, but not while
@@ -1252,16 +1362,12 @@
      * execution stack is large enough to execute this ByteCode.
      */
 
-    eePtr = iPtr->execEnvPtr;
-    initCatchTop = eePtr->tosPtr - eePtr->stackPtr;
-    catchTop = initCatchTop;
-
-    GrowEvaluationStack(eePtr,
-	    codePtr->maxExceptDepth + codePtr->maxStackDepth);
-    tosPtr = eePtr->tosPtr + codePtr->maxExceptDepth;
-
-    initStackDepth = CURR_DEPTH;
-
+    catchTop = initCatchTop =
+	(ptrdiff_t *) (GrowEvaluationStack(iPtr->execEnvPtr,
+		codePtr->maxExceptDepth + codePtr->maxStackDepth, 0) - 1);
+    tosPtr = initTosPtr = ((Tcl_Obj **) initCatchTop) + codePtr->maxExceptDepth;
+    esPtr = iPtr->execEnvPtr->execStackPtr;
+    
     /*
      * TIP #280: Initialize the frame. Do not push it yet.
      */
@@ -1282,7 +1388,7 @@
 #ifdef TCL_COMPILE_DEBUG
     if (tclTraceExec >= 2) {
 	PrintByteCodeInfo(codePtr);
-	fprintf(stdout, "  Starting stack top=%d\n", initStackDepth);
+	fprintf(stdout, "  Starting stack top=%d\n", CURR_DEPTH);
 	fflush(stdout);
     }
 #endif
@@ -1373,7 +1479,7 @@
      */
 
     ValidatePcAndStackTop(codePtr, pc, CURR_DEPTH,
-	    initStackDepth, /*checkStack*/ (expandNestList == NULL));
+	    0, /*checkStack*/ (expandNestList == NULL));
     if (traceInstructions) {
 	fprintf(stdout, "%2d: %2d ", iPtr->numLevels, (int) CURR_DEPTH);
 	TclPrintInstruction(codePtr, pc);
@@ -1458,7 +1564,7 @@
 	}
 
     case INST_DONE:
-	if (CURR_DEPTH > initStackDepth) {
+	if (tosPtr > initTosPtr) {
 	    /*
 	     * Set the interpreter's object result to point to the topmost
 	     * object from the stack, and check for a possible [catch]. The
@@ -1690,8 +1796,8 @@
 	 */
 
 	Tcl_Obj *objPtr;
-
-	TclNewObj(objPtr);
+	
+  	TclNewObj(objPtr);
 	objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) CURR_DEPTH;
 	objPtr->internalRep.twoPtrValue.ptr2 = (VOID *) expandNestList;
 	expandNestList = objPtr;
@@ -1701,6 +1807,7 @@
     case INST_EXPAND_STKTOP: {
 	int objc, length, i;
 	Tcl_Obj **objv, *valuePtr;
+	ptrdiff_t moved;
 
 	/*
 	 * Make sure that the element at stackTop is a list; if not, just
@@ -1724,10 +1831,22 @@
 	 * stack depth, as seen by the compiler.
 	 */
 
-	length = objc + codePtr->maxStackDepth - TclGetInt4AtPtr(pc+1);
+	length = objc + (codePtr->maxStackDepth - TclGetInt4AtPtr(pc+1));
 	DECACHE_STACK_INFO();
-	GrowEvaluationStack(eePtr, length);
-	CACHE_STACK_INFO();
+	moved = (GrowEvaluationStack(iPtr->execEnvPtr, length, 1) - 1)
+	        - (Tcl_Obj **) initCatchTop;
+
+	if (moved) {
+	    /*
+	     * Change the global data to point to the new stack.
+	     */
+	    
+	    initCatchTop += moved;
+	    catchTop     += moved;
+	    initTosPtr   += moved;
+	    tosPtr       += moved;
+	    esPtr         = iPtr->execEnvPtr->execStackPtr;
+	}
 
 	/*
 	 * Expand the list at stacktop onto the stack; free the list. Knowing
@@ -1787,13 +1906,6 @@
 	    const char *bytes;
 	    Command *cmdPtr;
 
-	    /*
-	     * We keep the stack reference count as a (char *), as that works
-	     * nicely as a portable pointer-sized counter.
-	     */
-
-	    char **preservedStackRefCountPtr;
-
 #ifdef TCL_COMPILE_DEBUG
 	    if (tclTraceExec >= 2) {
 		int i;
@@ -1815,17 +1927,6 @@
 #endif /*TCL_COMPILE_DEBUG*/
 
 	    /*
-	     * A reference to part of the stack vector itself escapes our
-	     * control: increase its refCount to stop it from being
-	     * deallocated by a recursive call to ourselves. The extra
-	     * variable is needed because all others are liable to change due
-	     * to the trace procedures.
-	     */
-
-	    preservedStackRefCountPtr = (char **) (eePtr->stackPtr-1);
-	    ++*preservedStackRefCountPtr;
-
-	    /*
 	     * Reset the instructionCount variable, since we're about to check
 	     * for async stuff anyway while processing TclEvalObjvInternal.
 	     */
@@ -1880,17 +1981,6 @@
 	    CACHE_STACK_INFO();
 	    iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr;
 
-	    /*
-	     * If the old stack is going to be released, it is safe to do so
-	     * now, since no references to objv are going to be used from now
-	     * on.
-	     */
-
-	    --*preservedStackRefCountPtr;
-	    if (*preservedStackRefCountPtr == (char *) 0) {
-		ckfree((char *) preservedStackRefCountPtr);
-	    }
-
 	    if (result == TCL_OK) {
 		Tcl_Obj *objPtr;
 
@@ -2871,7 +2961,7 @@
 	valuePtr = OBJ_AT_TOS;
 
 	/* TODO - check claim that taking address of b harms performance */
-	/* TODO - consider optimization search for eePtr->constants */
+	/* TODO - consider optimization search for constants */
 	result = TclGetBooleanFromObj(interp, valuePtr, &b);
 	if (result != TCL_OK) {
 	    TRACE_WITH_OBJ(("%d => ERROR: ", jmpOffset[
@@ -2963,7 +3053,7 @@
 	} else {
 	    iResult = (i1 && i2);
 	}
-	objResultPtr = eePtr->constants[iResult];
+	objResultPtr = constants[iResult];
 	TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), iResult));
 	NEXT_INST_F(1, 2, 1);
     }
@@ -3371,7 +3461,7 @@
 	    NEXT_INST_F((found ? TclGetInt4AtPtr(pc+1) : 5), 2, 0);
 	}
 #endif
-	objResultPtr = eePtr->constants[found];
+	objResultPtr = constants[found];
 	NEXT_INST_F(0, 2, 1);
     }
 
@@ -3442,7 +3532,7 @@
 	    NEXT_INST_F((iResult? TclGetInt4AtPtr(pc+1) : 5), 2, 0);
 	}
 #endif
-	objResultPtr = eePtr->constants[iResult];
+	objResultPtr = constants[iResult];
 	NEXT_INST_F(0, 2, 1);
     }
 
@@ -3547,7 +3637,7 @@
 	    TclNewIntObj(objResultPtr, -1);
 	    TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), -1));
 	} else {
-	    objResultPtr = eePtr->constants[(iResult>0)];
+	    objResultPtr = constants[(iResult>0)];
 	    TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr),
 		(iResult > 0)));
 	}
@@ -3669,7 +3759,7 @@
 	 */
 
 	TRACE(("%.20s %.20s => %d\n", O2S(valuePtr), O2S(value2Ptr), match));
-	objResultPtr = eePtr->constants[match];
+	objResultPtr = constants[match];
 	NEXT_INST_F(2, 2, 1);
     }
 
@@ -3986,7 +4076,7 @@
 	    NEXT_INST_F((iResult? TclGetInt4AtPtr(pc+1) : 5), 2, 0);
 	}
 #endif
-	objResultPtr = eePtr->constants[iResult];
+	objResultPtr = constants[iResult];
 	NEXT_INST_F(0, 2, 1);
     }
 
@@ -4038,7 +4128,7 @@
 		     * Div. by |1| always yields remainder of 0
 		     */
 
-		    objResultPtr = eePtr->constants[0];
+		    objResultPtr = constants[0];
 		    TRACE(("%s\n", O2S(objResultPtr)));
 		    NEXT_INST_F(1, 2, 1);
 		}
@@ -4050,7 +4140,7 @@
 		     * 0 % (non-zero) always yields remainder of 0
 		     */
 
-		    objResultPtr = eePtr->constants[0];
+		    objResultPtr = constants[0];
 		    TRACE(("%s\n", O2S(objResultPtr)));
 		    NEXT_INST_F(1, 2, 1);
 		}
@@ -4261,7 +4351,7 @@
 
 	if ((type1 == TCL_NUMBER_LONG) && (*((const long *)ptr1) == (long)0)) {
 	    TRACE(("%s %s => ", O2S(valuePtr), O2S(value2Ptr)));
-	    objResultPtr = eePtr->constants[0];
+	    objResultPtr = constants[0];
 	    TRACE(("%s\n", O2S(objResultPtr)));
 	    NEXT_INST_F(1, 2, 1);
 	}
@@ -4360,7 +4450,7 @@
 		    zero = 0;
 		}
 		if (zero) {
-		    objResultPtr = eePtr->constants[0];
+		    objResultPtr = constants[0];
 		} else {
 		    TclNewIntObj(objResultPtr, -1);
 		}
@@ -4377,7 +4467,7 @@
 		l1 = *((const long *)ptr1);
 		if ((size_t)shift >= CHAR_BIT*sizeof(long)) {
 		    if (l1 >= (long)0) {
-			objResultPtr = eePtr->constants[0];
+			objResultPtr = constants[0];
 		    } else {
 			TclNewIntObj(objResultPtr, -1);
 		    }
@@ -4398,7 +4488,7 @@
 
 		if ((size_t)shift >= CHAR_BIT*sizeof(Tcl_WideInt)) {
 		    if (w >= (Tcl_WideInt)0) {
-			objResultPtr = eePtr->constants[0];
+			objResultPtr = constants[0];
 		    } else {
 			TclNewIntObj(objResultPtr, -1);
 		    }
@@ -5106,7 +5196,7 @@
 		     * Anything to the zero power is 1.
 		     */
 
-		    objResultPtr = eePtr->constants[1];
+		    objResultPtr = constants[1];
 		    NEXT_INST_F(1, 2, 1);
 		}
 	    }
@@ -5153,7 +5243,7 @@
 			if (oddExponent) {
 			    TclNewIntObj(objResultPtr, -1);
 			} else {
-			    objResultPtr = eePtr->constants[1];
+			    objResultPtr = constants[1];
 			}
 			NEXT_INST_F(1, 2, 1);
 		    case 1:
@@ -5161,7 +5251,7 @@
 			 * 1 to any power is 1.
 			 */
 
-			objResultPtr = eePtr->constants[1];
+			objResultPtr = constants[1];
 			NEXT_INST_F(1, 2, 1);
 		    }
 		}
@@ -5171,7 +5261,7 @@
 		 * power yield the answer zero (see TIP 123).
 		 */
 
-		objResultPtr = eePtr->constants[0];
+		objResultPtr = constants[0];
 		NEXT_INST_F(1, 2, 1);
 	    }
 
@@ -5183,20 +5273,20 @@
 		     * Zero to a positive power is zero.
 		     */
 
-		    objResultPtr = eePtr->constants[0];
+		    objResultPtr = constants[0];
 		    NEXT_INST_F(1, 2, 1);
 		case 1:
 		    /*
 		     * 1 to any power is 1.
 		     */
 
-		    objResultPtr = eePtr->constants[1];
+		    objResultPtr = constants[1];
 		    NEXT_INST_F(1, 2, 1);
 		case -1:
 		    if (oddExponent) {
 			TclNewIntObj(objResultPtr, -1);
 		    } else {
-			objResultPtr = eePtr->constants[1];
+			objResultPtr = constants[1];
 		    }
 		    NEXT_INST_F(1, 2, 1);
 		}
@@ -5370,7 +5460,7 @@
 	Tcl_Obj *valuePtr = OBJ_AT_TOS;
 
 	/* TODO - check claim that taking address of b harms performance */
-	/* TODO - consider optimization search for eePtr->constants */
+	/* TODO - consider optimization search for constants */
 	result = TclGetBooleanFromObj(NULL, valuePtr, &b);
 	if (result != TCL_OK) {
 	    TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n", O2S(valuePtr),
@@ -5379,7 +5469,7 @@
 	    goto checkForCatch;
 	}
 	/* TODO: Consider peephole opt. */
-	objResultPtr = eePtr->constants[!b];
+	objResultPtr = constants[!b];
 	NEXT_INST_F(1, 1, 1);
     }
 
@@ -5813,7 +5903,7 @@
 	 * to the operand. Push the current stack depth onto the special catch
 	 * stack.
 	 */
-	eePtr->stackPtr[++catchTop] = (Tcl_Obj *) CURR_DEPTH;
+	*(++catchTop) = CURR_DEPTH;
 	TRACE(("%u => catchTop=%d, stackTop=%d\n",
 		TclGetUInt4AtPtr(pc+1), (catchTop - initCatchTop - 1),
 		(int) CURR_DEPTH));
@@ -6185,7 +6275,7 @@
 	}
 	TRACE_APPEND(("\"%.30s\" \"%.30s\" %d",
 		O2S(OBJ_UNDER_TOS), O2S(OBJ_AT_TOS), done));
-	objResultPtr = eePtr->constants[done];
+	objResultPtr = constants[done];
 	/* TODO: consider opt like INST_FOREACH_STEP4 */
 	NEXT_INST_F(5, 0, 1);
 
@@ -6509,9 +6599,10 @@
 	 */
 
 	while ((expandNestList != NULL) && ((catchTop == initCatchTop) ||
-		((ptrdiff_t) eePtr->stackPtr[catchTop] <=
+		(*catchTop <=
 		(ptrdiff_t) expandNestList->internalRep.twoPtrValue.ptr1))) {
 	    Tcl_Obj *objPtr = expandNestList->internalRep.twoPtrValue.ptr2;
+
 	    TclDecrRefCount(expandNestList);
 	    expandNestList = objPtr;
 	}
@@ -6565,7 +6656,7 @@
 	 */
 
 	processCatch:
-	while (CURR_DEPTH > ((ptrdiff_t) (eePtr->stackPtr[catchTop]))) {
+	while (CURR_DEPTH > *catchTop) {
 	    valuePtr = POP_OBJECT();
 	    TclDecrRefCount(valuePtr);
 	}
@@ -6573,7 +6664,7 @@
 	if (traceInstructions) {
 	    fprintf(stdout, "  ... found catch at %d, catchTop=%d, unwound to %ld, new pc %u\n",
 		    rangePtr->codeOffset, (catchTop - initCatchTop - 1),
-		    (long) eePtr->stackPtr[catchTop],
+		    (long) *catchTop,
 		    (unsigned int)(rangePtr->catchOffset));
 	}
 #endif
@@ -6592,7 +6683,7 @@
 
 	abnormalReturn:
 	{
-	    while (CURR_DEPTH > initStackDepth) {
+	    while (tosPtr > initTosPtr) {
 		Tcl_Obj *objPtr = POP_OBJECT();
 		Tcl_DecrRefCount(objPtr);
 	    }
@@ -6606,16 +6697,21 @@
 		TclDecrRefCount(expandNestList);
 		expandNestList = objPtr;
 	    }
-	    if (CURR_DEPTH < initStackDepth) {
+	    if (tosPtr < initTosPtr) {
 		fprintf(stderr, "\nTclExecuteByteCode: abnormal return at pc %u: stack top %d < entry stack top %d\n",
 			(unsigned int)(pc - codePtr->codeStart),
 			(unsigned int) CURR_DEPTH,
-			(unsigned int) initStackDepth);
+			(unsigned int) 0);
 		Tcl_Panic("TclExecuteByteCode execution failure: end stack top < start stack top");
 	    }
-	    eePtr->tosPtr = eePtr->stackPtr + initStackDepth - codePtr->maxExceptDepth;
 	}
     }
+
+    /*
+     * Restore the stack to the state it had previous to this bytecode. 
+     */
+
+    TclStackFree(interp);
     return result;
 #undef iPtr
 }
Index: generic/tclInt.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclInt.h,v
retrieving revision 1.311
diff -u -r1.311 tclInt.h
--- generic/tclInt.h	30 May 2007 18:12:58 -0000	1.311
+++ generic/tclInt.h	5 Jun 2007 17:42:28 -0000
@@ -1183,20 +1183,30 @@
 	struct CompileEnv *compEnvPtr, ClientData clientData);
 
 /*
+ * The data structure for a (linked list of) execution stacks.
+ */
+
+typedef struct ExecStack {
+    struct ExecStack *prevPtr;
+    struct ExecStack *nextPtr;
+    Tcl_Obj **markerPtr;
+    Tcl_Obj **endPtr;
+    Tcl_Obj **tosPtr;
+    Tcl_Obj *stackWords[1];
+} ExecStack;
+
+
+/*
  * The data structure defining the execution environment for ByteCode's.
  * There is one ExecEnv structure per Tcl interpreter. It holds the evaluation
  * stack that holds command operands and results. The stack grows towards
- * increasing addresses. The "stackTop" member is cached by TclExecuteByteCode
- * in a local variable: it must be set before calling TclExecuteByteCode and
- * will be restored by TclExecuteByteCode before it returns.
+ * increasing addresses. The member stackPtr points to the stackItems of the
+ * currently active execution stack.
  */
 
 typedef struct ExecEnv {
-    Tcl_Obj **stackPtr;		/* Points to the first item in the evaluation
+    ExecStack *execStackPtr;	/* Points to the first item in the evaluation
 				 * stack on the heap. */
-    Tcl_Obj **tosPtr;		/* Points to current top of stack;
-				 * (stackPtr-1) when the stack is empty. */
-    Tcl_Obj **endPtr;		/* Points to last usable item in stack. */
     Tcl_Obj *constants[2];	/* Pointers to constant "0" and "1" objs. */
 } ExecEnv;
 
