Subject: Getting better at doing more efficient break/continue instruction handling.

From: dkf <[email-masked]>
Date: Sat, 1 Jun 2013 21:05:55 +0000

---
 generic/tclCompCmds.c | 113 ++++++++++++++++++++++++++++----------------------
 generic/tclCompile.c  |  42 +++++++++++++++++++
 generic/tclCompile.h  |   6 +++
 3 files changed, 111 insertions(+), 50 deletions(-)

diff --git a/generic/tclCompCmds.c b/generic/tclCompCmds.c
index 1f99a22..6e5d187 100644
--- a/generic/tclCompCmds.c
+++ b/generic/tclCompCmds.c
@@ -480,7 +480,7 @@ TclCompileBreakCmd(
 				 * compiled. */
     CompileEnv *envPtr)		/* Holds resulting instructions. */
 {
-    int i, exnIdx;
+    int depth;
     ExceptionRange *rangePtr;
 
     if (parsePtr->numWords != 1) {
@@ -488,33 +488,40 @@ TclCompileBreakCmd(
     }
 
     /*
-     * Find the innermost exception range that contains this command. Relies
-     * on the fact that the range has a numCodeBytes = -1 when it is being
-     * populated and that inner ranges come after outer ranges.
+     * Find the innermost exception range that contains this command.
      */
 
-    exnIdx = -1;
-    for (i=0 ; i<envPtr->exceptArrayNext ; i++) {
-	rangePtr = &envPtr->exceptArrayPtr[i];
-	if (envPtr->codeStart+rangePtr->codeOffset <= envPtr->codeNext
-		&& rangePtr->numCodeBytes == -1) {
-	    exnIdx = i;
+    rangePtr = TclGetInnermostExceptionRange(envPtr, &depth);
+    if (rangePtr && rangePtr->type == LOOP_EXCEPTION_RANGE) {
+	int toPop = envPtr->currStackDepth - depth;
+
+	/*
+	 * Pop off the extra stack frames.
+	 */
+
+	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
 	}
-    }
-    if (exnIdx != -1) {
-	rangePtr = &envPtr->exceptArrayPtr[exnIdx];
-	if (rangePtr->type == LOOP_EXCEPTION_RANGE) {
-	    int toPop = envPtr->currStackDepth -
-		    envPtr->exnStackDepthArrayPtr[exnIdx];
+
+	if (envPtr->expandCount == 0 && rangePtr->breakOffset != -1) {
+	    int offset = (rangePtr->breakOffset - CurrentOffset(envPtr));
 
 	    /*
-	     * Pop off the extra stack frames.
+	     * Found the target! Also, no built-up expansion stack. No need
+	     * for a nasty INST_BREAK here.
 	     */
 
-	    while (toPop > 0) {
-		TclEmitOpcode(INST_POP, envPtr);
-		toPop--;
-	    }
+	    TclEmitInstInt4(INST_JUMP4, offset, envPtr);
+	    goto done;
 	}
     }
 
@@ -523,6 +530,8 @@ TclCompileBreakCmd(
      */
 
     TclEmitOpcode(INST_BREAK, envPtr);
+
+  done:
 #ifdef TCL_COMPILE_DEBUG
     /*
      * Instructions that raise exceptions don't really have to follow
@@ -824,7 +833,7 @@ TclCompileContinueCmd(
 				 * compiled. */
     CompileEnv *envPtr)		/* Holds resulting instructions. */
 {
-    int i, exnIdx;
+    int depth;
     ExceptionRange *rangePtr;
 
     /*
@@ -837,45 +846,40 @@ TclCompileContinueCmd(
 
     /*
      * See if we can find a valid continueOffset (i.e., not -1) in the
-     * innermost containing exception range. Relies on the fact that the range
-     * has a numCodeBytes = -1 when it is being populated and that inner
-     * ranges come after outer ranges.
+     * innermost containing exception range.
      */
 
-    exnIdx = -1;
-    for (i=0 ; i<envPtr->exceptArrayNext ; i++) {
-	rangePtr = &envPtr->exceptArrayPtr[i];
-	if (envPtr->codeStart+rangePtr->codeOffset <= envPtr->codeNext
-		&& rangePtr->numCodeBytes == -1) {
-	    exnIdx = i;
-	}
-    }
-    if (exnIdx >= 0) {
-	rangePtr = &envPtr->exceptArrayPtr[exnIdx];
-	if (rangePtr->type == LOOP_EXCEPTION_RANGE) {
-	    int toPop = envPtr->currStackDepth -
-		    envPtr->exnStackDepthArrayPtr[exnIdx];
+    rangePtr = TclGetInnermostExceptionRange(envPtr, &depth);
+    if (rangePtr && rangePtr->type == LOOP_EXCEPTION_RANGE) {
+	int toPop = envPtr->currStackDepth - depth;
+
+	/*
+	 * Pop off the extra stack frames.
+	 */
 
+	while (toPop > 0) {
+	    TclEmitOpcode(INST_POP, envPtr);
+	    toPop--;
+#ifdef TCL_COMPILE_DEBUG
 	    /*
-	     * Pop off the extra stack frames.
+	     * 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.
 	     */
-
-	    while (toPop > 0) {
-		TclEmitOpcode(INST_POP, envPtr);
-		toPop--;
-	    }
+	    TclAdjustStackDepth(1, envPtr);
+#endif
 	}
-	if (rangePtr->type == LOOP_EXCEPTION_RANGE
-		&& rangePtr->continueOffset != -1) {
+
+	if (envPtr->expandCount == 0 && rangePtr->continueOffset != -1) {
 	    int offset = (rangePtr->continueOffset - CurrentOffset(envPtr));
 
 	    /*
-	     * Found the target! No need for a nasty INST_CONTINUE here.
+	     * Found the target! Also, no built-up expansion stack. No need
+	     * for a nasty INST_CONTINUE here.
 	     */
 
 	    TclEmitInstInt4(INST_JUMP4, offset, envPtr);
-	    PushStringLiteral(envPtr, "");	/* Evil hack! */
-	    return TCL_OK;
+	    goto done;
 	}
     }
 
@@ -884,7 +888,16 @@ TclCompileContinueCmd(
      */
 
     TclEmitOpcode(INST_CONTINUE, envPtr);
-    PushStringLiteral(envPtr, "");	/* Evil hack! */
+
+  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.
+     */
+    TclAdjustStackDepth(1, envPtr);
+#endif
     return TCL_OK;
 }
 
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index 54946ee..c56b67f 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -1523,6 +1523,7 @@ TclInitCompileEnv(
     envPtr->cmdMapEnd = COMPILEENV_INIT_CMD_MAP_SIZE;
     envPtr->mallocedCmdMap = 0;
     envPtr->atCmdStart = 1;
+    envPtr->expandCount = 0;
 
     /*
      * TIP #280: Set up the extended command location information, based on
@@ -2060,6 +2061,7 @@ TclCompileScript(
 
 	    if (expand) {
 		TclEmitOpcode(INST_EXPAND_START, envPtr);
+		envPtr->expandCount++;
 	    }
 
 	    /*
@@ -2279,6 +2281,7 @@ TclCompileScript(
 		 */
 
 		TclEmitOpcode(INST_INVOKE_EXPANDED, envPtr);
+		envPtr->expandCount--;
 		TclAdjustStackDepth(1 - wordIdx, envPtr);
 	    } else if (wordIdx > 0) {
 		/*
@@ -3463,6 +3466,45 @@ TclCreateExceptRange(
 }
 
 /*
+ * ---------------------------------------------------------------------
+ *
+ * TclGetInnermostExceptionRange --
+ *
+ *	Returns the innermost exception range that covers the current code
+ *	creation point, and (optionally) the stack depth that is expected at
+ *	that point. Relies on the fact that the range has a numCodeBytes = -1
+ *	when it is being populated and that inner ranges come after outer
+ *	ranges.
+ *
+ * ---------------------------------------------------------------------
+ */
+
+ExceptionRange *
+TclGetInnermostExceptionRange(
+    CompileEnv *envPtr,
+    int *stackDepthPtr)
+{
+    int exnIdx = -1, i;
+
+    for (i=0 ; i<envPtr->exceptArrayNext ; i++) {
+	ExceptionRange *rangePtr = &envPtr->exceptArrayPtr[i];
+
+	if (CurrentOffset(envPtr) >= rangePtr->codeOffset &&
+		(rangePtr->numCodeBytes == -1 || CurrentOffset(envPtr) <
+			rangePtr->codeOffset+rangePtr->numCodeBytes)) {
+	    exnIdx = i;
+	}
+    }
+    if (exnIdx == -1) {
+	return NULL;
+    }
+    if (stackDepthPtr) {
+	*stackDepthPtr = envPtr->exnStackDepthArrayPtr[exnIdx];
+    }
+    return &envPtr->exceptArrayPtr[exnIdx];
+}
+
+/*
  *----------------------------------------------------------------------
  *
  * TclCreateAuxData --
diff --git a/generic/tclCompile.h b/generic/tclCompile.h
index c380823..c9cbbd4 100644
--- a/generic/tclCompile.h
+++ b/generic/tclCompile.h
@@ -318,6 +318,10 @@ typedef struct CompileEnv {
 				 * inefficient. If set to 2, that instruction
 				 * should not be issued at all (by the generic
 				 * part of the command compiler). */
+    int expandCount;		/* Number of INST_EXPAND_START instructions
+				 * encountered that have not yet been paired
+				 * with a corresponding
+				 * INST_INVOKE_EXPANDED. */
     ContLineLoc *clLoc;		/* If not NULL, the table holding the
 				 * locations of the invisible continuation
 				 * lines in the input script, to adjust the
@@ -990,6 +994,8 @@ MODULE_SCOPE void	TclInitCompileEnv(Tcl_Interp *interp,
 			    int numBytes, const CmdFrame *invoker, int word);
 MODULE_SCOPE void	TclInitJumpFixupArray(JumpFixupArray *fixupArrayPtr);
 MODULE_SCOPE void	TclInitLiteralTable(LiteralTable *tablePtr);
+MODULE_SCOPE ExceptionRange *TclGetInnermostExceptionRange(CompileEnv *envPtr,
+			    int *depthPtr);
 #ifdef TCL_COMPILE_STATS
 MODULE_SCOPE char *	TclLiteralStats(LiteralTable *tablePtr);
 MODULE_SCOPE int	TclLog2(int value);
-- 
cgit v0.12