diff options
author | dkf <donal.k.fellows@manchester.ac.uk> | 2013-06-05 21:05:41 (GMT) |
---|---|---|
committer | dkf <donal.k.fellows@manchester.ac.uk> | 2013-06-05 21:05:41 (GMT) |
commit | 38cbf3feb975cb1a1a9bffc69cf773f4f2d2c11c (patch) | |
tree | 8f36fb50efae7b28b8521abc6e17d7e90311e16a /generic/tclOptimize.c | |
parent | 24d2ee6791fc464e86c2aecbd60376628796af06 (diff) | |
download | tcl-38cbf3feb975cb1a1a9bffc69cf773f4f2d2c11c.zip tcl-38cbf3feb975cb1a1a9bffc69cf773f4f2d2c11c.tar.gz tcl-38cbf3feb975cb1a1a9bffc69cf773f4f2d2c11c.tar.bz2 |
Added the optimizer...
Diffstat (limited to 'generic/tclOptimize.c')
-rw-r--r-- | generic/tclOptimize.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/generic/tclOptimize.c b/generic/tclOptimize.c new file mode 100644 index 0000000..18dc208 --- /dev/null +++ b/generic/tclOptimize.c @@ -0,0 +1,287 @@ +/* + * tclOptimize.c -- + * + * This file contains the bytecode optimizer. + * + * Copyright (c) 2013 by Donal Fellows. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tclInt.h" +#include "tclCompile.h" +#include <assert.h> + +#define DefineTargetAddress(tablePtr, address) \ + ((void) Tcl_CreateHashEntry((tablePtr), (void *) (address), &isNew)) +#define IsTargetAddress(tablePtr, address) \ + (Tcl_FindHashEntry((tablePtr), (void *) (address)) != NULL) + +static void +LocateTargetAddresses( + CompileEnv *envPtr, + Tcl_HashTable *tablePtr) +{ + unsigned char *pc, *target; + int size, isNew, i; + Tcl_HashEntry *hPtr; + Tcl_HashSearch hSearch; + + Tcl_InitHashTable(tablePtr, TCL_ONE_WORD_KEYS); + + /* + * The starts of commands represent target addresses. + */ + + for (i=0 ; i<envPtr->numCommands ; i++) { + DefineTargetAddress(tablePtr, + envPtr->codeStart + envPtr->cmdMapPtr[i].codeOffset); + } + + /* + * Find places where we should be careful about replacing instructions + * because they are the targets of various types of jumps. + */ + + for (pc = envPtr->codeStart ; pc < envPtr->codeNext ; pc += size) { + size = tclInstructionTable[*pc].numBytes; + switch (*pc) { + case INST_JUMP1: + case INST_JUMP_TRUE1: + case INST_JUMP_FALSE1: + target = pc + TclGetInt1AtPtr(pc+1); + goto storeTarget; + case INST_JUMP4: + case INST_JUMP_TRUE4: + case INST_JUMP_FALSE4: + target = pc + TclGetInt4AtPtr(pc+1); + goto storeTarget; + case INST_BEGIN_CATCH4: + target = envPtr->codeStart + envPtr->exceptArrayPtr[ + TclGetUInt4AtPtr(pc+1)].codeOffset; + storeTarget: + DefineTargetAddress(tablePtr, target); + break; + case INST_JUMP_TABLE: + hPtr = Tcl_FirstHashEntry( + &JUMPTABLEINFO(envPtr, pc+1)->hashTable, &hSearch); + for (; hPtr ; hPtr = Tcl_NextHashEntry(&hSearch)) { + target = pc + PTR2INT(Tcl_GetHashValue(hPtr)); + DefineTargetAddress(tablePtr, target); + } + break; + case INST_RETURN_CODE_BRANCH: + for (i=TCL_ERROR ; i<TCL_CONTINUE+1 ; i++) { + DefineTargetAddress(tablePtr, pc + 2*i - 1); + } + break; + case INST_START_CMD: + assert (envPtr->atCmdStart < 2); + } + } + + /* + * Add a marker *after* the last bytecode instruction. WARNING: points to + * one past the end! + */ + + DefineTargetAddress(tablePtr, pc); + + /* + * Enter in the targets of exception ranges. + */ + + for (i=0 ; i<envPtr->exceptArrayNext ; i++) { + ExceptionRange *rangePtr = &envPtr->exceptArrayPtr[i]; + + if (rangePtr->type == CATCH_EXCEPTION_RANGE) { + target = envPtr->codeStart + rangePtr->catchOffset; + DefineTargetAddress(tablePtr, target); + } else { + target = envPtr->codeStart + rangePtr->breakOffset; + DefineTargetAddress(tablePtr, target); + if (rangePtr->continueOffset >= 0) { + target = envPtr->codeStart + rangePtr->continueOffset; + DefineTargetAddress(tablePtr, target); + } + } + } +} + +/* + * ---------------------------------------------------------------------- + * + * TclOptimizeBytecode -- + * + * A very simple peephole optimizer for bytecode. + * + * ---------------------------------------------------------------------- + */ + +void +TclOptimizeBytecode( + CompileEnv *envPtr) +{ + unsigned char *pc; + int size; + Tcl_HashTable targets; + + /* + * Replace PUSH/POP sequences (when non-hazardous) with NOPs. Also replace + * PUSH empty/CONCAT and TRY_CVT_NUMERIC (when followed by an operation + * that guarantees the check for arithmeticity) and eliminate LNOT when we + * can invert the following JUMP condition. + */ + + LocateTargetAddresses(envPtr, &targets); + for (pc = envPtr->codeStart ; pc < envPtr->codeNext ; pc += size) { + int blank = 0, i, inst; + + size = tclInstructionTable[*pc].numBytes; + while (*(pc+size) == INST_NOP) { + if (IsTargetAddress(&targets, pc + size)) { + break; + } + size += tclInstructionTable[INST_NOP].numBytes; + } + if (IsTargetAddress(&targets, pc + size)) { + continue; + } + inst = *(pc + size); + switch (*pc) { + case INST_PUSH1: + if (inst == INST_POP) { + blank = size + tclInstructionTable[inst].numBytes; + } else if (inst == INST_CONCAT1 + && TclGetUInt1AtPtr(pc + size + 1) == 2) { + Tcl_Obj *litPtr = TclFetchLiteral(envPtr, + TclGetUInt1AtPtr(pc + 1)); + int numBytes; + + (void) Tcl_GetStringFromObj(litPtr, &numBytes); + if (numBytes == 0) { + blank = size + tclInstructionTable[inst].numBytes; + } + } + break; + case INST_PUSH4: + if (inst == INST_POP) { + blank = size + 1; + } else if (inst == INST_CONCAT1 + && TclGetUInt1AtPtr(pc + size + 1) == 2) { + Tcl_Obj *litPtr = TclFetchLiteral(envPtr, + TclGetUInt4AtPtr(pc + 1)); + int numBytes; + + (void) Tcl_GetStringFromObj(litPtr, &numBytes); + if (numBytes == 0) { + blank = size + tclInstructionTable[inst].numBytes; + } + } + break; + case INST_LNOT: + switch (inst) { + case INST_JUMP_TRUE1: + blank = size; + *(pc + size) = INST_JUMP_FALSE1; + break; + case INST_JUMP_FALSE1: + blank = size; + *(pc + size) = INST_JUMP_TRUE1; + break; + case INST_JUMP_TRUE4: + blank = size; + *(pc + size) = INST_JUMP_FALSE4; + break; + case INST_JUMP_FALSE4: + blank = size; + *(pc + size) = INST_JUMP_TRUE4; + break; + } + break; + case INST_TRY_CVT_TO_NUMERIC: + switch (inst) { + case INST_JUMP_TRUE1: + case INST_JUMP_TRUE4: + case INST_JUMP_FALSE1: + case INST_JUMP_FALSE4: + case INST_INCR_SCALAR1: + case INST_INCR_ARRAY1: + case INST_INCR_ARRAY_STK: + case INST_INCR_SCALAR_STK: + case INST_INCR_STK: + case INST_LOR: + case INST_LAND: + case INST_EQ: + case INST_NEQ: + case INST_LT: + case INST_LE: + case INST_GT: + case INST_GE: + case INST_MOD: + case INST_LSHIFT: + case INST_RSHIFT: + case INST_BITOR: + case INST_BITXOR: + case INST_BITAND: + case INST_EXPON: + case INST_ADD: + case INST_SUB: + case INST_DIV: + case INST_MULT: + case INST_LNOT: + case INST_BITNOT: + case INST_UMINUS: + case INST_UPLUS: + case INST_TRY_CVT_TO_NUMERIC: + blank = size; + break; + } + break; + } + if (blank > 0) { + for (i=0 ; i<blank ; i++) { + *(pc + i) = INST_NOP; + } + size = blank; + } + } + Tcl_DeleteHashTable(&targets); + + /* + * Trim unreachable instructions after a DONE. + */ + + LocateTargetAddresses(envPtr, &targets); + for (pc = envPtr->codeStart ; pc < envPtr->codeNext-1 ; pc += size) { + int clear = 0; + + size = tclInstructionTable[*pc].numBytes; + if (*pc != INST_DONE) { + continue; + } + assert (size == 1); + while (!IsTargetAddress(&targets, pc + 1 + clear)) { + clear += tclInstructionTable[*(pc + 1 + clear)].numBytes; + } + if (pc + 1 + clear == envPtr->codeNext) { + envPtr->codeNext -= clear; + } else { + while (clear --> 0) { + *(pc + 1 + clear) = INST_NOP; + } + } + } + + Tcl_DeleteHashTable(&targets); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * tab-width: 8 + * End: + */ |