diff options
| author | dkf <donal.k.fellows@manchester.ac.uk> | 2025-05-02 15:54:41 (GMT) |
|---|---|---|
| committer | dkf <donal.k.fellows@manchester.ac.uk> | 2025-05-02 15:54:41 (GMT) |
| commit | e33638fe9dee868660175527228ced07e94de5c3 (patch) | |
| tree | 2d6c3f273d8cc06c7acf0b865f50b35fc03a37e5 /generic | |
| parent | c815194539f2bcc4a94ab20a37e2358246d6f860 (diff) | |
| parent | 93069ee44f5962ef803b537691b3330e104506fd (diff) | |
| download | tcl-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.c | 416 | ||||
| -rw-r--r-- | generic/tclCompCmds.c | 2 | ||||
| -rw-r--r-- | generic/tclCompCmdsSZ.c | 855 | ||||
| -rw-r--r-- | generic/tclCompile.c | 15 | ||||
| -rw-r--r-- | generic/tclCompile.h | 94 | ||||
| -rw-r--r-- | generic/tclExecute.c | 52 | ||||
| -rw-r--r-- | generic/tclOptimize.c | 13 |
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; } } |
