summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Sofer <miguel.sofer@gmail.com>2015-11-08 14:58:35 (GMT)
committerMiguel Sofer <miguel.sofer@gmail.com>2015-11-08 14:58:35 (GMT)
commitce9f44325016e5a430f94e10093885286ed86ecd (patch)
tree9803f811ca9e953a5a608a3ab31e1a5132fa3e52
parent0cf78463d3239b3b19fd0f38622daed8c6bc3f4c (diff)
downloadtcl-ce9f44325016e5a430f94e10093885286ed86ecd.zip
tcl-ce9f44325016e5a430f94e10093885286ed86ecd.tar.gz
tcl-ce9f44325016e5a430f94e10093885286ed86ecd.tar.bz2
drop in experimental optimizer; tmp branch as it is buggy
-rw-r--r--generic/tclOptimize.c1304
1 files changed, 910 insertions, 394 deletions
diff --git a/generic/tclOptimize.c b/generic/tclOptimize.c
index 2b83b3f..c754472 100644
--- a/generic/tclOptimize.c
+++ b/generic/tclOptimize.c
@@ -3,40 +3,59 @@
*
* This file contains the bytecode optimizer.
*
- * Copyright (c) 2013 by Donal Fellows.
* Copyright (c) 2013 by Miguel Sofer.
*
* 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>
-/*
- * Forward declarations.
- */
-
-static void AdvanceJumps(CompileEnv *envPtr);
-static void ConvertZeroEffectToNOP(CompileEnv *envPtr);
-static void LocateTargetAddresses(CompileEnv *envPtr,
- Tcl_HashTable *tablePtr);
-static void TrimUnreachable(CompileEnv *envPtr);
-static void CompactCode(CompileEnv *envPtr);
+typedef struct optPad {
+ int codeSize;
+ int cache;
+ int *npaths;
+ int *scratch;
+ Tcl_HashTable stack;
+ int first;
+} optPad;
+
+static void markPath(CompileEnv *envPtr, int pc, optPad *padPtr,
+ int mark);
+static int effectivePCInternal(CompileEnv *envPtr, int pc,
+ optPad *padPtr, int start);
+static void ThreadJumps(CompileEnv *envPtr, optPad *padPtr);
+static int UpdateJump(CompileEnv *envPtr,optPad *padPtr, int pc);
+static int optimizePush_0(CompileEnv *envPtr,optPad *padPtr, int pc);
+static int optimizePush_1(CompileEnv *envPtr,optPad *padPtr, int pc);
+static void MoveUnreachable(CompileEnv *envPtr, optPad *padPtr);
+static void CompactCode(CompileEnv *envPtr, optPad *padPtr,
+ int shrinkInst);
+static void Optimize_0(CompileEnv *envPtr, optPad *padPtr);
+static void Optimize_1(CompileEnv *envPtr, optPad *padPtr);
+
+#define INIT_SIZE \
+ int codeSize = padPtr->codeSize
+
+#define INIT_PATHS \
+ int *PATHS = padPtr->npaths
+
+#define INIT_STACK \
+ Tcl_HashTable *stackPtr = &padPtr->stack
/*
* Helper macros.
*/
-#define DefineTargetAddress(tablePtr, address) \
- ((void) Tcl_CreateHashEntry((tablePtr), (void *) (address), &isNew))
-#define IsTargetAddress(tablePtr, address) \
- (Tcl_FindHashEntry((tablePtr), (void *) (address)) != NULL)
#define AddrLength(address) \
(tclInstructionTable[*(unsigned char *)(address)].numBytes)
#define InstLength(instruction) \
(tclInstructionTable[(unsigned char)(instruction)].numBytes)
+#define InstEffect(instruction) \
+ (tclInstructionTable[(unsigned char)(instruction)].stackEffect)
+#define Op1Type(instruction) \
+ (tclInstructionTable[(unsigned char)(instruction)].opTypes[0])
/*
* Macros used in the code compactor.
@@ -45,16 +64,16 @@ static void CompactCode(CompileEnv *envPtr);
#define GET_INT1_AT_PC(pc) \
TclGetInt1AtPtr(envPtr->codeStart + (pc))
-#define GET_INT4_AT_PC(pc) \
+#define GET_INT4_AT_PC(pc) \
TclGetInt4AtPtr(envPtr->codeStart + (pc))
-#define GET_UINT1_AT_PC(pc) \
+#define GET_UINT1_AT_PC(pc) \
TclGetUInt1AtPtr(envPtr->codeStart + (pc))
#define GET_UINT4_AT_PC(pc) \
TclGetUInt4AtPtr(envPtr->codeStart + (pc))
-#define SET_INT1_AT_PC(i, pc) \
+#define SET_INT1_AT_PC(i, pc) \
TclStoreInt1AtPtr((i), envPtr->codeStart + (pc))
#define SET_INT4_AT_PC(i, pc) \
@@ -65,395 +84,296 @@ static void CompactCode(CompileEnv *envPtr);
#define NEXT_PC(pc) \
pc + InstLength(INST_AT_PC(pc))
+
+#define MARK(pc) \
+ markPath(envPtr, (pc), padPtr, /* mark */ 1)
+
+#define UNMARK(pc) \
+ markPath(envPtr, (pc), padPtr, /* mark */ 0)
+
+#define REPLACE(old, new) \
+ MARK(new); UNMARK(old)
+
+#define UPDATE_JUMP(pc) \
+ UpdateJump(envPtr, padPtr, (pc))
+
+#define UNSHARED(pc) (PATHS[pc] == 1)
+
+
+/*
+ * The code for following a path from a given PC.
+ */
+
+#define FOLLOW(pc) \
+ effectivePC(envPtr, (pc), padPtr)
+
+static inline int
+effectivePC(
+ CompileEnv *envPtr,
+ int pc,
+ optPad *padPtr)
+{
+ unsigned char inst = INST_AT_PC(pc);
+
+ if ((inst == INST_NOP)
+ || (inst == INST_JUMP1)
+ || (inst == INST_JUMP4)) {
+ /* do update the whole path forward */
+ pc = effectivePCInternal(envPtr, pc, padPtr, pc);
+ }
+ return pc;
+}
+
+
+static void Initialize(CompileEnv *envPtr, optPad *padPtr);
+
/*
* ----------------------------------------------------------------------
*
- * LocateTargetAddresses --
+ * OptimizeBytecode --
*
- * Populate a hash table with places that we need to be careful around
- * because they're the targets of various kinds of jumps and other
- * non-local behavior.
+ * An optimizer for bytecode to replace TclOptimizeBytecode.
*
* ----------------------------------------------------------------------
*/
-static void
-LocateTargetAddresses(
- CompileEnv *envPtr,
- Tcl_HashTable *tablePtr)
+void
+TclOptimizeBytecode(
+ void *env1Ptr)
{
- unsigned char *currentInstPtr, *targetInstPtr;
- int isNew, i;
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch hSearch;
+ CompileEnv *envPtr = (CompileEnv *) env1Ptr;
+ int codeSize = (envPtr->codeNext - envPtr->codeStart);
+ int padSize = sizeof(optPad) + 4*codeSize*sizeof(int);
+ optPad *padPtr;
+
+ padPtr = (optPad *) Tcl_AttemptAlloc(padSize);
+ if (!padPtr) {
+ /* Not enough memory to optimize this code */
+ Tcl_Panic("** Not enough mem to optimize! **");
+ return;
+ }
+ padPtr->codeSize = codeSize;
+ padPtr->cache = -1;
+ padPtr->npaths = &padPtr->first;
+ padPtr->scratch = &padPtr->npaths[2*codeSize + 1];
- Tcl_InitHashTable(tablePtr, TCL_ONE_WORD_KEYS);
+ Tcl_InitHashTable(&padPtr->stack, TCL_ONE_WORD_KEYS);
- /*
- * The starts of commands represent target addresses.
- */
+ /* Simplify the code as much as possible without knowing the paths */
- for (i=0 ; i<envPtr->numCommands ; i++) {
- DefineTargetAddress(tablePtr,
- envPtr->codeStart + envPtr->cmdMapPtr[i].codeOffset);
- }
+ /* 1. Path-independent opts: push elimination, jump threading, etc. We
+ * could advance some jumps (to cond-jump, to done or return) */
- /*
- * Find places where we should be careful about replacing instructions
- * because they are the targets of various types of jumps.
- */
+ Optimize_0(envPtr, padPtr);
+
+ /* 2. Initial path marking, move unreachable code to after INST_DONE and
+ * compress */
- for (currentInstPtr = envPtr->codeStart ;
- currentInstPtr < envPtr->codeNext ;
- currentInstPtr += AddrLength(currentInstPtr)) {
- switch (*currentInstPtr) {
- case INST_JUMP1:
- case INST_JUMP_TRUE1:
- case INST_JUMP_FALSE1:
- targetInstPtr = currentInstPtr+TclGetInt1AtPtr(currentInstPtr+1);
- goto storeTarget;
- case INST_JUMP4:
- case INST_JUMP_TRUE4:
- case INST_JUMP_FALSE4:
- case INST_START_CMD:
- targetInstPtr = currentInstPtr+TclGetInt4AtPtr(currentInstPtr+1);
- goto storeTarget;
- case INST_BEGIN_CATCH4:
- targetInstPtr = envPtr->codeStart + envPtr->exceptArrayPtr[
- TclGetUInt4AtPtr(currentInstPtr+1)].codeOffset;
- storeTarget:
- DefineTargetAddress(tablePtr, targetInstPtr);
- break;
- case INST_JUMP_TABLE:
- hPtr = Tcl_FirstHashEntry(
- &JUMPTABLEINFO(envPtr, currentInstPtr+1)->hashTable,
- &hSearch);
- for (; hPtr ; hPtr = Tcl_NextHashEntry(&hSearch)) {
- targetInstPtr = currentInstPtr +
- PTR2INT(Tcl_GetHashValue(hPtr));
- DefineTargetAddress(tablePtr, targetInstPtr);
- }
- break;
- case INST_RETURN_CODE_BRANCH:
- for (i=TCL_ERROR ; i<TCL_CONTINUE+1 ; i++) {
- DefineTargetAddress(tablePtr, currentInstPtr + 2*i - 1);
- }
- break;
- }
- }
+ Initialize(envPtr, padPtr);
+ /* 3. Iterate path-dependent optimizations until all done; this can
+ * conceivably enable new path-independent opts, so try again. Note that
+ * paths need to be kept up-to-date for this to work properly - careful
+ * with backwards jumps (? think properly). Maybe separate jump-threading
+ * from path computations. */
+ /* TODO: there MUST be a more efficient approach than relaxation */
+
+ Optimize_1(envPtr, padPtr);
+
+ /* Finally remove all nops and unreachable code, reduce code size */
+ CompactCode(envPtr, padPtr, 1);
+
+ Tcl_DeleteHashTable(&padPtr->stack);
+ Tcl_Free((char *) padPtr);
+}
+
+static void
+Initialize(
+ CompileEnv *envPtr,
+ optPad *padPtr)
+{
+ INIT_PATHS; INIT_SIZE;
+ int i;
+
/*
- * Add a marker *after* the last bytecode instruction. WARNING: points to
- * one past the end!
+ * Initialize NEXT to identity, PATHS to 0.
*/
- DefineTargetAddress(tablePtr, currentInstPtr);
+ for (i=0; i < codeSize; i++) {
+ PATHS[i] = 0;
+ }
/*
- * Enter in the targets of exception ranges.
+ * Compute the paths to reachable code
*/
+ MARK(0);
+
for (i=0 ; i<envPtr->exceptArrayNext ; i++) {
ExceptionRange *rangePtr = &envPtr->exceptArrayPtr[i];
- if (rangePtr->type == CATCH_EXCEPTION_RANGE) {
- targetInstPtr = envPtr->codeStart + rangePtr->catchOffset;
- DefineTargetAddress(tablePtr, targetInstPtr);
- } else {
- targetInstPtr = envPtr->codeStart + rangePtr->breakOffset;
- DefineTargetAddress(tablePtr, targetInstPtr);
- if (rangePtr->continueOffset >= 0) {
- targetInstPtr = envPtr->codeStart + rangePtr->continueOffset;
- DefineTargetAddress(tablePtr, targetInstPtr);
+ if ((rangePtr->type == LOOP_EXCEPTION_RANGE) &&
+ PATHS[rangePtr->codeOffset]) {
+ if (!PATHS[rangePtr->breakOffset]) {
+ MARK(rangePtr->breakOffset);
+ }
+ if ((rangePtr->continueOffset >= 0)
+ && !PATHS[rangePtr->continueOffset]){
+ MARK(rangePtr->continueOffset);
}
}
}
+ MoveUnreachable(envPtr, padPtr);
+ CompactCode(envPtr, padPtr, 0);
}
/*
* ----------------------------------------------------------------------
*
- * TrimUnreachable --
+ * Optimize --
*
- * Converts code that provably can't be executed into NOPs and reduces
- * the overall reported length of the bytecode where that is possible.
+ * Replaces instructions and threads jumps in order to speed up the
+ * execution. It also marks unreachable code, replacing it with NOPS that
+ * can later be removed.
*
* ----------------------------------------------------------------------
*/
-static void
-TrimUnreachable(
- CompileEnv *envPtr)
-{
- unsigned char *currentInstPtr;
- Tcl_HashTable targets;
+#define MODIFIED \
+ goto restart
- LocateTargetAddresses(envPtr, &targets);
+void
+Optimize_0(
+ CompileEnv *envPtr,
+ optPad *padPtr)
+{
+ int codeSize = padPtr->codeSize;
+ int pc, nextpc;
+ unsigned char inst;
- for (currentInstPtr = envPtr->codeStart ;
- currentInstPtr < envPtr->codeNext-1 ;
- currentInstPtr += AddrLength(currentInstPtr)) {
- int clear = 0;
+ for (pc = 0; pc < codeSize; pc = nextpc) {
+ nextpc = NEXT_PC(pc);
+ inst = INST_AT_PC(pc);
+
+ switch(inst) {
+ case INST_RETURN_CODE_BRANCH:
+ nextpc += 10;
+ continue;
- if (*currentInstPtr != INST_DONE) {
- continue;
- }
+ case INST_PUSH4:
+ optimizePush_0(envPtr, padPtr, pc);
- while (!IsTargetAddress(&targets, currentInstPtr + 1 + clear)) {
- clear += AddrLength(currentInstPtr + 1 + clear);
- }
- if (currentInstPtr + 1 + clear == envPtr->codeNext) {
- envPtr->codeNext -= clear;
- } else {
- while (clear --> 0) {
- *(currentInstPtr + 1 + clear) = INST_NOP;
- }
+ case INST_JUMP1:
+ case INST_JUMP_TRUE1:
+ case INST_JUMP_FALSE1:
+ case INST_JUMP_TRUE4:
+ case INST_JUMP_FALSE4:
+ case INST_JUMP4:
+ case INST_JUMP_TABLE:
+ UPDATE_JUMP(pc);
+ break;
+
}
}
-
- Tcl_DeleteHashTable(&targets);
}
-
-/*
- * ----------------------------------------------------------------------
- *
- * ConvertZeroEffectToNOP --
- *
- * Replace PUSH/POP sequences (when non-hazardous) with NOPs. Also
- * replace PUSH empty/STR_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.
- *
- * ----------------------------------------------------------------------
- */
-static void
-ConvertZeroEffectToNOP(
- CompileEnv *envPtr)
+void
+Optimize_1(
+ CompileEnv *envPtr,
+ optPad *padPtr)
{
- unsigned char *currentInstPtr;
- int size;
- Tcl_HashTable targets;
-
- LocateTargetAddresses(envPtr, &targets);
- for (currentInstPtr = envPtr->codeStart ;
- currentInstPtr < envPtr->codeNext ; currentInstPtr += size) {
- int blank = 0, i, nextInst;
-
- size = AddrLength(currentInstPtr);
- while ((currentInstPtr + size < envPtr->codeNext)
- && *(currentInstPtr+size) == INST_NOP) {
- if (IsTargetAddress(&targets, currentInstPtr + size)) {
- break;
- }
- size += InstLength(INST_NOP);
- }
- if (IsTargetAddress(&targets, currentInstPtr + size)) {
- continue;
- }
- nextInst = *(currentInstPtr + size);
- switch (*currentInstPtr) {
- case INST_PUSH1:
- if (nextInst == INST_POP) {
- blank = size + InstLength(nextInst);
- } else if (nextInst == INST_STR_CONCAT1
- && TclGetUInt1AtPtr(currentInstPtr + size + 1) == 2) {
- Tcl_Obj *litPtr = TclFetchLiteral(envPtr,
- TclGetUInt1AtPtr(currentInstPtr + 1));
- int numBytes;
-
- (void) Tcl_GetStringFromObj(litPtr, &numBytes);
- if (numBytes == 0) {
- blank = size + InstLength(nextInst);
- }
- }
- break;
- case INST_PUSH4:
- if (nextInst == INST_POP) {
- blank = size + 1;
- } else if (nextInst == INST_STR_CONCAT1
- && TclGetUInt1AtPtr(currentInstPtr + size + 1) == 2) {
- Tcl_Obj *litPtr = TclFetchLiteral(envPtr,
- TclGetUInt4AtPtr(currentInstPtr + 1));
- int numBytes;
-
- (void) Tcl_GetStringFromObj(litPtr, &numBytes);
- if (numBytes == 0) {
- blank = size + InstLength(nextInst);
- }
- }
- break;
+ INIT_PATHS;
+ int codeSize = padPtr->codeSize;
+ int pc, nextpc, tmp;
+ unsigned char inst;
- case INST_LNOT:
- switch (nextInst) {
+ restart:
+ ThreadJumps(envPtr, padPtr);
+ for (pc = 0; pc < codeSize; pc = nextpc) {
+ nextpc = NEXT_PC(pc);
+ inst = INST_AT_PC(pc);
+ if ((inst == INST_NOP) || !PATHS[pc]) continue;
+
+ switch(inst) {
+ case INST_RETURN_CODE_BRANCH:
+ nextpc += 10;
+ continue;
+
+ case INST_JUMP1:
case INST_JUMP_TRUE1:
- blank = size;
- *(currentInstPtr + size) = INST_JUMP_FALSE1;
- break;
case INST_JUMP_FALSE1:
- blank = size;
- *(currentInstPtr + size) = INST_JUMP_TRUE1;
- break;
- case INST_JUMP_TRUE4:
- blank = size;
- *(currentInstPtr + size) = INST_JUMP_FALSE4;
+ //Tcl_Panic("found a 1-jump!");
break;
- case INST_JUMP_FALSE4:
- blank = size;
- *(currentInstPtr + size) = INST_JUMP_TRUE4;
+
+ case INST_PUSH4:
+ if (optimizePush_1(envPtr, padPtr, pc)) {
+ if (!UNSHARED(pc)) MODIFIED;
+ }
break;
- }
- break;
- case INST_TRY_CVT_TO_NUMERIC:
- switch (nextInst) {
- 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;
- }
+ case INST_JUMP_FALSE4: {
+ int t1, t, tgt, new;
- if (blank > 0) {
- for (i=0 ; i<blank ; i++) {
- *(currentInstPtr + i) = INST_NOP;
- }
- size = blank;
- }
- }
- Tcl_DeleteHashTable(&targets);
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * AdvanceJumps --
- *
- * Advance jumps past NOPs and chained JUMPs. After this runs, the only
- * JUMPs that jump to a NOP or a JUMP will be length-1 ones that run out
- * of room in their opcode to be targeted to where they really belong.
- *
- * ----------------------------------------------------------------------
- */
-
-static void
-AdvanceJumps(
- CompileEnv *envPtr)
-{
- unsigned char *currentInstPtr;
- Tcl_HashTable jumps;
-
- for (currentInstPtr = envPtr->codeStart ;
- currentInstPtr < envPtr->codeNext-1 ;
- currentInstPtr += AddrLength(currentInstPtr)) {
- int offset, delta, isNew;
-
- redo:
- switch (*currentInstPtr) {
- case INST_PUSH4: {
- if (*(currentInstPtr + AddrLength(currentInstPtr)) == INST_POP) {
- *currentInstPtr = INST_JUMP4;
- TclStoreInt4AtPtr(6, currentInstPtr + 1);
- goto redo;
- }
- continue;
- }
+ /*
+ * detect stupid jump-around-jump, untangle
+ *
+ * <-- PC: CONDJMP->OLD !CONDJMP->TGT
+ * | T1: JMP->TGT <=> NOP
+ * --> OLD: FOO FOO
+ */
- case INST_JUMP1:
- case INST_JUMP_TRUE1:
- case INST_JUMP_FALSE1:
- offset = TclGetInt1AtPtr(currentInstPtr + 1);
- Tcl_InitHashTable(&jumps, TCL_ONE_WORD_KEYS);
- for (delta=0 ; offset+delta != 0 ;) {
- if (offset + delta < -128 || offset + delta > 127) {
- break;
- }
- Tcl_CreateHashEntry(&jumps, INT2PTR(offset), &isNew);
- if (!isNew) {
- offset = TclGetInt1AtPtr(currentInstPtr + 1);
- break;
- }
- offset += delta;
- switch (*(currentInstPtr + offset)) {
- case INST_NOP:
- delta = InstLength(INST_NOP);
- continue;
- case INST_JUMP1:
- delta = TclGetInt1AtPtr(currentInstPtr + offset + 1);
- continue;
- case INST_JUMP4:
- delta = TclGetInt4AtPtr(currentInstPtr + offset + 1);
- continue;
+ if (UPDATE_JUMP(pc)) MODIFIED;
+ new = pc + GET_INT4_AT_PC(pc+1);
+ for (t1 = NEXT_PC(pc);
+ (INST_AT_PC(t1) == INST_NOP) && UNSHARED(t1);
+ t1++) {};
+ if ((INST_AT_PC(t1) == INST_JUMP4) && UNSHARED(t1)) {
+ if (new == FOLLOW(NEXT_PC(t1))) {
+ /* ENTANGLED! undo ... */
+ tgt = t1 + GET_INT4_AT_PC(t1+1);
+ for (t = t1; t < t1 + 5; t++) {
+ INST_AT_PC(t) = INST_NOP;
+ PATHS[t] = 0;
+ }
+ t = pc + GET_INT4_AT_PC(pc+1);
+ REPLACE(t, t1);
+
+ t = FOLLOW(tgt);
+ REPLACE(tgt, t);
+ INST_AT_PC(pc) ^= 2;
+ SET_INT4_AT_PC(t-pc, pc+1);
+ if (!UNSHARED(pc)) MODIFIED;
+ }
}
break;
}
- Tcl_DeleteHashTable(&jumps);
- TclStoreInt1AtPtr(offset, currentInstPtr + 1);
- continue;
- case INST_JUMP4:
- case INST_JUMP_TRUE4:
- case INST_JUMP_FALSE4:
- Tcl_InitHashTable(&jumps, TCL_ONE_WORD_KEYS);
- Tcl_CreateHashEntry(&jumps, INT2PTR(0), &isNew);
- for (offset = TclGetInt4AtPtr(currentInstPtr + 1); offset!=0 ;) {
- Tcl_CreateHashEntry(&jumps, INT2PTR(offset), &isNew);
- if (!isNew) {
- offset = TclGetInt4AtPtr(currentInstPtr + 1);
- break;
- }
- switch (*(currentInstPtr + offset)) {
- case INST_NOP:
- offset += InstLength(INST_NOP);
- continue;
- case INST_JUMP1:
- offset += TclGetInt1AtPtr(currentInstPtr + offset + 1);
- continue;
- case INST_JUMP4:
- offset += TclGetInt4AtPtr(currentInstPtr + offset + 1);
- continue;
+ case INST_JUMP4:
+ if (UPDATE_JUMP(pc)) MODIFIED;
+ break;
+
+ case INST_LNOT:
+ tmp = FOLLOW(nextpc);
+ if (UNSHARED(tmp)) {
+ inst = INST_AT_PC(tmp);
+ switch(inst) {
+ case INST_JUMP_TRUE1:
+ case INST_JUMP_FALSE1:
+ case INST_JUMP_TRUE4:
+ case INST_JUMP_FALSE4:
+ /* Trick: this transforms to the negation! */
+ INST_AT_PC(tmp) ^= 2;
+ INST_AT_PC(pc) = INST_NOP;
+ if (!UNSHARED(pc)) MODIFIED;
+ }
}
break;
- }
- Tcl_DeleteHashTable(&jumps);
- TclStoreInt4AtPtr(offset, currentInstPtr + 1);
- continue;
}
}
}
+#undef MODIFIED
/*
* ----------------------------------------------------------------------
@@ -467,14 +387,17 @@ AdvanceJumps(
* ----------------------------------------------------------------------
*/
-static void
+void
CompactCode(
- CompileEnv *envPtr)
+ CompileEnv *envPtr,
+ optPad *padPtr,
+ int shrinkInst)
{
- int pc, nops;
- int inst, i, nextpc;
- int codeSize = (envPtr->codeNext - envPtr->codeStart);
- int *NEW = (int*) ckalloc(sizeof(int)*(codeSize + 1));
+ int codeSize = padPtr->codeSize;
+ int *PATHS = padPtr->npaths;
+ int *NEW = padPtr->scratch;
+ int pc, nops, i, nextpc;
+ unsigned char inst;
/*
* First pass: shrink jumps and push, compute new positions.
@@ -490,20 +413,24 @@ CompactCode(
inst = INST_AT_PC(pc);
NEW[pc] = pc - nops; /* new position */
+ if ((inst == INST_NOP) || !PATHS[pc]) {
+ nops += nextpc - pc;
+ continue;
+ }
+
resize = 0;
switch (inst) {
- case INST_NOP:
- nops++;
- break;
-
case INST_RETURN_CODE_BRANCH:
/* do not count NOPS in the special jump targets: skip them
* altogether */
- for (i = 1; i < 11; i++) {
- NEW[pc+i] = pc + i - nops;
- }
- nextpc += 10;
+ for (i = 0; i < 10; i++) {
+ NEW[nextpc] = nextpc - nops;
+ if (PATHS[nextpc] == 0) {
+ PATHS[nextpc] = 1;
+ }
+ nextpc++;
+ }
break;
case INST_PUSH4:
@@ -523,7 +450,7 @@ CompactCode(
break;
}
- if (resize) {
+ if (shrinkInst && resize) {
/*
* INST_XXX1 is always one less than INST_XXX4
*/
@@ -538,7 +465,7 @@ CompactCode(
}
if (nops == 0) {
- goto done;
+ return;
}
NEW[codeSize] = codeSize - nops;
@@ -552,7 +479,7 @@ CompactCode(
nextpc = NEXT_PC(pc);
inst = INST_AT_PC(pc);
- if (inst == INST_NOP) {
+ if ((inst == INST_NOP) || !PATHS[pc]) {
continue;
}
@@ -565,16 +492,6 @@ CompactCode(
Tcl_HashEntry *hPtr;
Tcl_HashSearch hSearch;
- case INST_RETURN_CODE_BRANCH:
- /*
- * Careful with NOPs in the next 10 bytes: they NEED to stay,
- * and their jumps NEED to be updated. Currently only JUMP1
- * or RETURN_STK/DONE are compiled for those destinations so
- * it is not a problem.
- *
- */
- break;
-
case INST_JUMP1:
case INST_JUMP_TRUE1:
case INST_JUMP_FALSE1:
@@ -596,25 +513,28 @@ CompactCode(
target = pc + GET_INT4_AT_PC(pc+1);
offset = NEW[target]-NEW[pc];
SET_INT4_AT_PC(offset, pc+1);
- if (inst == INST_START_CMD) break;
- if ((offset < 127) && (offset > -128)) {
+ if (inst != INST_START_CMD) {
if (offset == 5) {
if (inst == INST_JUMP4) {
INST_AT_PC(pc) = INST_NOP;
INST_AT_PC(pc+1) = INST_NOP;
nops += 2;
- } else {
+ goto push3nops;
+ } else if (shrinkInst) {
INST_AT_PC(pc) -= 1;
SET_INT1_AT_PC(2, pc+1);
+ goto push3nops;
}
- } else if ((offset < 127) && (offset > -128)) {
+ } else if (shrinkInst &&
+ (offset < 127) && (offset > -128)) {
INST_AT_PC(pc) -= 1;
SET_INT1_AT_PC(offset, pc+1);
+ push3nops:
+ INST_AT_PC(pc+2) = INST_NOP;
+ INST_AT_PC(pc+3) = INST_NOP;
+ INST_AT_PC(pc+4) = INST_NOP;
+ nops += 3;
}
- INST_AT_PC(pc+2) = INST_NOP;
- INST_AT_PC(pc+3) = INST_NOP;
- INST_AT_PC(pc+4) = INST_NOP;
- nops += 3;
}
break;
@@ -636,13 +556,16 @@ CompactCode(
break;
}
- /* move up opcode and operands, eliminate */
+ /* move up opcode and operands, en block */
for (i=0; pc+i < nextpc; i++) {
- INST_AT_PC(NEW[pc]+i) = INST_AT_PC(pc+i);
+ int old = pc+i, new = NEW[pc] + i;
+ INST_AT_PC(new) = INST_AT_PC(old);
+ PATHS[new] = PATHS[old];
}
}
envPtr->codeNext = envPtr->codeStart + NEW[codeSize];
-
+ padPtr->codeSize = NEW[codeSize];
+
/*
* Update range targets
*/
@@ -695,43 +618,636 @@ CompactCode(
/*
* Restart until all done; should be rare. Other possible criteria:
- * - restart if nops > x*codeSize
+ * - restart if (nops > x*codeSize), if new jumps, ...
* - use back jumps as loop indicators, restart only if some backjmp is
* reduced in size
* - don't restart, bet that there's not much more to be done
*/
if (nops) {
- codeSize = NEW[codeSize];
+ codeSize = padPtr->codeSize;
goto restart;
}
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * effectivePC, effectivePCinternal --
+ *
+ * Utility functions. Find the effective newpc that will be executed when
+ * we get at pc, by following through jumps and nops. The results are
+ * cached in the NEXT array.
+ *
+ * Side effects: may update both the NEXT and PATHS arrays.
+ *
+ * Remark: both arrays need to be initialized on first call; NEXT to the
+ * identity and PATHS to 0 (as this may call MARK/UNMARK which
+ * require that).
+ * ----------------------------------------------------------------------
+ */
+
+int
+effectivePCInternal(
+ CompileEnv *envPtr,
+ int pc,
+ optPad *padPtr,
+ int start)
+{
+ int old, new;
+ unsigned char inst;
- done:
- ckfree((char*) NEW);
+ /* recurse so that the whole path forward is updated and cached */
+
+ inst = INST_AT_PC(pc);
+ switch (inst) {
+ case INST_NOP:
+ old = pc + 1;
+ break;
+
+ case INST_JUMP1:
+ old = pc + GET_INT1_AT_PC(pc+1);
+ break;
+
+ case INST_JUMP4:
+ old = pc + GET_INT4_AT_PC(pc+1);
+ break;
+
+ default:
+ return pc;
+ }
+
+ if (start == old) {
+ /*
+ * INFINITE LOOP! TODO
+ * Eventually insert error generating code, possibly after INST_DONE?
+ */
+ return pc;
+ }
+
+ new = FOLLOW(old);
+ return new;
+}
+
+int
+UpdateJump(
+ CompileEnv *envPtr,
+ optPad *padPtr,
+ int pc)
+{
+ int old, new, inst;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch hSearch;
+ Tcl_HashTable *tPtr;
+ int result = 0;
+
+ inst = INST_AT_PC(pc);
+
+ switch(inst) {
+ default:
+ return 0;
+
+ case INST_JUMP_TABLE:
+ tPtr = &JUMPTABLEINFO(envPtr, envPtr->codeStart+pc+1)->hashTable;
+
+ hPtr = Tcl_FirstHashEntry(tPtr, &hSearch);
+ for (; hPtr ; hPtr = Tcl_NextHashEntry(&hSearch)) {
+ old = pc + PTR2INT(Tcl_GetHashValue(hPtr));
+ new = FOLLOW(old);
+ if (new != old) {
+ //REPLACE(old, new);
+ Tcl_SetHashValue(hPtr, INT2PTR(new - pc));
+ result = 1;
+ }
+ }
+ return result;
+
+ case INST_JUMP_TRUE4:
+ case INST_JUMP_FALSE4:
+ case INST_JUMP4:
+ old = pc + GET_INT4_AT_PC(pc+1);
+ break;
+ }
+
+ new = FOLLOW(old);
+ if (new == old) {
+ return 0;
+ }
+ SET_INT4_AT_PC(new - pc, pc+1);
+ //REPLACE(old, new);
+ return 1;
+}
+
+void
+ThreadJumps(
+ CompileEnv *envPtr,
+ optPad *padPtr)
+{
+ INIT_SIZE;
+ int pc, nextpc;
+ unsigned char inst;
+
+ for (pc = 0; pc < codeSize; pc = nextpc) {
+ nextpc = NEXT_PC(pc);
+ inst = INST_AT_PC(pc);
+
+ switch(inst) {
+ /* TODO:
+ * - condJmp around a non-target jmp -> invert the condition,
+ * remove the jump!
+ * - advance jump to unshared cond-jmp? error reporting problems,
+ * same as advancing done (when caught ... can it be?)
+ */
+
+ case INST_JUMP_TRUE4:
+ case INST_JUMP_FALSE4:
+ case INST_JUMP4:
+ case INST_JUMP_TABLE:
+ UPDATE_JUMP(pc);
+ break;
+ }
+ }
}
/*
* ----------------------------------------------------------------------
*
- * TclOptimizeBytecode --
+ * markPath --
*
- * A very simple peephole optimizer for bytecode.
+ * Count the number of paths reaching an instruction, following the jumps
+ * as possible.
*
* ----------------------------------------------------------------------
*/
+#define PUSH(pc) \
+ iPUSH((pc), padPtr, mark)
+
+#define POP(pc) \
+ ((pc) = iPOP(padPtr))
+
+static void
+iPUSH(
+ int pc,
+ optPad *padPtr,
+ int mark)
+{
+ INIT_SIZE; INIT_PATHS; INIT_STACK;
+ int tmp;
+ int *cachePtr = &padPtr->cache;
+ int cached = *cachePtr;
+
+ if (pc >= codeSize) return;
+
+ tmp = ((!mark && (--PATHS[pc] == 0))
+ || (mark && (++PATHS[pc] == 1)));
+
+ if (tmp) {
+ if (cached != -1) {
+ Tcl_CreateHashEntry(stackPtr, INT2PTR(cached), &tmp);
+ }
+ *cachePtr = pc;
+ }
+}
+
+static int
+iPOP(
+ optPad *padPtr)
+{
+ INIT_STACK;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch hSearch;
+ int pc;
+ int cached = padPtr->cache;
+
+ if (cached != -1) {
+ padPtr->cache = -1;
+ return cached;
+ }
+
+ hPtr = Tcl_FirstHashEntry(stackPtr, &hSearch);
+ if (!hPtr) {
+ return -1;
+ }
+
+ pc = PTR2INT(Tcl_GetHashKey(stackPtr, hPtr));
+ Tcl_DeleteHashEntry(hPtr);
+ return pc;
+}
+
void
-TclOptimizeBytecode(
- void *envPtr)
+markPath(
+ CompileEnv *envPtr,
+ int pc,
+ optPad *padPtr,
+ int mark)
+{
+ INIT_PATHS;
+ unsigned char inst;
+ int nextpc, i, target;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch hSearch;
+
+ /*
+ * Note that each pc will be followed at most once, so that only branch
+ * targets have a PATHS count > 1.
+ */
+
+ if (mark) {
+ if (PATHS[pc] > 0) {
+ PATHS[pc]++;
+ return;
+ }
+ } else {
+ if (PATHS[pc] > 1) {
+ PATHS[pc]--;
+ return;
+ } else if (PATHS[pc] <=0) {
+ PATHS[pc] = 0;
+ return;
+ }
+ }
+
+ PUSH(pc);
+ while (POP(pc) != -1) {
+ inst = INST_AT_PC(pc);
+ nextpc = NEXT_PC(pc);
+ mark = (PATHS[pc] > 0);
+ switch(inst) {
+ case INST_DONE:
+ case INST_TAILCALL:
+ case INST_CONTINUE:
+ case INST_BREAK:
+ break;
+
+ case INST_JUMP_TRUE1:
+ case INST_JUMP_FALSE1:
+ PUSH(nextpc);
+ case INST_JUMP1:
+ PUSH(pc + GET_INT1_AT_PC(pc+1));
+ break;
+
+ case INST_JUMP_TRUE4:
+ case INST_JUMP_FALSE4:
+ PUSH(nextpc);
+ case INST_JUMP4:
+ target = pc + GET_INT4_AT_PC(pc+1);
+ if (mark) {
+ target = FOLLOW(target);
+ SET_INT4_AT_PC(target-pc, pc+1);
+ }
+ PUSH(target);
+ break;
+
+ case INST_JUMP_TABLE:
+ hPtr = Tcl_FirstHashEntry(
+ &JUMPTABLEINFO(envPtr, envPtr->codeStart+pc+1)->hashTable,
+ &hSearch);
+ for (; hPtr ; hPtr = Tcl_NextHashEntry(&hSearch)) {
+ target = pc + PTR2INT(Tcl_GetHashValue(hPtr));
+ if (mark) {
+ target = FOLLOW(target);
+ Tcl_SetHashValue(hPtr, INT2PTR(target - pc));
+ }
+ PUSH(target);
+ }
+ PUSH(nextpc);
+ break;
+
+ case INST_RETURN_CODE_BRANCH:
+ /*
+ * uncommon jumps: handle all here. Convert possible unreachable
+ * NOP to DONE, marked as reachable so that they are not
+ * eliminated. This is followed by 2-bytes each for codes: error,
+ * return, break, continue, other. After that, the code for ok.
+ */
+
+ for (i = 0; i < 5; i++, nextpc += 2) {
+ PUSH(nextpc);
+ if (InstLength(INST_AT_PC(nextpc)) == 1) {
+ PUSH(nextpc + 1);
+ }
+ }
+ break;
+
+ default:
+ PUSH(nextpc);
+ break;
+ }
+ }
+#undef PUSH
+#undef POP
+}
+
+/*
+ * Move exception handling code to after INST_DONE. ASSUMES that there are no
+ * FORCED 1-jumps (as after INST_RETURN_CODE_BRANCH) from exception handling
+ * code to normal code.
+ */
+
+
+void
+MoveUnreachable(
+ CompileEnv *envPtr,
+ optPad *padPtr)
+{
+ INIT_PATHS;
+ int *NEW = padPtr->scratch;
+ int pc, nextpc, target, inst;
+ int fixend, curr, i, imax;
+ ExceptionRange *rangePtr;
+ JumptableInfo *infoPtr;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch hSearch;
+ int codeSize = padPtr->codeSize;
+
+ /*
+ * Move unreachable code out to after done, after checking there is enough
+ * room to do it - be generous.
+ */
+
+ if ((envPtr->codeEnd - envPtr->codeStart) <
+ 2 * (envPtr->codeNext - envPtr->codeStart)) {
+ TclExpandCodeArray(envPtr);
+ }
+
+#if 1
+ for (pc = 0; pc < codeSize; pc = nextpc) {
+ nextpc = NEXT_PC(pc);
+
+ if (PATHS[pc] > 0) {
+ NEW[pc] = pc;
+ continue;
+ }
+
+ curr = (envPtr->codeNext - envPtr->codeStart);
+
+ inst = INST_AT_PC(pc);
+ fixend = (nextpc >= codeSize)? 0 : PATHS[nextpc];
+ NEW[pc] = curr;
+ INST_AT_PC(curr) = INST_AT_PC(pc);
+
+ switch (inst) {
+ case INST_JUMP1:
+ case INST_JUMP_FALSE1:
+ case INST_JUMP_TRUE1:
+ /*
+ * They should be all gone by now! OK not to move this code?
+ *
+ * Used to Tcl_Panic, but that is not good if this loaded on
+ * a standard Tcl that produces lots of 1-jumps.
+ */
+ break;
+
+
+ case INST_JUMP4:
+ fixend = 0;
+ case INST_JUMP_FALSE4:
+ case INST_JUMP_TRUE4:
+ target = pc + GET_INT4_AT_PC(pc+1);
+ SET_INT4_AT_PC(target, curr+1);
+ break;
+
+ case INST_JUMP_TABLE:
+ i = GET_UINT4_AT_PC(pc+1);
+ SET_INT4_AT_PC(i, curr+1);
+ infoPtr = (JumptableInfo *) TclFetchAuxData(envPtr, i);
+ hPtr = Tcl_FirstHashEntry(&infoPtr->hashTable, &hSearch);
+ for (; hPtr ; hPtr = Tcl_NextHashEntry(&hSearch)) {
+ target = pc + PTR2INT(Tcl_GetHashValue(hPtr));
+ Tcl_SetHashValue(hPtr, INT2PTR(target));
+ }
+ break;
+
+ case INST_RETURN_CODE_BRANCH:
+ /* let's hope there was no 1-jump to OK code! */
+ nextpc += 10;
+
+ default:
+ for (i=1; i < nextpc-pc; i++) {
+ INST_AT_PC(curr+i) = INST_AT_PC(pc+i);
+ NEW[pc+i] = curr+i;
+ }
+ break;
+ }
+ envPtr->codeNext += (nextpc-pc);
+
+ if (fixend) {
+ /* add a jump back to ok code */
+ curr = (envPtr->codeNext - envPtr->codeStart);
+ envPtr->codeNext += 5;
+ INST_AT_PC(curr) = INST_JUMP4;
+ SET_INT4_AT_PC(nextpc, curr+1);
+ }
+ }
+
+ /*
+ * The code has been moved, jump targets are all absolute. Pass over the
+ * moved code, updating jump targets and setting.
+ */
+
+ for (pc = codeSize;
+ pc < envPtr->codeNext-envPtr->codeStart;
+ pc = nextpc) {
+ nextpc = NEXT_PC(pc);
+ inst = INST_AT_PC(pc);
+ NEW[pc] = pc;
+ PATHS[pc] = 0;
+
+ switch (inst) {
+ case INST_JUMP4:
+ case INST_JUMP_FALSE4:
+ case INST_JUMP_TRUE4:
+ target = NEW[GET_INT4_AT_PC(pc+1)];
+ SET_INT4_AT_PC(target-pc, pc+1);
+ break;
+
+ case INST_JUMP_TABLE:
+ infoPtr = (JumptableInfo *) TclFetchAuxData(envPtr, GET_UINT4_AT_PC(pc+1));
+ hPtr = Tcl_FirstHashEntry(&infoPtr->hashTable, &hSearch);
+ for (; hPtr ; hPtr = Tcl_NextHashEntry(&hSearch)) {
+ target = NEW[PTR2INT(Tcl_GetHashValue(hPtr))];
+ Tcl_SetHashValue(hPtr, INT2PTR(target-pc));
+ }
+ break;
+ }
+ }
+
+ /* Reset things to normal */
+
+ padPtr->codeSize = envPtr->codeNext - envPtr->codeStart;
+
+#else
+ for (pc = 0; pc < codeSize; pc = nextpc) {
+ nextpc = NEXT_PC(pc);
+ NEW[pc] = pc;
+ }
+#endif
+
+ /* Restore all accessible ranges */
+
+ imax = envPtr->exceptArrayNext;
+ for (i = 0 ; i < imax; i++) {
+ rangePtr = &envPtr->exceptArrayPtr[i];
+ if (!PATHS[NEW[rangePtr->codeOffset]]) continue;
+
+ if (rangePtr->type == CATCH_EXCEPTION_RANGE) {
+ target = FOLLOW(NEW[rangePtr->catchOffset]);
+ rangePtr->catchOffset = target;
+ MARK(target);
+ } else {
+ rangePtr->breakOffset = FOLLOW(NEW[rangePtr->breakOffset]);
+ if (!PATHS[rangePtr->breakOffset]) {
+ MARK(rangePtr->breakOffset);
+ }
+ if (rangePtr->continueOffset >= 0) {
+ rangePtr->continueOffset = FOLLOW(NEW[rangePtr->continueOffset]);
+ if (!PATHS[rangePtr->continueOffset]){
+ MARK(rangePtr->continueOffset);
+ }
+ }
+ }
+ }
+}
+
+int
+optimizePush_0(
+ CompileEnv *envPtr,
+ optPad *padPtr,
+ int pc)
{
- int i = 2;
+ int inst, nextpc, i, target, tmp;
+ Tcl_Obj *objPtr;
+
+ nextpc = NEXT_PC(pc);
+
+ tmp = FOLLOW(nextpc);
+ inst = INST_AT_PC(tmp);
+ objPtr = envPtr->literalArrayPtr[GET_UINT4_AT_PC(pc+1)].objPtr;
+
+ switch (inst) {
+ case INST_POP:
+ tmp = FOLLOW(tmp + 1);
+ INST_AT_PC(pc) = INST_JUMP4;
+ SET_INT4_AT_PC(tmp - pc, pc+1);
+ //REPLACE(nextpc, tmp);
+ return 1;
+
+ case INST_JUMP_TRUE1:
+ case INST_JUMP_FALSE1:
+ case INST_JUMP_TRUE4:
+ case INST_JUMP_FALSE4:
+ if (Tcl_GetBooleanFromObj(NULL, objPtr, &i) == TCL_ERROR) {
+ return 0;
+ }
+ /* let i indicate if we take the jump or not */
+ if (i) {
+ i = ((inst == INST_JUMP_TRUE1) || (inst == INST_JUMP_TRUE4));
+ } else {
+ i = ((inst == INST_JUMP_FALSE1) || (inst == INST_JUMP_FALSE4));
+ }
+ if (i) {
+ switch (inst) {
+ case INST_JUMP_TRUE1:
+ case INST_JUMP_FALSE1:
+ target = tmp + GET_INT1_AT_PC(tmp+1);
+ break;
+
+ case INST_JUMP_TRUE4:
+ case INST_JUMP_FALSE4:
+ target = tmp + GET_INT4_AT_PC(tmp+1);
+ break;
+ }
+ } else {
+ target = NEXT_PC(tmp);
+ }
+ target = FOLLOW(target);
+ //REPLACE(nextpc, target);
+ INST_AT_PC(pc) = INST_JUMP4;
+ SET_INT4_AT_PC(target-pc, pc+1);
+ return 1;
+
+ case INST_LNOT:
+ break;
+
+ /* FIXME: TODO, eval and jump past it? No room for the
+ * jump. If unshared target, change the literal if it
+ * is a number. */
+
+ case INST_UMINUS:
+ case INST_UPLUS:
+ case INST_TRY_CVT_TO_NUMERIC:
+ case INST_LSHIFT:
+ case INST_RSHIFT:
+ case INST_BITNOT:
+ break;
+ }
+ return 0;
+}
+
+static inline int
+optPush1(
+ CompileEnv *envPtr,
+ optPad *padPtr,
+ int pc)
+{
+ INIT_PATHS;
+ int inst, nextpc, i, tmp;
+ Tcl_Obj *objPtr;
+
+ nextpc = NEXT_PC(pc);
+
+ redoPush:
+ tmp = FOLLOW(nextpc);
+ if (!UNSHARED(tmp)) {
+ return 0;
+ }
+
+ inst = INST_AT_PC(tmp);
+ objPtr = envPtr->literalArrayPtr[GET_UINT4_AT_PC(pc+1)].objPtr;
+ switch (inst) {
+ case INST_LNOT:
+ if (Tcl_GetBooleanFromObj(NULL, objPtr, &i) != TCL_ERROR) {
+ INST_AT_PC(tmp) = INST_NOP;
+ i = TclRegisterNewLiteral(envPtr, i? "0" : "1", 1);
+ SET_INT4_AT_PC(i, pc+1);
+ goto redoPush;
+ }
+ break;
+
+ /* FIXME: TODO, eval and jump past it? No room for the
+ * jump. If unshared target, change the literal if it
+ * is a number. */
+
+ case INST_UMINUS:
+ case INST_UPLUS:
+ case INST_TRY_CVT_TO_NUMERIC:
+ case INST_LSHIFT:
+ case INST_RSHIFT:
+ case INST_BITNOT:
+ break;
+ }
+ return 0;
+}
+
+
+int
+optimizePush_1(
+ CompileEnv *envPtr,
+ optPad *padPtr,
+ int pc)
+{
+ int res;
+
+ if (INST_AT_PC(pc) != INST_PUSH4) {
+ return 0;
+ }
- while (i--) {
- ConvertZeroEffectToNOP(envPtr);
- AdvanceJumps(envPtr);
- TrimUnreachable(envPtr);
- CompactCode(envPtr);
+ res = optimizePush_0(envPtr, padPtr, pc);
+ if (!res) {
+ res = optPush1(envPtr, padPtr, pc);
}
+ return res;
}
/*