Subject: Many improvements to code generation of efficient break and continue.

From: dkf <[email-masked]>
Date: Sun, 2 Jun 2013 17:41:14 +0000

---
 generic/tclCompCmds.c   |  93 +++++++++++-----------------
 generic/tclCompCmdsSZ.c |  11 ++--
 generic/tclCompile.c    | 160 ++++++++++++++++++++++++++++++++++++++++++++----
 generic/tclCompile.h    |  52 ++++++++++++++--
 4 files changed, 235 insertions(+), 81 deletions(-)

diff --git a/generic/tclCompCmds.c b/generic/tclCompCmds.c
index 6e5d187..f2d2963 100644
--- a/generic/tclCompCmds.c
+++ b/generic/tclCompCmds.c
@@ -480,8 +480,8 @@ TclCompileBreakCmd(
 				 * compiled. */
     CompileEnv *envPtr)		/* Holds resulting instructions. */
 {
-    int depth;
     ExceptionRange *rangePtr;
+    ExceptionAux *auxPtr;
 
     if (parsePtr->numWords != 1) {
 	return TCL_ERROR;
@@ -491,9 +491,9 @@ TclCompileBreakCmd(
      * Find the innermost exception range that contains this command.
      */
 
-    rangePtr = TclGetInnermostExceptionRange(envPtr, &depth);
+    rangePtr = TclGetInnermostExceptionRange(envPtr, &auxPtr);
     if (rangePtr && rangePtr->type == LOOP_EXCEPTION_RANGE) {
-	int toPop = envPtr->currStackDepth - depth;
+	int toPop = envPtr->currStackDepth - auxPtr->stackDepth;
 
 	/*
 	 * Pop off the extra stack frames.
@@ -501,26 +501,18 @@ TclCompileBreakCmd(
 
 	while (toPop > 0) {
 	    TclEmitOpcode(INST_POP, envPtr);
-	    toPop--;
-#ifdef TCL_COMPILE_DEBUG
-	    /*
-	     * Instructions that raise exceptions don't really have to follow
-	     * the usual stack management rules.  But the checker wants them
-	     * followed, so lie about stack usage to make it happy.
-	     */
 	    TclAdjustStackDepth(1, envPtr);
-#endif
+	    toPop--;
 	}
 
-	if (envPtr->expandCount == 0 && rangePtr->breakOffset != -1) {
-	    int offset = (rangePtr->breakOffset - CurrentOffset(envPtr));
-
+	if (envPtr->expandCount == 0) {
 	    /*
 	     * Found the target! Also, no built-up expansion stack. No need
 	     * for a nasty INST_BREAK here.
 	     */
 
-	    TclEmitInstInt4(INST_JUMP4, offset, envPtr);
+	    TclAddLoopBreakFixup(envPtr, auxPtr);
+	    TclEmitInstInt4(INST_JUMP4, 0, envPtr);
 	    goto done;
 	}
     }
@@ -532,14 +524,12 @@ TclCompileBreakCmd(
     TclEmitOpcode(INST_BREAK, envPtr);
 
   done:
-#ifdef TCL_COMPILE_DEBUG
     /*
-     * Instructions that raise exceptions don't really have to follow
-     * the usual stack management rules.  But the checker wants them
-     * followed, so lie about stack usage to make it happy.
+     * Instructions that raise exceptions don't really have to follow the
+     * usual stack management rules, but the cleanup code does.
      */
+
     TclAdjustStackDepth(1, envPtr);
-#endif
     return TCL_OK;
 }
 
@@ -645,7 +635,7 @@ TclCompileCatchCmd(
      * uses.
      */
 
-    range = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
 
     /*
      * If the body is a simple word, compile a BEGIN_CATCH instruction,
@@ -833,8 +823,8 @@ TclCompileContinueCmd(
 				 * compiled. */
     CompileEnv *envPtr)		/* Holds resulting instructions. */
 {
-    int depth;
     ExceptionRange *rangePtr;
+    ExceptionAux *auxPtr;
 
     /*
      * There should be no argument after the "continue".
@@ -849,9 +839,9 @@ TclCompileContinueCmd(
      * innermost containing exception range.
      */
 
-    rangePtr = TclGetInnermostExceptionRange(envPtr, &depth);
+    rangePtr = TclGetInnermostExceptionRange(envPtr, &auxPtr);
     if (rangePtr && rangePtr->type == LOOP_EXCEPTION_RANGE) {
-	int toPop = envPtr->currStackDepth - depth;
+	int toPop = envPtr->currStackDepth - auxPtr->stackDepth;
 
 	/*
 	 * Pop off the extra stack frames.
@@ -859,26 +849,18 @@ TclCompileContinueCmd(
 
 	while (toPop > 0) {
 	    TclEmitOpcode(INST_POP, envPtr);
-	    toPop--;
-#ifdef TCL_COMPILE_DEBUG
-	    /*
-	     * Instructions that raise exceptions don't really have to follow
-	     * the usual stack management rules.  But the checker wants them
-	     * followed, so lie about stack usage to make it happy.
-	     */
 	    TclAdjustStackDepth(1, envPtr);
-#endif
+	    toPop--;
 	}
 
-	if (envPtr->expandCount == 0 && rangePtr->continueOffset != -1) {
-	    int offset = (rangePtr->continueOffset - CurrentOffset(envPtr));
-
+	if (envPtr->expandCount == 0) {
 	    /*
 	     * Found the target! Also, no built-up expansion stack. No need
 	     * for a nasty INST_CONTINUE here.
 	     */
 
-	    TclEmitInstInt4(INST_JUMP4, offset, envPtr);
+	    TclAddLoopContinueFixup(envPtr, auxPtr);
+	    TclEmitInstInt4(INST_JUMP4, 0, envPtr);
 	    goto done;
 	}
     }
@@ -890,14 +872,12 @@ TclCompileContinueCmd(
     TclEmitOpcode(INST_CONTINUE, envPtr);
 
   done:
-#ifdef TCL_COMPILE_DEBUG
     /*
-     * Instructions that raise exceptions don't really have to follow
-     * the usual stack management rules.  But the checker wants them
-     * followed, so lie about stack usage to make it happy.
+     * Instructions that raise exceptions don't really have to follow the
+     * usual stack management rules, but the cleanup code does.
      */
+
     TclAdjustStackDepth(1, envPtr);
-#endif
     return TCL_OK;
 }
 
@@ -1350,7 +1330,7 @@ TclCompileDictMergeCmd(
      * For each of the remaining dictionaries...
      */
 
-    outLoop = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+    outLoop = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
     TclEmitInstInt4(		INST_BEGIN_CATCH4, outLoop,	envPtr);
     ExceptionRangeStarts(envPtr, outLoop);
     for (i=2 ; i<parsePtr->numWords ; i++) {
@@ -1563,7 +1543,7 @@ CompileDictEachCmd(
      * started by Tcl_DictObjFirst above.
      */
 
-    catchRange = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+    catchRange = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
     TclEmitInstInt4(	INST_BEGIN_CATCH4, catchRange,		envPtr);
     ExceptionRangeStarts(envPtr, catchRange);
 
@@ -1581,7 +1561,7 @@ CompileDictEachCmd(
      * Set up the loop exception targets.
      */
 
-    loopRange = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE);
+    loopRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
     ExceptionRangeStarts(envPtr, loopRange);
 
     /*
@@ -1629,6 +1609,7 @@ CompileDictEachCmd(
      */
 
     ExceptionRangeTarget(envPtr, loopRange, breakOffset);
+    TclFinalizeLoopExceptionRange(envPtr, loopRange);
     TclEmitInstInt1(	INST_UNSET_SCALAR, 0,			envPtr);
     TclEmitInt4(		infoIndex,			envPtr);
     TclEmitOpcode(	INST_END_CATCH,				envPtr);
@@ -1807,7 +1788,7 @@ TclCompileDictUpdateCmd(
     TclEmitInstInt4(	INST_DICT_UPDATE_START, dictIndex,	envPtr);
     TclEmitInt4(	infoIndex,				envPtr);
 
-    range = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
     TclEmitInstInt4(	INST_BEGIN_CATCH4, range,		envPtr);
 
     ExceptionRangeStarts(envPtr, range);
@@ -2164,7 +2145,7 @@ TclCompileDictWithCmd(
      * Now the body of the [dict with].
      */
 
-    range = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
     TclEmitInstInt4(		INST_BEGIN_CATCH4, range,	envPtr);
 
     ExceptionRangeStarts(envPtr, range);
@@ -2446,15 +2427,6 @@ TclCompileForCmd(
     }
 
     /*
-     * Create ExceptionRange records for the body and the "next" command. The
-     * "next" command's ExceptionRange supports break but not continue (and
-     * has a -1 continueOffset).
-     */
-
-    bodyRange = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE);
-    nextRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
-
-    /*
      * Inline compile the initial command.
      */
 
@@ -2480,6 +2452,7 @@ TclCompileForCmd(
      * Compile the loop body.
      */
 
+    bodyRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
     bodyCodeOffset = ExceptionRangeStarts(envPtr, bodyRange);
     SetLineInformation(4);
     CompileBody(envPtr, bodyTokenPtr, interp);
@@ -2488,9 +2461,12 @@ TclCompileForCmd(
     TclEmitOpcode(INST_POP, envPtr);
 
     /*
-     * Compile the "next" subcommand.
+     * Compile the "next" subcommand. Note that this exception range will not
+     * have a continueOffset (other than -1) connected to it; it won't trap
+     * TCL_CONTINUE but rather just TCL_BREAK.
      */
 
+    nextRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
     envPtr->currStackDepth = savedStackDepth;
     nextCodeOffset = ExceptionRangeStarts(envPtr, nextRange);
     SetLineInformation(3);
@@ -2538,6 +2514,8 @@ TclCompileForCmd(
 
     ExceptionRangeTarget(envPtr, bodyRange, breakOffset);
     ExceptionRangeTarget(envPtr, nextRange, breakOffset);
+    TclFinalizeLoopExceptionRange(envPtr, bodyRange);
+    TclFinalizeLoopExceptionRange(envPtr, nextRange);
 
     /*
      * The for command's result is an empty string.
@@ -2829,7 +2807,7 @@ CompileEachloopCmd(
      * Create an exception record to handle [break] and [continue].
      */
 
-    range = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE);
+    range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
 
     /*
      * Evaluate then store each value list in the associated temporary.
@@ -2935,6 +2913,7 @@ CompileEachloopCmd(
      */
 
     ExceptionRangeTarget(envPtr, range, breakOffset);
+    TclFinalizeLoopExceptionRange(envPtr, range);
 
     /*
      * The command's result is an empty string if not collecting, or the
diff --git a/generic/tclCompCmdsSZ.c b/generic/tclCompCmdsSZ.c
index 4f4286e..721f59a 100644
--- a/generic/tclCompCmdsSZ.c
+++ b/generic/tclCompCmdsSZ.c
@@ -834,7 +834,7 @@ TclSubstCompile(
 	}
 
 	envPtr->line = bline;
-	catchRange = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+	catchRange = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
 	OP4(	BEGIN_CATCH4, catchRange);
 	ExceptionRangeStarts(envPtr, catchRange);
 
@@ -2302,7 +2302,7 @@ IssueTryInstructions(
      * (and it's never called when there's a finally clause).
      */
 
-    range = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
     OP4(				BEGIN_CATCH4, range);
     ExceptionRangeStarts(envPtr, range);
     BODY(				bodyToken, 1);
@@ -2455,7 +2455,7 @@ IssueTryFinallyInstructions(
      * (if any trap matches) and run a finally clause.
      */
 
-    range = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+    range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
     OP4(				BEGIN_CATCH4, range);
     ExceptionRangeStarts(envPtr, range);
     envPtr->currStackDepth = savedStackDepth;
@@ -2522,7 +2522,7 @@ IssueTryFinallyInstructions(
 	     */
 
 	    if (resultVars[i] >= 0 || handlerTokens[i]) {
-		range = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
+		range = TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
 		OP4(			BEGIN_CATCH4, range);
 		ExceptionRangeStarts(envPtr, range);
 	    }
@@ -2833,7 +2833,7 @@ TclCompileWhileCmd(
      * implement break and continue.
      */
 
-    range = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE);
+    range = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
 
     /*
      * Jump to the evaluation of the condition. This code uses the "loop
@@ -2917,6 +2917,7 @@ TclCompileWhileCmd(
     envPtr->exceptArrayPtr[range].continueOffset = testCodeOffset;
     envPtr->exceptArrayPtr[range].codeOffset = bodyCodeOffset;
     ExceptionRangeTarget(envPtr, range, breakOffset);
+    TclFinalizeLoopExceptionRange(envPtr, range);
 
     /*
      * The while command's result is an empty string.
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index c56b67f..96f8683 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -1514,7 +1514,7 @@ TclInitCompileEnv(
     envPtr->mallocedLiteralArray = 0;
 
     envPtr->exceptArrayPtr = envPtr->staticExceptArraySpace;
-    envPtr->exnStackDepthArrayPtr = envPtr->staticExnStackDepthArraySpace;
+    envPtr->exceptAuxArrayPtr = envPtr->staticExAuxArraySpace;
     envPtr->exceptArrayNext = 0;
     envPtr->exceptArrayEnd = COMPILEENV_INIT_EXCEPT_RANGES;
     envPtr->mallocedExceptArray = 0;
@@ -1728,7 +1728,7 @@ TclFreeCompileEnv(
     }
     if (envPtr->mallocedExceptArray) {
 	ckfree(envPtr->exceptArrayPtr);
-	ckfree(envPtr->exnStackDepthArrayPtr);
+	ckfree(envPtr->exceptAuxArrayPtr);
     }
     if (envPtr->mallocedCmdMap) {
 	ckfree(envPtr->cmdMapPtr);
@@ -3413,6 +3413,7 @@ TclCreateExceptRange(
 				 * new ExceptionRange structure. */
 {
     register ExceptionRange *rangePtr;
+    register ExceptionAux *auxPtr;
     int index = envPtr->exceptArrayNext;
 
     if (index >= envPtr->exceptArrayEnd) {
@@ -3424,16 +3425,16 @@ TclCreateExceptRange(
 
 	size_t currBytes =
 		envPtr->exceptArrayNext * sizeof(ExceptionRange);
-	size_t currBytes2 = envPtr->exceptArrayNext * sizeof(int);
+	size_t currBytes2 = envPtr->exceptArrayNext * sizeof(ExceptionAux);
 	int newElems = 2*envPtr->exceptArrayEnd;
 	size_t newBytes = newElems * sizeof(ExceptionRange);
-	size_t newBytes2 = newElems * sizeof(int);
+	size_t newBytes2 = newElems * sizeof(ExceptionAux);
 
 	if (envPtr->mallocedExceptArray) {
 	    envPtr->exceptArrayPtr =
 		    ckrealloc(envPtr->exceptArrayPtr, newBytes);
-	    envPtr->exnStackDepthArrayPtr =
-		    ckrealloc(envPtr->exnStackDepthArrayPtr, newBytes2);
+	    envPtr->exceptAuxArrayPtr =
+		    ckrealloc(envPtr->exceptAuxArrayPtr, newBytes2);
 	} else {
 	    /*
 	     * envPtr->exceptArrayPtr isn't a ckalloc'd pointer, so we must
@@ -3441,12 +3442,12 @@ TclCreateExceptRange(
 	     */
 
 	    ExceptionRange *newPtr = ckalloc(newBytes);
-	    int *newPtr2 = ckalloc(newBytes2);
+	    ExceptionAux *newPtr2 = ckalloc(newBytes2);
 
 	    memcpy(newPtr, envPtr->exceptArrayPtr, currBytes);
-	    memcpy(newPtr2, envPtr->exnStackDepthArrayPtr, currBytes2);
+	    memcpy(newPtr2, envPtr->exceptAuxArrayPtr, currBytes2);
 	    envPtr->exceptArrayPtr = newPtr;
-	    envPtr->exnStackDepthArrayPtr = newPtr2;
+	    envPtr->exceptAuxArrayPtr = newPtr2;
 	    envPtr->mallocedExceptArray = 1;
 	}
 	envPtr->exceptArrayEnd = newElems;
@@ -3461,7 +3462,14 @@ TclCreateExceptRange(
     rangePtr->breakOffset = -1;
     rangePtr->continueOffset = -1;
     rangePtr->catchOffset = -1;
-    envPtr->exnStackDepthArrayPtr[index] = envPtr->currStackDepth;
+    auxPtr = &envPtr->exceptAuxArrayPtr[index];
+    auxPtr->stackDepth = envPtr->currStackDepth;
+    auxPtr->numBreakTargets = 0;
+    auxPtr->breakTargets = NULL;
+    auxPtr->allocBreakTargets = 0;
+    auxPtr->numContinueTargets = 0;
+    auxPtr->continueTargets = NULL;
+    auxPtr->allocContinueTargets = 0;
     return index;
 }
 
@@ -3482,7 +3490,7 @@ TclCreateExceptRange(
 ExceptionRange *
 TclGetInnermostExceptionRange(
     CompileEnv *envPtr,
-    int *stackDepthPtr)
+    ExceptionAux **auxPtrPtr)
 {
     int exnIdx = -1, i;
 
@@ -3498,12 +3506,122 @@ TclGetInnermostExceptionRange(
     if (exnIdx == -1) {
 	return NULL;
     }
-    if (stackDepthPtr) {
-	*stackDepthPtr = envPtr->exnStackDepthArrayPtr[exnIdx];
+    if (auxPtrPtr) {
+	*auxPtrPtr = &envPtr->exceptAuxArrayPtr[exnIdx];
     }
     return &envPtr->exceptArrayPtr[exnIdx];
 }
 
+void
+TclAddLoopBreakFixup(
+    CompileEnv *envPtr,
+    ExceptionAux *auxPtr)
+{
+    int range = auxPtr - envPtr->exceptAuxArrayPtr;
+
+    if (envPtr->exceptArrayPtr[range].type != LOOP_EXCEPTION_RANGE) {
+	Tcl_Panic("trying to add 'break' fixup to full exception range");
+    }
+
+    if (++auxPtr->numBreakTargets > auxPtr->allocBreakTargets) {
+	auxPtr->allocBreakTargets *= 2;
+	auxPtr->allocBreakTargets += 2;
+	if (auxPtr->breakTargets) {
+	    auxPtr->breakTargets = ckrealloc(auxPtr->breakTargets,
+		    sizeof(int) * auxPtr->allocBreakTargets);
+	} else {
+	    auxPtr->breakTargets =
+		    ckalloc(sizeof(int) * auxPtr->allocBreakTargets);
+	}
+    }
+    auxPtr->breakTargets[auxPtr->numBreakTargets - 1] = CurrentOffset(envPtr);
+}
+
+void
+TclAddLoopContinueFixup(
+    CompileEnv *envPtr,
+    ExceptionAux *auxPtr)
+{
+    int range = auxPtr - envPtr->exceptAuxArrayPtr;
+
+    if (envPtr->exceptArrayPtr[range].type != LOOP_EXCEPTION_RANGE) {
+	Tcl_Panic("trying to add 'continue' fixup to full exception range");
+    }
+
+    if (++auxPtr->numContinueTargets > auxPtr->allocContinueTargets) {
+	auxPtr->allocContinueTargets *= 2;
+	auxPtr->allocContinueTargets += 2;
+	if (auxPtr->continueTargets) {
+	    auxPtr->continueTargets = ckrealloc(auxPtr->continueTargets,
+		    sizeof(int) * auxPtr->allocContinueTargets);
+	} else {
+	    auxPtr->continueTargets =
+		    ckalloc(sizeof(int) * auxPtr->allocContinueTargets);
+	}
+    }
+    auxPtr->continueTargets[auxPtr->numContinueTargets - 1] =
+	    CurrentOffset(envPtr);
+}
+
+void
+TclFinalizeLoopExceptionRange(
+    CompileEnv *envPtr,
+    int range)
+{
+    ExceptionRange *rangePtr = &envPtr->exceptArrayPtr[range];
+    ExceptionAux *auxPtr = &envPtr->exceptAuxArrayPtr[range];
+    int i, offset;
+    unsigned char *site;
+
+    if (rangePtr->type != LOOP_EXCEPTION_RANGE) {
+	Tcl_Panic("trying to finalize a loop exception range");
+    }
+
+    /*
+     * Do the jump fixups. Note that these are always issued as INST_JUMP4 so
+     * there is no need to fuss around with updating code offsets.
+     */
+
+    for (i=0 ; i<auxPtr->numBreakTargets ; i++) {
+	site = envPtr->codeStart + auxPtr->breakTargets[i];
+	offset = rangePtr->breakOffset - auxPtr->breakTargets[i];
+	TclUpdateInstInt4AtPc(INST_JUMP4, offset, site);
+    }
+    for (i=0 ; i<auxPtr->numContinueTargets ; i++) {
+	site = envPtr->codeStart + auxPtr->continueTargets[i];
+	if (rangePtr->continueOffset == -1) {
+	    int j;
+
+	    /*
+	     * WTF? Can't bind, so revert to an INST_CONTINUE.
+	     */
+
+	    *site = INST_CONTINUE;
+	    for (j=0 ; j<4 ; j++) {
+		*++site = INST_NOP;
+	    }
+	} else {
+	    offset = rangePtr->continueOffset - auxPtr->continueTargets[i];
+	    TclUpdateInstInt4AtPc(INST_JUMP4, offset, site);
+	}
+    }
+
+    /*
+     * Drop the arrays we were holding the only reference to.
+     */
+
+    if (auxPtr->breakTargets) {
+	ckfree(auxPtr->breakTargets);
+	auxPtr->breakTargets = NULL;
+	auxPtr->numBreakTargets = 0;
+    }
+    if (auxPtr->continueTargets) {
+	ckfree(auxPtr->continueTargets);
+	auxPtr->continueTargets = NULL;
+	auxPtr->numContinueTargets = 0;
+    }
+}
+
 /*
  *----------------------------------------------------------------------
  *
@@ -3864,6 +3982,22 @@ TclFixupForwardJump(
 	}
     }
 
+    for (k = 0 ; k < envPtr->exceptArrayNext ; k++) {
+	ExceptionAux *auxPtr = &envPtr->exceptAuxArrayPtr[k];
+	int i;
+
+	for (i=0 ; i<auxPtr->numBreakTargets ; i++) {
+	    if (jumpFixupPtr->codeOffset < auxPtr->breakTargets[i]) {
+		auxPtr->breakTargets[i] += 3;
+	    }
+	}
+	for (i=0 ; i<auxPtr->numContinueTargets ; i++) {
+	    if (jumpFixupPtr->codeOffset < auxPtr->continueTargets[i]) {
+		auxPtr->continueTargets[i] += 3;
+	    }
+	}
+    }
+
     /*
      * TIP #280: Adjust the mapping from PC values to the per-command
      * information about arguments and their line numbers.
diff --git a/generic/tclCompile.h b/generic/tclCompile.h
index c9cbbd4..8430da3 100644
--- a/generic/tclCompile.h
+++ b/generic/tclCompile.h
@@ -100,6 +100,38 @@ typedef struct ExceptionRange {
 } ExceptionRange;
 
 /*
+ * Auxiliary data used when issuing (currently just loop) exception ranges,
+ * but which is not required during execution.
+ */
+
+typedef struct ExceptionAux {
+    int stackDepth;		/* The stack depth at the point where the
+				 * exception range was created. This is used
+				 * to calculate the number of POPs required to
+				 * restore the stack to its prior state. */
+    int numBreakTargets;	/* The number of [break]s that want to be
+				 * targeted to the place where this loop
+				 * exception will be bound to. */
+    int *breakTargets;		/* The offsets of the INST_JUMP4 instructions
+				 * issued by the [break]s that we must
+				 * update. Note that resizing a jump (via
+				 * TclFixupForwardJump) can cause the contents
+				 * of this array to be updated. When
+				 * numBreakTargets==0, this is NULL. */
+    int allocBreakTargets;	/* The size of the breakTargets array. */
+    int numContinueTargets;	/* The number of [continue]s that want to be
+				 * targeted to the place where this loop
+				 * exception will be bound to. */
+    int *continueTargets;	/* The offsets of the INST_JUMP4 instructions
+				 * issued by the [continue]s that we must
+				 * update. Note that resizing a jump (via
+				 * TclFixupForwardJump) can cause the contents
+				 * of this array to be updated. When
+				 * numContinueTargets==0, this is NULL. */
+    int allocContinueTargets;	/* The size of the continueTargets array. */
+} ExceptionAux;
+
+/*
  * Structure used to map between instruction pc and source locations. It
  * defines for each compiled Tcl command its code's starting offset and its
  * source's starting offset and length. Note that the code offset increases
@@ -275,9 +307,11 @@ typedef struct CompileEnv {
 				 * entry. */
     int mallocedExceptArray;	/* 1 if ExceptionRange array was expanded and
 				 * exceptArrayPtr points in heap, else 0. */
-    int *exnStackDepthArrayPtr;	/* Array of stack depths to restore to when
-				 * processing BREAK/CONTINUE exceptions. Must
-				 * be the same size as the exceptArrayPtr. */
+    ExceptionAux *exceptAuxArrayPtr;
+				/* Array of information used to restore the
+				 * state when processing BREAK/CONTINUE
+				 * exceptions. Must be the same size as the
+				 * exceptArrayPtr. */
     CmdLocation *cmdMapPtr;	/* Points to start of CmdLocation array.
 				 * numCommands is the index of the next entry
 				 * to use; (numCommands-1) is the entry index
@@ -299,8 +333,8 @@ typedef struct CompileEnv {
 				/* Initial storage of LiteralEntry array. */
     ExceptionRange staticExceptArraySpace[COMPILEENV_INIT_EXCEPT_RANGES];
 				/* Initial ExceptionRange array storage. */
-    int staticExnStackDepthArraySpace[COMPILEENV_INIT_EXCEPT_RANGES];
-				/* Initial static except stack depth array
+    ExceptionAux staticExAuxArraySpace[COMPILEENV_INIT_EXCEPT_RANGES];
+				/* Initial static except auxiliary info array
 				 * storage. */
     CmdLocation staticCmdMapSpace[COMPILEENV_INIT_CMD_MAP_SIZE];
 				/* Initial storage for cmd location map. */
@@ -995,7 +1029,13 @@ MODULE_SCOPE void	TclInitCompileEnv(Tcl_Interp *interp,
 MODULE_SCOPE void	TclInitJumpFixupArray(JumpFixupArray *fixupArrayPtr);
 MODULE_SCOPE void	TclInitLiteralTable(LiteralTable *tablePtr);
 MODULE_SCOPE ExceptionRange *TclGetInnermostExceptionRange(CompileEnv *envPtr,
-			    int *depthPtr);
+			    ExceptionAux **auxPtrPtr);
+MODULE_SCOPE void	TclAddLoopBreakFixup(CompileEnv *envPtr,
+			    ExceptionAux *auxPtr);
+MODULE_SCOPE void	TclAddLoopContinueFixup(CompileEnv *envPtr,
+			    ExceptionAux *auxPtr);
+MODULE_SCOPE void	TclFinalizeLoopExceptionRange(CompileEnv *envPtr,
+			    int range);
 #ifdef TCL_COMPILE_STATS
 MODULE_SCOPE char *	TclLiteralStats(LiteralTable *tablePtr);
 MODULE_SCOPE int	TclLog2(int value);
-- 
cgit v0.12