summaryrefslogtreecommitdiffstats
path: root/generic
diff options
context:
space:
mode:
authordkf <donal.k.fellows@manchester.ac.uk>2025-05-02 15:54:41 (GMT)
committerdkf <donal.k.fellows@manchester.ac.uk>2025-05-02 15:54:41 (GMT)
commite33638fe9dee868660175527228ced07e94de5c3 (patch)
tree2d6c3f273d8cc06c7acf0b865f50b35fc03a37e5 /generic
parentc815194539f2bcc4a94ab20a37e2358246d6f860 (diff)
parent93069ee44f5962ef803b537691b3330e104506fd (diff)
downloadtcl-e33638fe9dee868660175527228ced07e94de5c3.zip
tcl-e33638fe9dee868660175527228ced07e94de5c3.tar.gz
tcl-e33638fe9dee868660175527228ced07e94de5c3.tar.bz2
Combine the new jumptable into this branch
Diffstat (limited to 'generic')
-rw-r--r--generic/tclAssembly.c416
-rw-r--r--generic/tclCompCmds.c2
-rw-r--r--generic/tclCompCmdsSZ.c855
-rw-r--r--generic/tclCompile.c15
-rw-r--r--generic/tclCompile.h94
-rw-r--r--generic/tclExecute.c52
-rw-r--r--generic/tclOptimize.c13
7 files changed, 1149 insertions, 298 deletions
diff --git a/generic/tclAssembly.c b/generic/tclAssembly.c
index e8e466c..68e3f1b 100644
--- a/generic/tclAssembly.c
+++ b/generic/tclAssembly.c
@@ -101,6 +101,8 @@ typedef struct BasicBlock {
* ranges belonging to embedded scripts and
* expressions in this block */
JumptableInfo* jtPtr; /* Jump table at the end of this basic block */
+ JumptableNumInfo* jtnPtr; /* Numeric jump table at the end of this basic
+ * block */
int flags; /* Boolean flags */
} BasicBlock;
@@ -264,10 +266,13 @@ static ByteCode * CompileAssembleObj(Tcl_Interp *interp,
static void CompileEmbeddedScript(AssemblyEnv*, Tcl_Token*,
const TalInstDesc*);
static int DefineLabel(AssemblyEnv* envPtr, const char* label);
-static void DeleteMirrorJumpTable(JumptableInfo* jtPtr);
+static void DeleteMirrorJumpTable(JumptableInfo* jtPtr,
+ JumptableNumInfo* jtnPtr);
static void FillInJumpOffsets(AssemblyEnv*);
static int CreateMirrorJumpTable(AssemblyEnv* assemEnvPtr,
- Tcl_Obj* jumpTable);
+ Tcl_Size objc, Tcl_Obj** objv);
+static int CreateMirrorNumJumpTable(AssemblyEnv* assemEnvPtr,
+ Tcl_Size objc, Tcl_Obj** objv);
static size_t FindLocalVar(AssemblyEnv* envPtr,
Tcl_Token** tokenPtrPtr);
static int FinishAssembly(AssemblyEnv*);
@@ -395,6 +400,7 @@ static const TalInstDesc TalInstructionTable[] = {
// For legacy code
{"jumpFalse4", ASSEM_JUMP, INST_JUMP_FALSE, 1, 0},
{"jumpTable", ASSEM_JUMPTABLE,INST_JUMP_TABLE, 1, 0},
+ {"jumpTableNum", ASSEM_JUMPTABLE,INST_JUMP_TABLE_NUM, 1, 0},
{"jumpTrue", ASSEM_JUMP, INST_JUMP_TRUE, 1, 0},
// For legacy code
{"jumpTrue4", ASSEM_JUMP, INST_JUMP_TRUE, 1, 0},
@@ -1172,9 +1178,10 @@ FreeAssemblyEnv(
Tcl_Free(thisBB->foreignExceptions);
}
nextBB = thisBB->successor1;
- if (thisBB->jtPtr != NULL) {
- DeleteMirrorJumpTable(thisBB->jtPtr);
+ if (thisBB->jtPtr || thisBB->jtnPtr) {
+ DeleteMirrorJumpTable(thisBB->jtPtr, thisBB->jtnPtr);
thisBB->jtPtr = NULL;
+ thisBB->jtnPtr = NULL;
}
Tcl_Free(thisBB);
}
@@ -1227,7 +1234,6 @@ AssembleOneLine(
int litIndex; /* Literal pool index of a constant */
Tcl_Size localVar; /* LVT index of a local variable */
int flags; /* Flags for a basic block */
- JumptableInfo* jtPtr; /* Pointer to a jumptable */
Tcl_Size infoIndex; /* Index of the jumptable in auxdata */
int status = TCL_ERROR; /* Return value from this function */
@@ -1480,7 +1486,10 @@ AssembleOneLine(
StartBasicBlock(assemEnvPtr, flags, operand1Obj);
break;
- case ASSEM_JUMPTABLE:
+ case ASSEM_JUMPTABLE: {
+ Tcl_Size jtObjc;
+ Tcl_Obj **jtObjv;
+
if (parsePtr->numWords != 2) {
Tcl_WrongNumArgs(interp, 1, &instNameObj, "table");
goto cleanup;
@@ -1488,25 +1497,54 @@ AssembleOneLine(
if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
goto cleanup;
}
+ if (TclListObjGetElements(interp, operand1Obj, &jtObjc, &jtObjv) != TCL_OK) {
+ goto cleanup;
+ }
+ if (jtObjc % 2 != 0) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "jump table must have an even number of list elements",
+ TCL_AUTO_LENGTH));
+ Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADJUMPTABLE", (char *)NULL);
+ goto cleanup;
+ }
+
+ if (TalInstructionTable[tblIdx].tclInstCode == INST_JUMP_TABLE) {
+ JumptableInfo* jtPtr = AllocJumptable();
+
+ assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
+ assemEnvPtr->curr_bb->jumpOffset = CurrentOffset(envPtr);
+ DEBUG_PRINT("bb %p jumpLine %d jumpOffset %d\n",
+ assemEnvPtr->curr_bb, assemEnvPtr->cmdLine,
+ CurrentOffset(envPtr));
+
+ infoIndex = RegisterJumptable(jtPtr, envPtr);
+ DEBUG_PRINT("auxdata index=%d\n", infoIndex);
+
+ BBEmitInstInt4(assemEnvPtr, tblIdx, infoIndex, 0);
+ if (CreateMirrorJumpTable(assemEnvPtr, jtObjc, jtObjv) != TCL_OK) {
+ goto cleanup;
+ }
+ } else {
+ JumptableNumInfo* jtnPtr = AllocJumptableNum();
- jtPtr = (JumptableInfo*)Tcl_Alloc(sizeof(JumptableInfo));
-
- Tcl_InitHashTable(&jtPtr->hashTable, TCL_STRING_KEYS);
- assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
- assemEnvPtr->curr_bb->jumpOffset = CurrentOffset(envPtr);
- DEBUG_PRINT("bb %p jumpLine %d jumpOffset %d\n",
- assemEnvPtr->curr_bb, assemEnvPtr->cmdLine,
- CurrentOffset(envPtr));
+ assert(TalInstructionTable[tblIdx].tclInstCode == INST_JUMP_TABLE_NUM);
+ assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
+ assemEnvPtr->curr_bb->jumpOffset = CurrentOffset(envPtr);
+ DEBUG_PRINT("bb %p jumpLine %d jumpOffset %d\n",
+ assemEnvPtr->curr_bb, assemEnvPtr->cmdLine,
+ CurrentOffset(envPtr));
- infoIndex = TclCreateAuxData(jtPtr, &tclJumptableInfoType, envPtr);
- DEBUG_PRINT("auxdata index=%d\n", infoIndex);
+ infoIndex = RegisterJumptableNum(jtnPtr, envPtr);
+ DEBUG_PRINT("auxdata index=%d\n", infoIndex);
- BBEmitInstInt4(assemEnvPtr, tblIdx, infoIndex, 0);
- if (CreateMirrorJumpTable(assemEnvPtr, operand1Obj) != TCL_OK) {
- goto cleanup;
+ BBEmitInstInt4(assemEnvPtr, tblIdx, infoIndex, 0);
+ if (CreateMirrorNumJumpTable(assemEnvPtr, jtObjc, jtObjv) != TCL_OK) {
+ goto cleanup;
+ }
}
StartBasicBlock(assemEnvPtr, BB_JUMPTABLE|BB_FALLTHRU, NULL);
break;
+ }
case ASSEM_LABEL:
if (parsePtr->numWords != 2) {
@@ -1858,8 +1896,8 @@ MoveExceptionRangesToBasicBlock(
curr_bb, exceptionCount, savedExceptArrayNext);
curr_bb->foreignExceptionBase = savedExceptArrayNext;
curr_bb->foreignExceptionCount = exceptionCount;
- curr_bb->foreignExceptions =
- (ExceptionRange*)Tcl_Alloc(exceptionCount * sizeof(ExceptionRange));
+ curr_bb->foreignExceptions = (ExceptionRange*)
+ Tcl_Alloc(exceptionCount * sizeof(ExceptionRange));
memcpy(curr_bb->foreignExceptions,
envPtr->exceptArrayPtr + savedExceptArrayNext,
exceptionCount * sizeof(ExceptionRange));
@@ -1890,10 +1928,9 @@ MoveExceptionRangesToBasicBlock(
static int
CreateMirrorJumpTable(
AssemblyEnv* assemEnvPtr, /* Assembly environment */
- Tcl_Obj* jumps) /* List of alternating keywords and labels */
+ Tcl_Size objc, /* Number of elements in the 'jumps' list */
+ Tcl_Obj** objv) /* Pointers to the elements in the list */
{
- Tcl_Size objc; /* Number of elements in the 'jumps' list */
- Tcl_Obj** objv; /* Pointers to the elements in the list */
CompileEnv* envPtr = assemEnvPtr->envPtr;
/* Compilation environment */
Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
@@ -1901,35 +1938,94 @@ CreateMirrorJumpTable(
BasicBlock* bbPtr = assemEnvPtr->curr_bb;
/* Current basic block */
JumptableInfo* jtPtr;
- Tcl_HashTable* jtHashPtr; /* Hashtable in the JumptableInfo */
- Tcl_HashEntry* hashEntry; /* Entry for a key in the hashtable */
+ Tcl_HashEntry* hPtr; /* Entry for a key in the hashtable */
int isNew; /* Flag==1 if the key is not yet in the
* table. */
Tcl_Size i;
- if (TclListObjLength(interp, jumps, &objc) != TCL_OK) {
- return TCL_ERROR;
- }
- if (objc % 2 != 0) {
- if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "jump table must have an even number of list elements",
- -1));
- Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADJUMPTABLE", (char *)NULL);
+ /*
+ * Allocate the jumptable. Don't write to BB until we know we aren't going
+ * to fail the build of the table.
+ */
+
+ jtPtr = AllocJumptable();
+
+ /*
+ * Fill the keys and labels into the table.
+ */
+
+ DEBUG_PRINT("jump table {\n");
+ for (i = 0; i < objc; i+=2) {
+ DEBUG_PRINT(" %s -> %s\n", TclGetString(objv[i]),
+ TclGetString(objv[i+1]));
+ hPtr = Tcl_CreateHashEntry(&jtPtr->hashTable, TclGetString(objv[i]),
+ &isNew);
+ if (!isNew) {
+ if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "duplicate entry in jump table for \"%s\"",
+ TclGetString(objv[i])));
+ Tcl_SetErrorCode(interp, "TCL", "ASSEM", "DUPJUMPTABLEENTRY", (char *)NULL);
+ }
+ DeleteMirrorJumpTable(jtPtr, NULL);
+ return TCL_ERROR;
}
- return TCL_ERROR;
- }
- if (TclListObjGetElements(interp, jumps, &objc, &objv) != TCL_OK) {
- return TCL_ERROR;
+ Tcl_SetHashValue(hPtr, objv[i+1]);
+ Tcl_IncrRefCount(objv[i+1]);
}
+ DEBUG_PRINT("}\n");
/*
- * Allocate the jumptable.
+ * Put the mirror jumptable in the basic block struct.
*/
- jtPtr = (JumptableInfo*)Tcl_Alloc(sizeof(JumptableInfo));
- jtHashPtr = &jtPtr->hashTable;
- Tcl_InitHashTable(jtHashPtr, TCL_STRING_KEYS);
+ bbPtr->jtPtr = jtPtr;
+ return TCL_OK;
+}
+
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CreateMirrorNumJumpTable --
+ *
+ * Makes a jump table with comparison values and assembly code labels.
+ *
+ * Results:
+ * Returns a standard Tcl status, with an error message in the
+ * interpreter on error.
+ *
+ * Side effects:
+ * Initializes the jump table pointer in the current basic block to a
+ * JumptableNumInfo. The keys in the JumptableNumInfo are the comparison
+ * integers. The values, instead of being jump displacements, are
+ * Tcl_Obj's with the code labels.
+ */
+
+static int
+CreateMirrorNumJumpTable(
+ AssemblyEnv* assemEnvPtr, /* Assembly environment */
+ Tcl_Size objc, /* Number of elements in the 'jumps' list */
+ Tcl_Obj** objv) /* Pointers to the elements in the list */
+{
+ CompileEnv* envPtr = assemEnvPtr->envPtr;
+ /* Compilation environment */
+ Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+ /* Tcl interpreter */
+ BasicBlock* bbPtr = assemEnvPtr->curr_bb;
+ /* Current basic block */
+ JumptableNumInfo* jtnPtr;
+ Tcl_HashEntry* hPtr; /* Entry for a key in the hashtable */
+ int isNew; /* Flag==1 if the key is not yet in the
+ * table. */
+ Tcl_Size i;
+ Tcl_WideInt key;
+
+ /*
+ * Allocate the jumptable. Don't write to BB until we know we aren't going
+ * to fail the build of the table.
+ */
+
+ jtnPtr = AllocJumptableNum();
/*
* Fill the keys and labels into the table.
@@ -1939,19 +2035,26 @@ CreateMirrorJumpTable(
for (i = 0; i < objc; i+=2) {
DEBUG_PRINT(" %s -> %s\n", TclGetString(objv[i]),
TclGetString(objv[i+1]));
- hashEntry = Tcl_CreateHashEntry(jtHashPtr, TclGetString(objv[i]),
- &isNew);
+ if (Tcl_GetWideIntFromObj(NULL, objv[i], &key) != TCL_OK) {
+ if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "jump table must have 64-bit integer keys",
+ TCL_AUTO_LENGTH));
+ Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADJUMPTABLEENTRY", (char *)NULL);
+ }
+ goto error;
+ }
+ hPtr = Tcl_CreateHashEntry(&jtnPtr->hashTable, (void*)key, &isNew);
if (!isNew) {
if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"duplicate entry in jump table for \"%s\"",
TclGetString(objv[i])));
Tcl_SetErrorCode(interp, "TCL", "ASSEM", "DUPJUMPTABLEENTRY", (char *)NULL);
- DeleteMirrorJumpTable(jtPtr);
- return TCL_ERROR;
}
+ goto error;
}
- Tcl_SetHashValue(hashEntry, objv[i+1]);
+ Tcl_SetHashValue(hPtr, objv[i+1]);
Tcl_IncrRefCount(objv[i+1]);
}
DEBUG_PRINT("}\n");
@@ -1960,8 +2063,12 @@ CreateMirrorJumpTable(
* Put the mirror jumptable in the basic block struct.
*/
- bbPtr->jtPtr = jtPtr;
+ bbPtr->jtnPtr = jtnPtr;
return TCL_OK;
+
+ error:
+ DeleteMirrorJumpTable(NULL, jtnPtr);
+ return TCL_ERROR;
}
/*
@@ -1976,23 +2083,38 @@ CreateMirrorJumpTable(
static void
DeleteMirrorJumpTable(
- JumptableInfo* jtPtr)
+ JumptableInfo* jtPtr,
+ JumptableNumInfo* jtnPtr)
{
- Tcl_HashTable* jtHashPtr = &jtPtr->hashTable;
- /* Hash table pointer */
+ Tcl_HashTable* hashPtr; /* Hash table pointer */
Tcl_HashSearch search; /* Hash search control */
Tcl_HashEntry* entry; /* Hash table entry containing a jump label */
Tcl_Obj* label; /* Jump label from the hash table */
- for (entry = Tcl_FirstHashEntry(jtHashPtr, &search);
- entry != NULL;
- entry = Tcl_NextHashEntry(&search)) {
- label = (Tcl_Obj*)Tcl_GetHashValue(entry);
- Tcl_DecrRefCount(label);
- Tcl_SetHashValue(entry, NULL);
+ if (jtPtr) {
+ hashPtr = &jtPtr->hashTable;
+ for (entry = Tcl_FirstHashEntry(hashPtr, &search);
+ entry != NULL;
+ entry = Tcl_NextHashEntry(&search)) {
+ label = (Tcl_Obj*)Tcl_GetHashValue(entry);
+ Tcl_DecrRefCount(label);
+ Tcl_SetHashValue(entry, NULL);
+ }
+ Tcl_DeleteHashTable(hashPtr);
+ Tcl_Free(jtPtr);
+ }
+ if (jtnPtr) {
+ hashPtr = &jtnPtr->hashTable;
+ for (entry = Tcl_FirstHashEntry(hashPtr, &search);
+ entry != NULL;
+ entry = Tcl_NextHashEntry(&search)) {
+ label = (Tcl_Obj*)Tcl_GetHashValue(entry);
+ Tcl_DecrRefCount(label);
+ Tcl_SetHashValue(entry, NULL);
+ }
+ Tcl_DeleteHashTable(hashPtr);
+ Tcl_Free(jtnPtr);
}
- Tcl_DeleteHashTable(jtHashPtr);
- Tcl_Free(jtPtr);
}
/*
@@ -2591,6 +2713,7 @@ AllocBB(
bb->foreignExceptionCount = 0;
bb->foreignExceptions = NULL;
bb->jtPtr = NULL;
+ bb->jtnPtr = NULL;
bb->flags = 0;
return bb;
@@ -2749,8 +2872,7 @@ CheckJumpTableLabels(
AssemblyEnv* assemEnvPtr, /* Assembly environment */
BasicBlock* bbPtr) /* Basic block that ends in a jump table */
{
- Tcl_HashTable* symHash = &bbPtr->jtPtr->hashTable;
- /* Hash table with the symbols */
+ Tcl_HashTable* symHash; /* Hash table with the symbols */
Tcl_HashSearch search; /* Hash table iterator */
Tcl_HashEntry* symEntryPtr; /* Hash entry for the symbols */
Tcl_Obj* symbolObj; /* Jump target */
@@ -2761,18 +2883,37 @@ CheckJumpTableLabels(
*/
DEBUG_PRINT("check jump table labels %p {\n", bbPtr);
- for (symEntryPtr = Tcl_FirstHashEntry(symHash, &search);
- symEntryPtr != NULL;
- symEntryPtr = Tcl_NextHashEntry(&search)) {
- symbolObj = (Tcl_Obj*)Tcl_GetHashValue(symEntryPtr);
- valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
- TclGetString(symbolObj));
- DEBUG_PRINT(" %s -> %s (%d)\n",
- (char *)Tcl_GetHashKey(symHash, symEntryPtr),
- TclGetString(symbolObj), (valEntryPtr != NULL));
- if (valEntryPtr == NULL) {
- ReportUndefinedLabel(assemEnvPtr, bbPtr, symbolObj);
- return TCL_ERROR;
+ if (bbPtr->jtPtr) {
+ symHash = &bbPtr->jtPtr->hashTable;
+ for (symEntryPtr = Tcl_FirstHashEntry(symHash, &search);
+ symEntryPtr != NULL;
+ symEntryPtr = Tcl_NextHashEntry(&search)) {
+ symbolObj = (Tcl_Obj*)Tcl_GetHashValue(symEntryPtr);
+ valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+ TclGetString(symbolObj));
+ DEBUG_PRINT(" %s -> %s (%d)\n",
+ (char *)Tcl_GetHashKey(symHash, symEntryPtr),
+ TclGetString(symbolObj), (valEntryPtr != NULL));
+ if (valEntryPtr == NULL) {
+ ReportUndefinedLabel(assemEnvPtr, bbPtr, symbolObj);
+ return TCL_ERROR;
+ }
+ }
+ } else {
+ symHash = &bbPtr->jtnPtr->hashTable;
+ for (symEntryPtr = Tcl_FirstHashEntry(symHash, &search);
+ symEntryPtr != NULL;
+ symEntryPtr = Tcl_NextHashEntry(&search)) {
+ symbolObj = (Tcl_Obj*)Tcl_GetHashValue(symEntryPtr);
+ valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+ TclGetString(symbolObj));
+ DEBUG_PRINT(" %" TCL_SIZE_MODIFIER "d -> %s (%d)\n",
+ (Tcl_Size)Tcl_GetHashKey(symHash, symEntryPtr),
+ TclGetString(symbolObj), (valEntryPtr != NULL));
+ if (valEntryPtr == NULL) {
+ ReportUndefinedLabel(assemEnvPtr, bbPtr, symbolObj);
+ return TCL_ERROR;
+ }
}
}
DEBUG_PRINT("}\n");
@@ -2877,15 +3018,11 @@ ResolveJumpTableTargets(
{
CompileEnv* envPtr = assemEnvPtr->envPtr;
/* Compilation environment */
- Tcl_HashTable* symHash = &bbPtr->jtPtr->hashTable;
- /* Hash table with the symbols */
+ Tcl_HashTable* symHash; /* Hash table with the symbols */
Tcl_HashSearch search; /* Hash table iterator */
Tcl_HashEntry* symEntryPtr; /* Hash entry for the symbols */
Tcl_Obj* symbolObj; /* Jump target */
Tcl_HashEntry* valEntryPtr; /* Hash entry for the resolutions */
- int auxDataIndex; /* Index of the auxdata */
- JumptableInfo* realJumpTablePtr;
- /* Jump table in the actual code */
Tcl_HashTable* realJumpHashPtr;
/* Jump table hash in the actual code */
Tcl_HashEntry* realJumpEntryPtr;
@@ -2895,38 +3032,90 @@ ResolveJumpTableTargets(
/* Basic block that the jump proceeds to */
int junk;
- auxDataIndex = TclGetInt4AtPtr(envPtr->codeStart + bbPtr->jumpOffset + 1);
- DEBUG_PRINT("bbPtr = %p jumpOffset = %d auxDataIndex = %d\n",
- bbPtr, bbPtr->jumpOffset, auxDataIndex);
- realJumpTablePtr = (JumptableInfo*)TclFetchAuxData(envPtr, auxDataIndex);
- realJumpHashPtr = &realJumpTablePtr->hashTable;
+ if (bbPtr->jtPtr) {
+ int auxDataIndex; /* Index of the auxdata */
+ JumptableInfo* realJumpTablePtr;
+ /* Jump table in the actual code */
- /*
- * Look up every jump target in the jump hash.
- */
+ symHash = &bbPtr->jtPtr->hashTable;
+ auxDataIndex = TclGetInt4AtPtr(
+ envPtr->codeStart + bbPtr->jumpOffset + 1);
+ DEBUG_PRINT("bbPtr = %p jumpOffset = %d auxDataIndex = %d\n",
+ bbPtr, bbPtr->jumpOffset, auxDataIndex);
+ realJumpTablePtr = (JumptableInfo*)
+ TclFetchAuxData(envPtr, auxDataIndex);
+ realJumpHashPtr = &realJumpTablePtr->hashTable;
- DEBUG_PRINT("resolve jump table {\n");
- for (symEntryPtr = Tcl_FirstHashEntry(symHash, &search);
- symEntryPtr != NULL;
- symEntryPtr = Tcl_NextHashEntry(&search)) {
- symbolObj = (Tcl_Obj*)Tcl_GetHashValue(symEntryPtr);
- DEBUG_PRINT(" symbol %s\n", TclGetString(symbolObj));
-
- valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
- TclGetString(symbolObj));
- jumpTargetBBPtr = (BasicBlock*)Tcl_GetHashValue(valEntryPtr);
-
- realJumpEntryPtr = Tcl_CreateHashEntry(realJumpHashPtr,
- Tcl_GetHashKey(symHash, symEntryPtr), &junk);
- DEBUG_PRINT(" %s -> %s -> bb %p (pc %d) hash entry %p\n",
- (char *)Tcl_GetHashKey(symHash, symEntryPtr),
- TclGetString(symbolObj), jumpTargetBBPtr,
- jumpTargetBBPtr->startOffset, realJumpEntryPtr);
-
- Tcl_SetHashValue(realJumpEntryPtr,
- INT2PTR(jumpTargetBBPtr->startOffset - bbPtr->jumpOffset));
- }
- DEBUG_PRINT("}\n");
+ /*
+ * Look up every jump target in the jump hash.
+ */
+
+ DEBUG_PRINT("resolve jump table {\n");
+ for (symEntryPtr = Tcl_FirstHashEntry(symHash, &search);
+ symEntryPtr != NULL;
+ symEntryPtr = Tcl_NextHashEntry(&search)) {
+ symbolObj = (Tcl_Obj*)Tcl_GetHashValue(symEntryPtr);
+ DEBUG_PRINT(" symbol %s\n", TclGetString(symbolObj));
+
+ valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+ TclGetString(symbolObj));
+ jumpTargetBBPtr = (BasicBlock*)Tcl_GetHashValue(valEntryPtr);
+
+ realJumpEntryPtr = Tcl_CreateHashEntry(realJumpHashPtr,
+ Tcl_GetHashKey(symHash, symEntryPtr), &junk);
+ DEBUG_PRINT(" %s -> %s -> bb %p (pc %d) hash entry %p\n",
+ (char *)Tcl_GetHashKey(symHash, symEntryPtr),
+ TclGetString(symbolObj), jumpTargetBBPtr,
+ jumpTargetBBPtr->startOffset, realJumpEntryPtr);
+
+ Tcl_SetHashValue(realJumpEntryPtr,
+ INT2PTR(jumpTargetBBPtr->startOffset - bbPtr->jumpOffset));
+ }
+ DEBUG_PRINT("}\n");
+ } else {
+ int auxDataIndex; /* Index of the auxdata */
+ JumptableNumInfo* realNumJumpTablePtr;
+ /* Jump table in the actual code */
+
+ assert(bbPtr->jtnPtr);
+ symHash = &bbPtr->jtnPtr->hashTable;
+ auxDataIndex = TclGetInt4AtPtr(
+ envPtr->codeStart + bbPtr->jumpOffset + 1);
+ DEBUG_PRINT("bbPtr = %p jumpOffset = %d auxDataIndex = %d\n",
+ bbPtr, bbPtr->jumpOffset, auxDataIndex);
+ realNumJumpTablePtr = (JumptableNumInfo*)
+ TclFetchAuxData(envPtr, auxDataIndex);
+ realJumpHashPtr = &realNumJumpTablePtr->hashTable;
+
+ /*
+ * Look up every jump target in the jump hash.
+ */
+
+ DEBUG_PRINT("resolve jump table {\n");
+ for (symEntryPtr = Tcl_FirstHashEntry(symHash, &search);
+ symEntryPtr != NULL;
+ symEntryPtr = Tcl_NextHashEntry(&search)) {
+ symbolObj = (Tcl_Obj*)Tcl_GetHashValue(symEntryPtr);
+ DEBUG_PRINT(" symbol %s\n", TclGetString(symbolObj));
+
+ valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+ TclGetString(symbolObj));
+ jumpTargetBBPtr = (BasicBlock*)Tcl_GetHashValue(valEntryPtr);
+
+ realJumpEntryPtr = Tcl_CreateHashEntry(realJumpHashPtr,
+ Tcl_GetHashKey(symHash, symEntryPtr), &junk);
+ DEBUG_PRINT(
+ " %" TCL_SIZE_MODIFIER "d -> %s -> bb %p (pc %d)"
+ " hash entry %p\n",
+ (Tcl_Size) Tcl_GetHashKey(symHash, symEntryPtr),
+ TclGetString(symbolObj), jumpTargetBBPtr,
+ jumpTargetBBPtr->startOffset, realJumpEntryPtr);
+
+ Tcl_SetHashValue(realJumpEntryPtr,
+ INT2PTR(jumpTargetBBPtr->startOffset - bbPtr->jumpOffset));
+ }
+ DEBUG_PRINT("}\n");
+ }
}
/*
@@ -3303,8 +3492,9 @@ StackCheckBasicBlock(
*/
if (blockPtr->flags & BB_JUMPTABLE) {
- for (jtEntry = Tcl_FirstHashEntry(&blockPtr->jtPtr->hashTable,
- &jtSearch);
+ Tcl_HashTable *tablePtr = (blockPtr->jtPtr ?
+ &blockPtr->jtPtr->hashTable : &blockPtr->jtnPtr->hashTable);
+ for (jtEntry = Tcl_FirstHashEntry(tablePtr, &jtSearch);
result == TCL_OK && jtEntry != NULL;
jtEntry = Tcl_NextHashEntry(&jtSearch)) {
targetLabel = (Tcl_Obj*)Tcl_GetHashValue(jtEntry);
@@ -3625,7 +3815,9 @@ ProcessCatchesInBasicBlock(
*/
if (bbPtr->flags & BB_JUMPTABLE) {
- for (jtEntry = Tcl_FirstHashEntry(&bbPtr->jtPtr->hashTable,&jtSearch);
+ Tcl_HashTable *tablePtr = (bbPtr->jtPtr ?
+ &bbPtr->jtPtr->hashTable : &bbPtr->jtnPtr->hashTable);
+ for (jtEntry = Tcl_FirstHashEntry(tablePtr, &jtSearch);
result == TCL_OK && jtEntry != NULL;
jtEntry = Tcl_NextHashEntry(&jtSearch)) {
targetLabel = (Tcl_Obj*)Tcl_GetHashValue(jtEntry);
diff --git a/generic/tclCompCmds.c b/generic/tclCompCmds.c
index 8983ed1..6fbf7fa 100644
--- a/generic/tclCompCmds.c
+++ b/generic/tclCompCmds.c
@@ -101,6 +101,8 @@ TclGetAuxDataType(
return &dictUpdateInfoType;
} else if (!strcmp(typeName, tclJumptableInfoType.name)) {
return &tclJumptableInfoType;
+ } else if (!strcmp(typeName, tclJumptableNumericInfoType.name)) {
+ return &tclJumptableNumericInfoType;
}
return NULL;
}
diff --git a/generic/tclCompCmdsSZ.c b/generic/tclCompCmdsSZ.c
index 8056c9b..327c54a 100644
--- a/generic/tclCompCmdsSZ.c
+++ b/generic/tclCompCmdsSZ.c
@@ -50,6 +50,9 @@ static AuxDataDupProc DupJumptableInfo;
static AuxDataFreeProc FreeJumptableInfo;
static AuxDataPrintProc PrintJumptableInfo;
static AuxDataPrintProc DisassembleJumptableInfo;
+static AuxDataDupProc DupJumptableNumInfo;
+static AuxDataPrintProc PrintJumptableNumInfo;
+static AuxDataPrintProc DisassembleJumptableNumInfo;
static int CompileAssociativeBinaryOpCmd(Tcl_Interp *interp,
Tcl_Parse *parsePtr, const char *identity,
int instruction, CompileEnv *envPtr);
@@ -71,10 +74,18 @@ static void IssueSwitchJumpTable(Tcl_Interp *interp,
static int IssueTryClausesInstructions(Tcl_Interp *interp,
CompileEnv *envPtr, Tcl_Token *bodyToken,
Tcl_Size numHandlers, TryHandlerInfo *handlers);
+static int IssueTryTraplessClausesInstructions(Tcl_Interp *interp,
+ CompileEnv *envPtr, Tcl_Token *bodyToken,
+ Tcl_Size numHandlers, TryHandlerInfo *handlers);
static int IssueTryClausesFinallyInstructions(Tcl_Interp *interp,
CompileEnv *envPtr, Tcl_Token *bodyToken,
Tcl_Size numHandlers, TryHandlerInfo *handlers,
Tcl_Token *finallyToken);
+static int IssueTryTraplessClausesFinallyInstructions(
+ Tcl_Interp *interp, CompileEnv *envPtr,
+ Tcl_Token *bodyToken,
+ Tcl_Size numHandlers, TryHandlerInfo *handlers,
+ Tcl_Token *finallyToken);
static int IssueTryFinallyInstructions(Tcl_Interp *interp,
CompileEnv *envPtr, Tcl_Token *bodyToken,
Tcl_Token *finallyToken);
@@ -90,6 +101,14 @@ const AuxDataType tclJumptableInfoType = {
PrintJumptableInfo, /* printProc */
DisassembleJumptableInfo /* disassembleProc */
};
+
+const AuxDataType tclJumptableNumericInfoType = {
+ "JumptableNumInfo", /* name */
+ DupJumptableNumInfo, /* dupProc */
+ FreeJumptableInfo, /* freeProc */
+ PrintJumptableNumInfo, /* printProc */
+ DisassembleJumptableNumInfo /* disassembleProc */
+};
/*
*----------------------------------------------------------------------
@@ -1503,8 +1522,9 @@ TclSubstCompile(
Tcl_Size length;
int literal;
Tcl_ExceptionRange catchRange;
- Tcl_BytecodeLabel end, haveOk, haveReturn, haveBreak, haveContinue;
- Tcl_BytecodeLabel haveOther;
+ Tcl_BytecodeLabel end, haveOk, haveOther, tableBase;
+ JumptableNumInfo *retCodeTable;
+ Tcl_AuxDataRef tableIdx;
char buf[4] = "";
switch (tokenPtr->type) {
@@ -1602,58 +1622,41 @@ TclSubstCompile(
/* Exceptional return codes processed here */
CATCH_TARGET( catchRange);
- OP( PUSH_RETURN_OPTIONS);
- OP( PUSH_RESULT);
OP( PUSH_RETURN_CODE);
- OP( END_CATCH);
- OP( RETURN_CODE_BRANCH);
+
+ retCodeTable = AllocJumptableNum();
+ tableIdx = RegisterJumptableNum(retCodeTable, envPtr);
+ tableBase = CurrentOffset(envPtr);
+ OP4( JUMP_TABLE_NUM, tableIdx);
+ FWDJUMP( JUMP, haveOther);
/* ERROR -> reraise it; NB: can't require BREAK/CONTINUE handling */
+ CreateJumptableNumEntryToHere(retCodeTable, TCL_ERROR, tableBase);
+ OP( PUSH_RETURN_OPTIONS);
+ OP( PUSH_RESULT);
+ OP( END_CATCH); // catchRange
OP( RETURN_STK);
- OP( NOP);
- OP( NOP);
- OP( NOP);
- OP( NOP);
-
- /* RETURN */
- FWDJUMP( JUMP, haveReturn);
-
- /* BREAK */
- FWDJUMP( JUMP, haveBreak);
-
- /* CONTINUE */
- FWDJUMP( JUMP, haveContinue);
-
- /* OTHER */
- FWDJUMP( JUMP, haveOther);
+ STKDELTA(-1);
- STKDELTA(+1);
/* BREAK destination */
- FWDLABEL( haveBreak);
- OP( POP);
- OP( POP);
-
+ CreateJumptableNumEntryToHere(retCodeTable, TCL_BREAK, tableBase);
+ OP( END_CATCH); // catchRange
BACKJUMP( JUMP, breakOffset);
- STKDELTA(+2);
/* CONTINUE destination */
- FWDLABEL( haveContinue);
- OP( POP);
- OP( POP);
+ CreateJumptableNumEntryToHere(retCodeTable, TCL_CONTINUE, tableBase);
+ OP( END_CATCH); // catchRange
FWDJUMP( JUMP, end);
- STKDELTA(+2);
/* RETURN + other destination */
- FWDLABEL( haveReturn);
FWDLABEL( haveOther);
+ OP( PUSH_RESULT);
+ OP( END_CATCH); // catchRange
/*
* Pull the result to top of stack, discard options dict.
*/
- OP( SWAP);
- OP( POP);
-
/* OK destination */
FWDLABEL( haveOk);
if (count > 1) {
@@ -2321,7 +2324,6 @@ IssueSwitchJumpTable(
Tcl_Size numRealBodies = 0, i;
Tcl_BytecodeLabel jumpLocation, jumpToDefault, *finalFixups;
Tcl_DString buffer;
- Tcl_HashEntry *hPtr;
/*
* If doing case-insensitive matching, convert to lower case and then do
@@ -2341,9 +2343,8 @@ IssueSwitchJumpTable(
* Start by allocating the jump table itself, plus some workspace.
*/
- jtPtr = (JumptableInfo *)Tcl_Alloc(sizeof(JumptableInfo));
- Tcl_InitHashTable(&jtPtr->hashTable, TCL_STRING_KEYS);
- infoIndex = TclCreateAuxData(jtPtr, &tclJumptableInfoType, envPtr);
+ jtPtr = AllocJumptable();
+ infoIndex = RegisterJumptable(jtPtr, envPtr);
finalFixups = (Tcl_BytecodeLabel *)TclStackAlloc(interp,
sizeof(Tcl_BytecodeLabel) * numArms);
foundDefault = 0;
@@ -2385,19 +2386,15 @@ IssueSwitchJumpTable(
Tcl_DStringInit(&buffer);
TclDStringAppendToken(&buffer, arm->valueToken);
if (noCase) {
- Tcl_Size slength = Tcl_UtfToLower(Tcl_DStringValue(&buffer));
- Tcl_DStringSetLength(&buffer, slength);
- }
- hPtr = Tcl_CreateHashEntry(&jtPtr->hashTable,
- Tcl_DStringValue(&buffer), &isNew);
- if (isNew) {
/*
- * First time we've encountered this match clause, so it must
- * point to here.
+ * We do case-insensitive matching by conversion to lower case.
*/
- Tcl_SetHashValue(hPtr, INT2PTR(CurrentOffset(envPtr) - jumpLocation));
+ Tcl_Size slength = Tcl_UtfToLower(Tcl_DStringValue(&buffer));
+ Tcl_DStringSetLength(&buffer, slength);
}
+ isNew = CreateJumptableEntry(jtPtr, Tcl_DStringValue(&buffer),
+ CurrentOffset(envPtr) - jumpLocation);
Tcl_DStringFree(&buffer);
} else {
/*
@@ -2484,22 +2481,28 @@ IssueSwitchJumpTable(
/*
*----------------------------------------------------------------------
*
- * DupJumptableInfo, FreeJumptableInfo --
+ * DupJumptableInfo, FreeJumptableInfo, etc --
*
- * Functions to duplicate, release and print a jump-table created for use
- * with the INST_JUMP_TABLE instruction.
+ * Functions to duplicate, release and print jump-tables created for use
+ * with the INST_JUMP_TABLE or INST_JUMP_TABLE_NUM instructions.
*
* Results:
* DupJumptableInfo: a copy of the jump-table
* FreeJumptableInfo: none
* PrintJumptableInfo: none
* DisassembleJumptableInfo: none
+ * DupJumptableNumInfo: a copy of the jump-table
+ * PrintJumptableNumInfo: none
+ * DisassembleJumptableNumInfo: none
*
* Side effects:
* DupJumptableInfo: allocates memory
* FreeJumptableInfo: releases memory
* PrintJumptableInfo: none
* DisassembleJumptableInfo: none
+ * DupJumptableNumInfo: allocates memory
+ * PrintJumptableNumInfo: none
+ * DisassembleJumptableNumInfo: none
*
*----------------------------------------------------------------------
*/
@@ -2509,12 +2512,11 @@ DupJumptableInfo(
void *clientData)
{
JumptableInfo *jtPtr = (JumptableInfo *)clientData;
- JumptableInfo *newJtPtr = (JumptableInfo *)Tcl_Alloc(sizeof(JumptableInfo));
+ JumptableInfo *newJtPtr = AllocJumptable();
Tcl_HashEntry *hPtr, *newHPtr;
Tcl_HashSearch search;
int isNew;
- Tcl_InitHashTable(&newJtPtr->hashTable, TCL_STRING_KEYS);
hPtr = Tcl_FirstHashEntry(&jtPtr->hashTable, &search);
for (; hPtr ; hPtr = Tcl_NextHashEntry(&search)) {
newHPtr = Tcl_CreateHashEntry(&newJtPtr->hashTable,
@@ -2586,6 +2588,83 @@ DisassembleJumptableInfo(
}
TclDictPut(NULL, dictObj, "mapping", mapping);
}
+
+static void *
+DupJumptableNumInfo(
+ void *clientData)
+{
+ JumptableNumInfo *jtnPtr = (JumptableNumInfo *) clientData;
+ JumptableNumInfo *newJtnPtr = AllocJumptableNum();
+ Tcl_HashEntry *hPtr, *newHPtr;
+ Tcl_HashSearch search;
+ int isNew;
+
+ hPtr = Tcl_FirstHashEntry(&jtnPtr->hashTable, &search);
+ for (; hPtr ; hPtr = Tcl_NextHashEntry(&search)) {
+ newHPtr = Tcl_CreateHashEntry(&newJtnPtr->hashTable,
+ Tcl_GetHashKey(&jtnPtr->hashTable, hPtr), &isNew);
+ Tcl_SetHashValue(newHPtr, Tcl_GetHashValue(hPtr));
+ }
+ return newJtnPtr;
+}
+
+// No FreeJumptableNumInfo; same as FreeJumptableInfo
+
+static void
+PrintJumptableNumInfo(
+ void *clientData,
+ Tcl_Obj *appendObj,
+ TCL_UNUSED(ByteCode *),
+ size_t pcOffset)
+{
+ JumptableNumInfo *jtnPtr = (JumptableNumInfo *)clientData;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ Tcl_Size key;
+ size_t offset, i = 0;
+
+ hPtr = Tcl_FirstHashEntry(&jtnPtr->hashTable, &search);
+ for (; hPtr ; hPtr = Tcl_NextHashEntry(&search)) {
+ key = (Tcl_Size) Tcl_GetHashKey(&jtnPtr->hashTable, hPtr);
+ offset = PTR2INT(Tcl_GetHashValue(hPtr));
+
+ if (i++) {
+ Tcl_AppendToObj(appendObj, ", ", TCL_AUTO_LENGTH);
+ if (i%4==0) {
+ Tcl_AppendToObj(appendObj, "\n\t\t", TCL_AUTO_LENGTH);
+ }
+ }
+ Tcl_AppendPrintfToObj(appendObj,
+ "%" TCL_SIZE_MODIFIER "d->pc %" TCL_Z_MODIFIER "u",
+ key, pcOffset + offset);
+ }
+}
+
+static void
+DisassembleJumptableNumInfo(
+ void *clientData,
+ Tcl_Obj *dictObj,
+ TCL_UNUSED(ByteCode *),
+ TCL_UNUSED(size_t))
+{
+ JumptableNumInfo *jtnPtr = (JumptableNumInfo *)clientData;
+ Tcl_Obj *mapping;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ Tcl_Size key;
+ size_t offset;
+
+ TclNewObj(mapping);
+ hPtr = Tcl_FirstHashEntry(&jtnPtr->hashTable, &search);
+ for (; hPtr ; hPtr = Tcl_NextHashEntry(&search)) {
+ key = (Tcl_Size) Tcl_GetHashKey(&jtnPtr->hashTable, hPtr);
+ offset = PTR2INT(Tcl_GetHashValue(hPtr));
+ // Cannot fail: keys already known to be unique
+ Tcl_DictObjPut(NULL, mapping, Tcl_NewWideIntObj(key),
+ Tcl_NewWideIntObj(offset));
+ }
+ TclDictPut(NULL, dictObj, "mapping", mapping);
+}
/*
*----------------------------------------------------------------------
@@ -2763,7 +2842,7 @@ TclCompileTryCmd(
CompileEnv *envPtr) /* Holds resulting instructions. */
{
Tcl_Size numHandlers, numWords = parsePtr->numWords;
- int result = TCL_ERROR;
+ int result = TCL_ERROR, anyTrapClauses = 0;
Tcl_Token *bodyToken, *finallyToken, *tokenPtr;
TryHandlerInfo staticHandler, *handlers = &staticHandler;
Tcl_Size handlerIdx = 0;
@@ -2773,17 +2852,6 @@ TclCompileTryCmd(
}
bodyToken = TokenAfter(parsePtr->tokenPtr);
-
- if (numWords == 2) {
- /*
- * No handlers or finally; do nothing beyond evaluating the body.
- */
-
- DefineLineInformation; /* TIP #280 */
- BODY( bodyToken, 1);
- return TCL_OK;
- }
-
numWords -= 2;
tokenPtr = TokenAfter(bodyToken);
@@ -2822,6 +2890,7 @@ TclCompileTryCmd(
Tcl_ListObjReplace(NULL, tmpObj, 0, 0, 0, NULL);
Tcl_IncrRefCount(tmpObj);
handlers[handlerIdx].matchClause = tmpObj;
+ anyTrapClauses = 1;
} else if (IS_TOKEN_LITERALLY(tokenPtr, "on")) {
int code;
@@ -2923,6 +2992,10 @@ TclCompileTryCmd(
if (finallyToken->type != TCL_TOKEN_SIMPLE_WORD) {
goto failedToCompile;
}
+ // Special case: empty finally clause
+ if (finallyToken[1].size == 0) {
+ finallyToken = NULL;
+ }
} else {
goto failedToCompile;
}
@@ -2931,15 +3004,33 @@ TclCompileTryCmd(
* Issue the bytecode.
*/
- if (!finallyToken) {
- result = IssueTryClausesInstructions(interp, envPtr, bodyToken,
- numHandlers, handlers);
+ if (!finallyToken && numHandlers == 0) {
+ /*
+ * No handlers or finally; do nothing beyond evaluating the body.
+ */
+
+ DefineLineInformation; /* TIP #280 */
+ BODY( bodyToken, 1);
+ result = TCL_OK;
+ } else if (!finallyToken) {
+ if (!anyTrapClauses) {
+ result = IssueTryTraplessClausesInstructions(interp, envPtr,
+ bodyToken, numHandlers, handlers);
+ } else {
+ result = IssueTryClausesInstructions(interp, envPtr, bodyToken,
+ numHandlers, handlers);
+ }
} else if (numHandlers == 0) {
result = IssueTryFinallyInstructions(interp, envPtr, bodyToken,
finallyToken);
} else {
- result = IssueTryClausesFinallyInstructions(interp, envPtr, bodyToken,
- numHandlers, handlers, finallyToken);
+ if (!anyTrapClauses) {
+ result = IssueTryTraplessClausesFinallyInstructions(interp, envPtr,
+ bodyToken, numHandlers, handlers, finallyToken);
+ } else {
+ result = IssueTryClausesFinallyInstructions(interp, envPtr,
+ bodyToken, numHandlers, handlers, finallyToken);
+ }
}
/*
@@ -2961,7 +3052,8 @@ TclCompileTryCmd(
/*
*----------------------------------------------------------------------
*
- * IssueTryClausesInstructions, IssueTryClausesFinallyInstructions,
+ * IssueTryClausesInstructions, IssueTryTraplessClausesInstructions,
+ * IssueTryClausesFinallyInstructions, IssueTryTraplessClausesFinallyInstructions,
* IssueTryFinallyInstructions --
*
* The code generators for [try]. Split from the parsing engine for
@@ -2969,8 +3061,6 @@ TclCompileTryCmd(
* just-finally and with-finally cases because so many of the details of
* generation vary between the three.
*
- * The macros below make the instruction issuing easier to follow.
- *
*----------------------------------------------------------------------
*/
@@ -2979,16 +3069,17 @@ IssueTryClausesInstructions(
Tcl_Interp *interp,
CompileEnv *envPtr,
Tcl_Token *bodyToken,
- Tcl_Size numHandlers,
+ Tcl_Size numHandlers, /* Min 1 */
TryHandlerInfo *handlers)
{
DefineLineInformation; /* TIP #280 */
Tcl_LVTIndex resultVar, optionsVar;
Tcl_Size i, j, len;
- int forwardsNeedFixing = 0, trapZero = 0;
+ int continuationsPending = 0, trapZero = 0;
Tcl_ExceptionRange range;
- Tcl_BytecodeLabel afterBody = 0, pushReturnOptions = 0, *forwardsToFix;
- Tcl_BytecodeLabel notCodeJumpSource, notECJumpSource, *addrsToFix, *noError;
+ Tcl_BytecodeLabel afterBody = 0, pushReturnOptions = 0;
+ Tcl_BytecodeLabel notCodeJumpSource, notECJumpSource, dontSpliceDuring;
+ Tcl_BytecodeLabel *continuationJumps, *afterReturn0, *noError;
resultVar = AnonymousLocal(envPtr);
optionsVar = AnonymousLocal(envPtr);
@@ -3050,13 +3141,15 @@ IssueTryClausesInstructions(
* Slight overallocation, but reduces size of this function.
*/
- addrsToFix = (Tcl_BytecodeLabel *)TclStackAlloc(interp,
+ afterReturn0 = (Tcl_BytecodeLabel *)TclStackAlloc(interp,
sizeof(Tcl_BytecodeLabel) * numHandlers * 3);
- forwardsToFix = addrsToFix + numHandlers;
- noError = forwardsToFix + numHandlers;
+ continuationJumps = afterReturn0 + numHandlers;
+ noError = continuationJumps + numHandlers;
+ for (i=0; i<numHandlers*3; i++) {
+ afterReturn0[i] = -1;
+ }
for (i=0 ; i<numHandlers ; i++) {
- noError[i] = -1;
OP( DUP);
PUSH_OBJ( Tcl_NewIntObj(handlers[i].matchCode));
OP( EQ);
@@ -3089,60 +3182,62 @@ IssueTryClausesInstructions(
OP4( LOAD_SCALAR, resultVar);
OP4( STORE_SCALAR, handlers[i].resultVar);
OP( POP);
- if (handlers[i].optionVar >= 0) {
- OP4( LOAD_SCALAR, optionsVar);
- OP4( STORE_SCALAR, handlers[i].optionVar);
- OP( POP);
- }
+ }
+ if (handlers[i].optionVar >= 0) {
+ OP4( LOAD_SCALAR, optionsVar);
+ OP4( STORE_SCALAR, handlers[i].optionVar);
+ OP( POP);
}
if (!handlers[i].tokenPtr) {
- forwardsNeedFixing = 1;
- FWDJUMP( JUMP, forwardsToFix[i]);
+ continuationsPending = 1;
+ FWDJUMP( JUMP, continuationJumps[i]);
STKDELTA(+1);
} else {
- Tcl_BytecodeLabel dontChangeOptions;
-
- forwardsToFix[i] = -1;
- if (forwardsNeedFixing) {
- forwardsNeedFixing = 0;
+ if (continuationsPending) {
+ continuationsPending = 0;
for (j=0 ; j<i ; j++) {
- if (forwardsToFix[j] == -1) {
- continue;
+ if (continuationJumps[j] != -1) {
+ FWDLABEL(continuationJumps[j]);
}
- FWDLABEL(forwardsToFix[j]);
- forwardsToFix[j] = -1;
+ continuationJumps[j] = -1;
}
}
- range = MAKE_CATCH_RANGE();
- OP4( BEGIN_CATCH, range);
- CATCH_RANGE(range) {
- BODY( handlers[i].tokenPtr, 5 + i*4);
- }
- OP( END_CATCH);
- FWDJUMP( JUMP, noError[i]);
- STKDELTA(-1);
- CATCH_TARGET(range);
- OP( PUSH_RESULT);
- OP( PUSH_RETURN_OPTIONS);
- OP( PUSH_RETURN_CODE);
- OP( END_CATCH);
- PUSH( "1");
- OP( EQ);
- FWDJUMP( JUMP_FALSE, dontChangeOptions);
-
- // Next bit isn't DICT_SET; alter which dict is in optionsVar
- PUSH( "-during");
- OP4( LOAD_SCALAR, optionsVar);
- OP( DICT_PUT);
- OP4( STORE_SCALAR, optionsVar);
+ if (handlers[i].tokenPtr[1].size == 0) {
+ // Empty handler body; can't generate non-trivial result tuple
+ PUSH( "");
+ FWDJUMP( JUMP, noError[i]);
+ } else {
+ range = MAKE_CATCH_RANGE();
+ OP4( BEGIN_CATCH, range);
+ CATCH_RANGE(range) {
+ BODY( handlers[i].tokenPtr, 5 + i*4);
+ }
+ OP( END_CATCH);
+ FWDJUMP( JUMP, noError[i]);
- FWDLABEL( dontChangeOptions);
- OP( SWAP);
- INVOKE( RETURN_STK);
+ STKDELTA(-1);
+ CATCH_TARGET(range);
+ OP( PUSH_RESULT);
+ OP( PUSH_RETURN_OPTIONS);
+ OP( PUSH_RETURN_CODE);
+ OP( END_CATCH);
+
+ PUSH( "1");
+ OP( EQ);
+ FWDJUMP( JUMP_FALSE, dontSpliceDuring);
+ // Next bit isn't DICT_SET; alter which dict is in optionsVar
+ PUSH( "-during");
+ OP4( LOAD_SCALAR, optionsVar);
+ OP( DICT_PUT);
+ FWDLABEL( dontSpliceDuring);
+
+ OP( SWAP);
+ INVOKE( RETURN_STK);
+ FWDJUMP( JUMP, afterReturn0[i]);
+ }
}
- FWDJUMP( JUMP, addrsToFix[i]);
if (handlers[i].matchClause) {
FWDLABEL( notECJumpSource);
}
@@ -3169,12 +3264,206 @@ IssueTryClausesInstructions(
FWDLABEL( afterBody);
}
for (i=0 ; i<numHandlers ; i++) {
- FWDLABEL( addrsToFix[i]);
+ if (afterReturn0[i] != -1) {
+ FWDLABEL( afterReturn0[i]);
+ }
if (noError[i] != -1) {
FWDLABEL( noError[i]);
}
}
- TclStackFree(interp, addrsToFix);
+ TclStackFree(interp, afterReturn0);
+ return TCL_OK;
+}
+
+static int
+IssueTryTraplessClausesInstructions(
+ Tcl_Interp *interp,
+ CompileEnv *envPtr,
+ Tcl_Token *bodyToken,
+ Tcl_Size numHandlers, /* Min 1 */
+ TryHandlerInfo *handlers)
+{
+ DefineLineInformation; /* TIP #280 */
+ Tcl_LVTIndex resultVar, optionsVar;
+ Tcl_Size i, j;
+ int continuationsPending = 0, trapZero = 0;
+ Tcl_ExceptionRange range;
+ Tcl_BytecodeLabel afterBody = 0, pushReturnOptions = 0;
+ Tcl_BytecodeLabel dontSpliceDuring, tableBase, haveOther;
+ Tcl_BytecodeLabel *continuationJumps, *afterReturn0, *noError;
+ JumptableNumInfo *tablePtr;
+ Tcl_AuxDataRef tableIdx;
+
+ resultVar = AnonymousLocal(envPtr);
+ optionsVar = AnonymousLocal(envPtr);
+ if (resultVar < 0 || optionsVar < 0) {
+ return TCL_ERROR;
+ }
+ afterReturn0 = (Tcl_BytecodeLabel *)TclStackAlloc(interp,
+ sizeof(Tcl_BytecodeLabel) * numHandlers * 3);
+ continuationJumps = afterReturn0 + numHandlers;
+ noError = continuationJumps + numHandlers;
+ for (i=0; i<numHandlers*3; i++) {
+ afterReturn0[i] = -1;
+ }
+ tablePtr = AllocJumptableNum();
+ tableIdx = RegisterJumptableNum(tablePtr, envPtr);
+
+ /*
+ * Check if we're supposed to trap a normal TCL_OK completion of the body.
+ * If not, we can handle that case much more efficiently.
+ */
+
+ for (i=0 ; i<numHandlers ; i++) {
+ if (handlers[i].matchCode == 0) {
+ trapZero = 1;
+ break;
+ }
+ }
+
+ /*
+ * Compile the body, trapping any error in it so that we can trap on it
+ * and/or run a finally clause. Note that there must be at least one
+ * on/trap clause; when none is present, this whole function is not called
+ * (and it's never called when there's a finally clause).
+ */
+
+ range = MAKE_CATCH_RANGE();
+ OP4( BEGIN_CATCH, range);
+ CATCH_RANGE(range) {
+ BODY( bodyToken, 1);
+ }
+ if (!trapZero) {
+ OP( END_CATCH);
+ FWDJUMP( JUMP, afterBody);
+ STKDELTA(-1);
+ } else {
+ PUSH( "0");
+ OP( SWAP);
+ FWDJUMP( JUMP, pushReturnOptions);
+ STKDELTA(-2);
+ }
+ CATCH_TARGET( range);
+ OP( PUSH_RETURN_CODE);
+ OP( PUSH_RESULT);
+ if (pushReturnOptions > 0) {
+ FWDLABEL( pushReturnOptions);
+ }
+ OP( PUSH_RETURN_OPTIONS);
+ OP( END_CATCH);
+ OP4( STORE_SCALAR, optionsVar);
+ OP( POP);
+ OP4( STORE_SCALAR, resultVar);
+ OP( POP);
+
+ /*
+ * Now we handle all the registered 'on' handlers.
+ * For us to be here, there must be at least one handler.
+ *
+ * Slight overallocation, but reduces size of this function.
+ */
+
+ BACKLABEL( tableBase);
+ OP4( JUMP_TABLE_NUM, tableIdx);
+ FWDJUMP( JUMP, haveOther);
+ for (i=0 ; i<numHandlers ; i++) {
+ CreateJumptableNumEntryToHere(tablePtr, handlers[i].matchCode, tableBase);
+
+ /*
+ * There is no finally clause, so we can avoid wrapping a catch
+ * context around the handler. That simplifies what instructions need
+ * to be issued a lot since we can let errors just fall through.
+ */
+
+ if (handlers[i].resultVar >= 0) {
+ OP4( LOAD_SCALAR, resultVar);
+ OP4( STORE_SCALAR, handlers[i].resultVar);
+ OP( POP);
+ }
+ if (handlers[i].optionVar >= 0) {
+ OP4( LOAD_SCALAR, optionsVar);
+ OP4( STORE_SCALAR, handlers[i].optionVar);
+ OP( POP);
+ }
+ if (!handlers[i].tokenPtr) {
+ continuationsPending = 1;
+ FWDJUMP( JUMP, continuationJumps[i]);
+ } else {
+ if (continuationsPending) {
+ continuationsPending = 0;
+ for (j=0 ; j<i ; j++) {
+ if (continuationJumps[j] != -1) {
+ FWDLABEL(continuationJumps[j]);
+ }
+ continuationJumps[j] = -1;
+ }
+ }
+
+ if (handlers[i].tokenPtr[1].size == 0) {
+ // Empty handler body; can't generate non-trivial result tuple
+ PUSH( "");
+ FWDJUMP( JUMP, noError[i]);
+ } else {
+ range = MAKE_CATCH_RANGE();
+ OP4( BEGIN_CATCH, range);
+ CATCH_RANGE(range) {
+ BODY( handlers[i].tokenPtr, 5 + i*4);
+ }
+ OP( END_CATCH);
+ FWDJUMP( JUMP, noError[i]);
+
+ STKDELTA(-1);
+ CATCH_TARGET(range);
+ OP( PUSH_RESULT);
+ OP( PUSH_RETURN_OPTIONS);
+ OP( PUSH_RETURN_CODE);
+ OP( END_CATCH);
+
+ PUSH( "1");
+ OP( EQ);
+ FWDJUMP( JUMP_FALSE, dontSpliceDuring);
+ // Next bit isn't DICT_SET; alter which dict is in optionsVar
+ PUSH( "-during");
+ OP4( LOAD_SCALAR, optionsVar);
+ OP( DICT_PUT);
+ FWDLABEL( dontSpliceDuring);
+
+ OP( SWAP);
+ INVOKE( RETURN_STK);
+ FWDJUMP( JUMP, afterReturn0[i]);
+ }
+ STKDELTA(-1);
+ }
+ }
+
+ /*
+ * Drop the result code since it didn't match any clause, and reissue the
+ * exception. Note also that INST_RETURN_STK can proceed to the next
+ * instruction.
+ */
+
+ FWDLABEL( haveOther);
+ OP4( LOAD_SCALAR, optionsVar);
+ OP4( LOAD_SCALAR, resultVar);
+ INVOKE( RETURN_STK);
+
+ /*
+ * Fix all the jumps from taken clauses to here (which is the end of the
+ * [try]).
+ */
+
+ if (!trapZero) {
+ FWDLABEL( afterBody);
+ }
+ for (i=0 ; i<numHandlers ; i++) {
+ if (afterReturn0[i] != -1) {
+ FWDLABEL( afterReturn0[i]);
+ }
+ if (noError[i] != -1) {
+ FWDLABEL( noError[i]);
+ }
+ }
+ TclStackFree(interp, afterReturn0);
return TCL_OK;
}
@@ -3183,7 +3472,7 @@ IssueTryClausesFinallyInstructions(
Tcl_Interp *interp,
CompileEnv *envPtr,
Tcl_Token *bodyToken,
- Tcl_Size numHandlers,
+ Tcl_Size numHandlers, /* Min 1 */
TryHandlerInfo *handlers,
Tcl_Token *finallyToken) /* Not NULL */
{
@@ -3193,7 +3482,7 @@ IssueTryClausesFinallyInstructions(
int forwardsNeedFixing = 0, trapZero = 0;
Tcl_ExceptionRange range;
Tcl_BytecodeLabel *addrsToFix, *forwardsToFix;
- Tcl_BytecodeLabel finalOK, finalError, noFinalError;
+ Tcl_BytecodeLabel finalOK, dontSpliceDuring;
Tcl_BytecodeLabel pushReturnOptions = 0, endCatch = 0, afterBody = 0;
resultLocal = AnonymousLocal(envPtr);
@@ -3265,8 +3554,7 @@ IssueTryClausesFinallyInstructions(
forwardsToFix = addrsToFix + numHandlers;
for (i=0 ; i<numHandlers ; i++) {
- Tcl_BytecodeLabel noTrapError, trapError, codeNotMatched;
- Tcl_BytecodeLabel notErrorCodeMatched = -1;
+ Tcl_BytecodeLabel codeNotMatched, notErrorCodeMatched = -1;
OP( DUP);
PUSH_OBJ( Tcl_NewIntObj(handlers[i].matchCode));
@@ -3295,15 +3583,18 @@ IssueTryClausesFinallyInstructions(
* failed trap for the result from the main script.
*/
- if (handlers[i].resultVar >= 0 || handlers[i].tokenPtr) {
+ if (handlers[i].resultVar >= 0 || handlers[i].optionVar >= 0
+ || handlers[i].tokenPtr) {
range = MAKE_CATCH_RANGE();
OP4( BEGIN_CATCH, range);
ExceptionRangeStarts(envPtr, range);
}
- if (handlers[i].resultVar >= 0) {
- OP4( LOAD_SCALAR, resultLocal);
- OP4( STORE_SCALAR, handlers[i].resultVar);
- OP( POP);
+ if (handlers[i].resultVar >= 0 || handlers[i].optionVar >= 0) {
+ if (handlers[i].resultVar >= 0) {
+ OP4( LOAD_SCALAR, resultLocal);
+ OP4( STORE_SCALAR, handlers[i].resultVar);
+ OP( POP);
+ }
if (handlers[i].optionVar >= 0) {
OP4( LOAD_SCALAR, optionsLocal);
OP4( STORE_SCALAR, handlers[i].optionVar);
@@ -3381,21 +3672,18 @@ IssueTryClausesFinallyInstructions(
OP( END_CATCH);
OP4( STORE_SCALAR, resultLocal);
OP( POP);
+
PUSH( "1");
OP( EQ);
- FWDJUMP( JUMP_FALSE, noTrapError);
-
+ FWDJUMP( JUMP_FALSE, dontSpliceDuring);
// Next bit isn't DICT_SET; alter which dict is in optionsLocal
PUSH( "-during");
OP4( LOAD_SCALAR, optionsLocal);
OP( DICT_PUT);
- OP4( STORE_SCALAR, optionsLocal);
- FWDJUMP( JUMP, trapError);
+ FWDLABEL( dontSpliceDuring);
- FWDLABEL( noTrapError);
OP4( STORE_SCALAR, optionsLocal);
- FWDLABEL( trapError);
/* Skip POP at end; can clean up with subsequent POP */
if (i+1 < numHandlers) {
OP( POP);
@@ -3452,23 +3740,287 @@ IssueTryClausesFinallyInstructions(
PUSH( "1");
OP( EQ);
- FWDJUMP( JUMP_FALSE, noFinalError);
-
+ FWDJUMP( JUMP_FALSE, dontSpliceDuring);
// Next bit isn't DICT_SET; alter which dict is in optionsLocal
PUSH( "-during");
OP4( LOAD_SCALAR, optionsLocal);
OP( DICT_PUT);
+ FWDLABEL( dontSpliceDuring);
+
OP4( STORE_SCALAR, optionsLocal);
OP( POP);
- // result
- FWDJUMP( JUMP, finalError);
- STKDELTA(+1);
- FWDLABEL( noFinalError);
+ OP4( STORE_SCALAR, resultLocal);
+ OP( POP);
+
+ FWDLABEL( finalOK);
+ OP4( LOAD_SCALAR, optionsLocal);
+ OP4( LOAD_SCALAR, resultLocal);
+ INVOKE( RETURN_STK);
+
+ return TCL_OK;
+}
+
+static int
+IssueTryTraplessClausesFinallyInstructions(
+ Tcl_Interp *interp,
+ CompileEnv *envPtr,
+ Tcl_Token *bodyToken,
+ Tcl_Size numHandlers, /* Min 1 */
+ TryHandlerInfo *handlers,
+ Tcl_Token *finallyToken) /* Not NULL */
+{
+ DefineLineInformation; /* TIP #280 */
+ Tcl_LVTIndex resultLocal, optionsLocal;
+ Tcl_Size i, j;
+ int forwardsNeedFixing = 0, trapZero = 0;
+ Tcl_ExceptionRange range;
+ Tcl_BytecodeLabel *addrsToFix, *forwardsToFix;
+ Tcl_BytecodeLabel finalOK, dontSpliceDuring, tableBase, haveOther;
+ Tcl_BytecodeLabel pushReturnOptions = 0, afterBody = 0;
+ JumptableNumInfo *tablePtr;
+ Tcl_AuxDataRef tableIdx;
+
+ resultLocal = AnonymousLocal(envPtr);
+ optionsLocal = AnonymousLocal(envPtr);
+ if (resultLocal < 0 || optionsLocal < 0) {
+ return TCL_ERROR;
+ }
+ addrsToFix = (Tcl_BytecodeLabel *)TclStackAlloc(interp,
+ sizeof(Tcl_BytecodeLabel) * numHandlers * 2);
+ forwardsToFix = addrsToFix + numHandlers;
+ for (i=0; i < numHandlers * 2; i++) {
+ addrsToFix[i] = -1;
+ }
+ tablePtr = AllocJumptableNum();
+ tableIdx = RegisterJumptableNum(tablePtr, envPtr);
+
+ /*
+ * Check if we're supposed to trap a normal TCL_OK completion of the body.
+ * If not, we can handle that case much more efficiently.
+ */
+
+ for (i=0 ; i<numHandlers ; i++) {
+ if (handlers[i].matchCode == 0) {
+ trapZero = 1;
+ break;
+ }
+ }
+
+ /*
+ * Compile the body, trapping any error in it so that we can trap on it
+ * (if any trap matches) and run a finally clause.
+ */
+
+ range = MAKE_CATCH_RANGE();
+ OP4( BEGIN_CATCH, range);
+ CATCH_RANGE(range) {
+ BODY( bodyToken, 1);
+ }
+ if (!trapZero) {
+ OP( END_CATCH);
+ OP4( STORE_SCALAR, resultLocal);
+ OP( POP);
+ PUSH( "-level 0 -code 0");
+ OP4( STORE_SCALAR, optionsLocal);
+ OP( POP);
+ FWDJUMP( JUMP, afterBody);
+ } else {
+ /*
+ * Fake a return code to go with our result.
+ */
+ PUSH( "0");
+ OP( SWAP);
+ FWDJUMP( JUMP, pushReturnOptions);
+ STKDELTA(-2);
+ }
+ CATCH_TARGET( range);
+ OP( PUSH_RETURN_CODE);
+ OP( PUSH_RESULT);
+ if (pushReturnOptions) {
+ FWDLABEL( pushReturnOptions);
+ }
+ OP( PUSH_RETURN_OPTIONS);
+ OP( END_CATCH);
+ OP4( STORE_SCALAR, optionsLocal);
+ OP( POP);
+ OP4( STORE_SCALAR, resultLocal);
+ OP( POP);
+
+ /*
+ * Now we handle all the registered 'on' and 'trap' handlers in order.
+ */
+
+ BACKLABEL( tableBase);
+ OP4( JUMP_TABLE_NUM, tableIdx);
+ FWDJUMP( JUMP, haveOther);
+ for (i=0 ; i<numHandlers ; i++) {
+ Tcl_BytecodeLabel endCatch = 0;
+ CreateJumptableNumEntryToHere(tablePtr, handlers[i].matchCode, tableBase);
+
+ /*
+ * There is a finally clause, so we need a fairly complex sequence of
+ * instructions to deal with an on/trap handler because we must call
+ * the finally handler *and* we need to substitute the result from a
+ * failed trap for the result from the main script.
+ */
+
+ if (handlers[i].resultVar >= 0 || handlers[i].optionVar >= 0
+ || handlers[i].tokenPtr) {
+ range = MAKE_CATCH_RANGE();
+ OP4( BEGIN_CATCH, range);
+ ExceptionRangeStarts(envPtr, range);
+ }
+ if (handlers[i].resultVar >= 0 || handlers[i].optionVar >= 0) {
+ if (handlers[i].resultVar >= 0) {
+ OP4( LOAD_SCALAR, resultLocal);
+ OP4( STORE_SCALAR, handlers[i].resultVar);
+ OP( POP);
+ }
+ if (handlers[i].optionVar >= 0) {
+ OP4( LOAD_SCALAR, optionsLocal);
+ OP4( STORE_SCALAR, handlers[i].optionVar);
+ OP( POP);
+ }
+
+ if (!handlers[i].tokenPtr) {
+ /*
+ * No handler. Will not be the last handler (that is a
+ * condition that is checked by the caller). Chain to the next
+ * one.
+ */
+
+ ExceptionRangeEnds(envPtr, range);
+ OP( END_CATCH);
+ forwardsNeedFixing = 1;
+ endCatch = 0;
+ FWDJUMP( JUMP, forwardsToFix[i]);
+ goto finishTrapCatchHandling;
+ }
+ } else if (!handlers[i].tokenPtr) {
+ /*
+ * No handler. Will not be the last handler (that condition is
+ * checked by the caller). Chain to the next one.
+ */
+
+ forwardsNeedFixing = 1;
+ FWDJUMP( JUMP, forwardsToFix[i]);
+ goto endOfThisArm;
+ }
+
+ /*
+ * Got a handler. Make sure that any pending patch-up actions from
+ * previous unprocessed handlers are dealt with now that we know where
+ * they are to jump to.
+ */
+
+ if (forwardsNeedFixing) {
+ Tcl_BytecodeLabel bodyStart;
+ forwardsNeedFixing = 0;
+ FWDJUMP( JUMP, bodyStart);
+ for (j=0 ; j<i ; j++) {
+ if (forwardsToFix[j] == -1) {
+ continue;
+ }
+ FWDLABEL(forwardsToFix[j]);
+ forwardsToFix[j] = -1;
+ }
+ OP4( BEGIN_CATCH, range);
+ FWDLABEL( bodyStart);
+ }
+ BODY( handlers[i].tokenPtr, 5 + i*4);
+ ExceptionRangeEnds(envPtr, range);
+ OP( PUSH_RETURN_OPTIONS);
+ OP( END_CATCH);
+ FWDJUMP( JUMP, endCatch);
+ STKDELTA(-2);
+
+ /*
+ * Error in handler or setting of variables; replace the stored
+ * exception with the new one. Note that we only push this if we have
+ * either a body or some variable setting here. Otherwise this code is
+ * unreachable.
+ */
+
+ finishTrapCatchHandling:
+ CATCH_TARGET( range);
+ OP( PUSH_RESULT);
+ OP( PUSH_RETURN_OPTIONS);
+ OP( PUSH_RETURN_CODE);
+ OP( END_CATCH);
+
+ PUSH( "1");
+ OP( EQ);
+ FWDJUMP( JUMP_FALSE, dontSpliceDuring);
+ // Next bit isn't DICT_SET; alter which dict is in optionsLocal
+ PUSH( "-during");
+ OP4( LOAD_SCALAR, optionsLocal);
+ OP( DICT_PUT);
+ FWDLABEL( dontSpliceDuring);
+
+ if (endCatch) {
+ FWDLABEL( endCatch);
+ }
+ OP4( STORE_SCALAR, optionsLocal);
+ OP( POP);
+ OP4( STORE_SCALAR, resultLocal);
+ OP( POP);
+
+ endOfThisArm:
+ if (i+1 < numHandlers) {
+ FWDJUMP( JUMP, addrsToFix[i]);
+ }
+ }
+
+ /*
+ * Fix all the jumps from taken clauses and the jump from after the jump
+ * table to point to the start of the finally processing.
+ */
+
+ for (i=0 ; i<numHandlers-1 ; i++) {
+ FWDLABEL( addrsToFix[i]);
+ }
+ TclStackFree(interp, addrsToFix);
+ FWDLABEL( haveOther);
+ if (!trapZero) {
+ FWDLABEL( afterBody);
+ }
+
+ /*
+ * Process the finally clause (at last!) Note that we do not wrap this in
+ * error handlers because we would just rethrow immediately anyway. Then
+ * (on normal success) we reissue the exception. Note also that
+ * INST_RETURN_STK can proceed to the next instruction; that'll be the
+ * next command (or some inter-command manipulation).
+ */
+
+ range = MAKE_CATCH_RANGE();
+ OP4( BEGIN_CATCH, range);
+ CATCH_RANGE(range) {
+ BODY( finallyToken, 3 + 4*numHandlers);
+ }
+ OP( END_CATCH);
+ OP( POP);
+ FWDJUMP( JUMP, finalOK);
+
+ CATCH_TARGET( range);
+ OP( PUSH_RESULT);
+ OP( PUSH_RETURN_OPTIONS);
+ OP( PUSH_RETURN_CODE);
+ OP( END_CATCH);
+
+ PUSH( "1");
+ OP( EQ);
+ FWDJUMP( JUMP_FALSE, dontSpliceDuring);
+ // Next bit isn't DICT_SET; alter which dict is in optionsLocal
+ PUSH( "-during");
+ OP4( LOAD_SCALAR, optionsLocal);
+ OP( DICT_PUT);
+ FWDLABEL( dontSpliceDuring);
+
OP4( STORE_SCALAR, optionsLocal);
OP( POP);
- FWDLABEL( finalError);
OP4( STORE_SCALAR, resultLocal);
OP( POP);
@@ -3489,7 +4041,7 @@ IssueTryFinallyInstructions(
{
DefineLineInformation; /* TIP #280 */
Tcl_ExceptionRange bodyRange, finallyRange;
- Tcl_BytecodeLabel jumpOK, jumpSplice, endCatch;
+ Tcl_BytecodeLabel jumpOK, dontSpliceDuring, endCatch;
/*
* Note that this one is simple enough that we can issue it without
@@ -3533,11 +4085,12 @@ IssueTryFinallyInstructions(
// Don't forget original error
PUSH( "1");
OP( EQ);
- FWDJUMP( JUMP_FALSE, jumpSplice);
+ FWDJUMP( JUMP_FALSE, dontSpliceDuring);
PUSH( "-during");
OP4( OVER, 3);
OP( DICT_PUT);
- FWDLABEL( jumpSplice);
+ FWDLABEL( dontSpliceDuring);
+
OP4( REVERSE, 4);
OP( POP);
OP( POP);
diff --git a/generic/tclCompile.c b/generic/tclCompile.c
index dd7af2c..31e3157 100644
--- a/generic/tclCompile.c
+++ b/generic/tclCompile.c
@@ -477,6 +477,7 @@ InstructionDesc const tclInstructionTable[] = {
/* Jump according to the jump-table (in AuxData as indicated by the
* operand) and the argument popped from the list. Always executes the
* next instruction if no match against the table's entries was found.
+ * Keys are strings.
* Stack: ... value => ...
* Note that the jump table contains offsets relative to the PC when
* it points to this instruction; the code is relocatable. */
@@ -880,11 +881,6 @@ InstructionDesc const tclInstructionTable[] = {
/* Create constant. Variable name and value on stack.
* Stack: ... varName value => ... */
- TCL_INSTRUCTION_ENTRY(
- "returnCodeBranch", -1),
- /* Jump to next instruction based on the return code on top of stack
- * ERROR: +1; RETURN: +6; BREAK: +11; CONTINUE: +16;
- * Other non-OK: +21 */
TCL_INSTRUCTION_ENTRY1(
"incrScalar", 5, 0, OPERAND_LVT4),
/* Incr scalar at index op1 in frame; incr amount is stktop */
@@ -949,6 +945,15 @@ InstructionDesc const tclInstructionTable[] = {
/* Test if the value at the top of the stack is empty (via a call to
* Tcl_IsEmpty).
* Stack: ... value => ... boolean */
+ TCL_INSTRUCTION_ENTRY1(
+ "jumpTableNum", 5, -1, OPERAND_AUX4),
+ /* Jump according to the jump-table (in AuxData as indicated by the
+ * operand) and the argument popped from the list. Always executes the
+ * next instruction if no match against the table's entries was found.
+ * Keys are Tcl_WideInt.
+ * Stack: ... value => ...
+ * Note that the jump table contains offsets relative to the PC when
+ * it points to this instruction; the code is relocatable. */
{NULL, 0, 0, 0, {OPERAND_NONE}}
};
diff --git a/generic/tclCompile.h b/generic/tclCompile.h
index 3b06e90..a19c79b 100644
--- a/generic/tclCompile.h
+++ b/generic/tclCompile.h
@@ -385,7 +385,7 @@ typedef struct CompileEnv {
/* TIP #280 */
ExtCmdLoc *extCmdMapPtr; /* Extended command location information for
* 'info frame'. */
- int line; /* First line of the script, based on the
+ int line; /* First line of the script, based on the
* invoking context, then the line of the
* command currently compiled. */
int atCmdStart; /* Flag to say whether an INST_START_CMD
@@ -777,7 +777,7 @@ enum TclInstruction {
/* For [subst] compilation */
INST_NOP,
- DEPRECATED_OPCODE(INST_RETURN_CODE_BRANCH1),
+ DEPRECATED_OPCODE(INST_RETURN_CODE_BRANCH),
/* For [unset] compilation */
INST_UNSET_SCALAR,
@@ -878,8 +878,7 @@ enum TclInstruction {
INST_CONST_IMM,
INST_CONST_STK,
- /* Updated compilations with fewer arg size constraints */
- INST_RETURN_CODE_BRANCH,
+ /* Updated compilations with fewer arg size constraints for 9.1 */
INST_INCR_SCALAR,
INST_INCR_ARRAY,
INST_INCR_SCALAR_IMM,
@@ -888,12 +887,14 @@ enum TclInstruction {
INST_TCLOO_NEXT,
INST_TCLOO_NEXT_CLASS,
+ /* Really new opcodes for 9.1 */
INST_SWAP,
INST_ERROR_PREFIX_EQ,
INST_TCLOO_ID,
INST_DICT_PUT,
INST_DICT_REMOVE,
INST_IS_EMPTY,
+ INST_JUMP_TABLE_NUM,
/* The last opcode */
LAST_INST_OPCODE
@@ -1076,7 +1077,7 @@ typedef struct ForeachInfo {
} ForeachInfo;
/*
- * Structure used to hold information about a switch command that is needed
+ * Structures used to hold information about a switch command that is needed
* during program execution. These structures are stored in CompileEnv and
* ByteCode structures as auxiliary data.
*/
@@ -1091,6 +1092,69 @@ MODULE_SCOPE const AuxDataType tclJumptableInfoType;
#define JUMPTABLEINFO(envPtr, index) \
((JumptableInfo *) TclFetchAuxData((envPtr), TclGetUInt4AtPtr(index)))
+static inline JumptableInfo *
+AllocJumptable(void)
+{
+ JumptableInfo *jtPtr = (JumptableInfo *) Tcl_Alloc(sizeof(JumptableInfo));
+ Tcl_InitHashTable(&jtPtr->hashTable, TCL_STRING_KEYS);
+ return jtPtr;
+}
+
+static inline int
+CreateJumptableEntry(
+ JumptableInfo *jtPtr,
+ const char *keyPtr,
+ Tcl_Size offset)
+{
+ int isNew;
+ Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&jtPtr->hashTable, keyPtr, &isNew);
+ if (isNew) {
+ Tcl_SetHashValue(hPtr, INT2PTR(offset));
+ }
+ return isNew;
+}
+
+#define CreateJumptableEntryToHere(jtPtr, key, baseOffset) \
+ CreateJumptableEntry((jtPtr), (key), CurrentOffset(envPtr) - (baseOffset))
+
+typedef struct JumptableNumInfo {
+ Tcl_HashTable hashTable; /* Hash that maps Tcl_WideInt to signed ints
+ * (PC offsets). */
+} JumptableNumInfo;
+
+MODULE_SCOPE const AuxDataType tclJumptableNumericInfoType;
+
+#define JUMPTABLENUMINFO(envPtr, index) \
+ ((JumptableNumInfo *) TclFetchAuxData((envPtr), TclGetUInt4AtPtr(index)))
+
+static inline JumptableNumInfo *
+AllocJumptableNum(void)
+{
+ JumptableNumInfo *jtnPtr = (JumptableNumInfo *)
+ Tcl_Alloc(sizeof(JumptableNumInfo));
+ Tcl_InitHashTable(&jtnPtr->hashTable, TCL_ONE_WORD_KEYS);
+ return jtnPtr;
+}
+
+static inline int
+CreateJumptableNumEntry(
+ JumptableNumInfo *jtnPtr,
+ Tcl_Size key,
+ Tcl_Size offset)
+{
+ int isNew;
+ Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&jtnPtr->hashTable, INT2PTR(key),
+ &isNew);
+ if (isNew) {
+ Tcl_SetHashValue(hPtr, INT2PTR(offset));
+ }
+ return isNew;
+}
+
+#define CreateJumptableNumEntryToHere(jtnPtr, key, baseOffset) \
+ CreateJumptableNumEntry((jtnPtr), (key), \
+ CurrentOffset(envPtr) - (baseOffset))
+
/*
* Structure used to hold information about a [dict update] command that is
* needed during program execution. These structures are stored in CompileEnv
@@ -1913,6 +1977,26 @@ enum Lreplace4Flags {
};
/*
+ * Helper functions for jump tables that call other internal API bits.
+ */
+
+static inline Tcl_Size
+RegisterJumptable(
+ JumptableInfo *jtPtr,
+ CompileEnv *envPtr)
+{
+ return TclCreateAuxData(jtPtr, &tclJumptableInfoType, envPtr);
+}
+
+static inline Tcl_Size
+RegisterJumptableNum(
+ JumptableNumInfo *jtPtr,
+ CompileEnv *envPtr)
+{
+ return TclCreateAuxData(jtPtr, &tclJumptableNumericInfoType, envPtr);
+}
+
+/*
* DTrace probe macros (NOPs if DTrace support is not enabled).
*/
diff --git a/generic/tclExecute.c b/generic/tclExecute.c
index 0b1057e..448850b 100644
--- a/generic/tclExecute.c
+++ b/generic/tclExecute.c
@@ -4484,7 +4484,7 @@ TEBCresume(
/*
* Jump to location looked up in a hashtable; fall through to next
- * instr if lookup fails.
+ * instr if lookup fails. Lookup by string.
*/
opnd = TclGetInt4AtPtr(pc + 1);
@@ -4503,6 +4503,36 @@ TEBCresume(
}
}
break;
+ case INST_JUMP_TABLE_NUM: {
+ Tcl_HashEntry *hPtr;
+ JumptableNumInfo *jtnPtr;
+ Tcl_WideInt key;
+
+ /*
+ * Jump to location looked up in a hashtable; fall through to next
+ * instr if lookup fails. Lookup by integer.
+ */
+
+ opnd = TclGetInt4AtPtr(pc + 1);
+ jtnPtr = (JumptableNumInfo *) codePtr->auxDataArrayPtr[opnd].clientData;
+ TRACE(("%d \"%.20s\" => ", opnd, O2S(OBJ_AT_TOS)));
+ if (Tcl_GetWideIntFromObj(NULL, OBJ_AT_TOS, &key) != TCL_OK) {
+ goto jumpTableNumFallthrough;
+ }
+ hPtr = Tcl_FindHashEntry(&jtnPtr->hashTable, (void *)key);
+ if (hPtr != NULL) {
+ Tcl_Size jumpOffset = PTR2INT(Tcl_GetHashValue(hPtr));
+
+ TRACE_APPEND(("found in table, new pc %" TCL_Z_MODIFIER "u\n",
+ (pc - codePtr->codeStart + jumpOffset)));
+ NEXT_INST_F0(jumpOffset, 1);
+ } else {
+ jumpTableNumFallthrough:
+ TRACE_APPEND(("not found in table\n"));
+ NEXT_INST_F0(5, 1);
+ }
+ }
+ break;
/*
* -----------------------------------------------------------------
@@ -6958,10 +6988,10 @@ TEBCresume(
break;
#ifndef REMOVE_DEPRECATED_OPCODES
- case INST_RETURN_CODE_BRANCH1: {
+ case INST_RETURN_CODE_BRANCH: {
int code;
- DEPRECATED_OPCODE_MARK(INST_RETURN_CODE_BRANCH1);
+ DEPRECATED_OPCODE_MARK(INST_RETURN_CODE_BRANCH);
if (TclGetIntFromObj(NULL, OBJ_AT_TOS, &code) != TCL_OK) {
Tcl_Panic("INST_RETURN_CODE_BRANCH: TOS not a return code!");
}
@@ -6976,22 +7006,6 @@ TEBCresume(
}
#endif
- case INST_RETURN_CODE_BRANCH: {
- int code;
-
- if (TclGetIntFromObj(NULL, OBJ_AT_TOS, &code) != TCL_OK) {
- Tcl_Panic("INST_RETURN_CODE_BRANCH: TOS not a return code!");
- }
- if (code == TCL_OK) {
- Tcl_Panic("INST_RETURN_CODE_BRANCH: TOS is TCL_OK!");
- }
- if (code < TCL_ERROR || code > TCL_CONTINUE) {
- code = TCL_CONTINUE + 1;
- }
- TRACE(("\"%s\" => jump offset %d\n", O2S(OBJ_AT_TOS), 5*code - 4));
- NEXT_INST_F0(5*code - 4, 1);
- }
-
case INST_ERROR_PREFIX_EQ: {
/*
* A special equality operator for errorcode prefix matching in
diff --git a/generic/tclOptimize.c b/generic/tclOptimize.c
index 39cbbd4..3e64a2d 100644
--- a/generic/tclOptimize.c
+++ b/generic/tclOptimize.c
@@ -99,10 +99,16 @@ LocateTargetAddresses(
storeTarget:
DefineTargetAddress(tablePtr, targetInstPtr);
break;
+ case INST_JUMP_TABLE_NUM:
+ hPtr = Tcl_FirstHashEntry(
+ &JUMPTABLENUMINFO(envPtr, currentInstPtr+1)->hashTable,
+ &hSearch);
+ goto storeJumpTableTargets;
case INST_JUMP_TABLE:
hPtr = Tcl_FirstHashEntry(
&JUMPTABLEINFO(envPtr, currentInstPtr+1)->hashTable,
&hSearch);
+ storeJumpTableTargets:
for (; hPtr ; hPtr = Tcl_NextHashEntry(&hSearch)) {
targetInstPtr = currentInstPtr +
PTR2INT(Tcl_GetHashValue(hPtr));
@@ -110,17 +116,12 @@ LocateTargetAddresses(
}
break;
#ifndef REMOVE_DEPRECATED_OPCODES
- case INST_RETURN_CODE_BRANCH1:
+ case INST_RETURN_CODE_BRANCH:
for (i=TCL_ERROR ; i<TCL_CONTINUE+1 ; i++) {
DefineTargetAddress(tablePtr, currentInstPtr + 2*i - 1);
}
break;
#endif
- case INST_RETURN_CODE_BRANCH:
- for (i=TCL_ERROR ; i<TCL_CONTINUE+1 ; i++) {
- DefineTargetAddress(tablePtr, currentInstPtr + 5*i - 4);
- }
- break;
}
}