OSDN Git Service

mrcImageOpticalFlow & mrcImageLucasKanade & mrcImageHornSchunckの変更
[eos/base.git] / util / src / TclTk / tcl8.6.12 / generic / tclAssembly.c
diff --git a/util/src/TclTk/tcl8.6.12/generic/tclAssembly.c b/util/src/TclTk/tcl8.6.12/generic/tclAssembly.c
new file mode 100644 (file)
index 0000000..bf86b90
--- /dev/null
@@ -0,0 +1,4350 @@
+/*
+ * tclAssembly.c --
+ *
+ *     Assembler for Tcl bytecodes.
+ *
+ * This file contains the procedures that convert Tcl Assembly Language (TAL)
+ * to a sequence of bytecode instructions for the Tcl execution engine.
+ *
+ * Copyright (c) 2010 by Ozgur Dogan Ugurlu.
+ * Copyright (c) 2010 by Kevin B. Kenny.
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+/*-
+ *- THINGS TO DO:
+ *- More instructions:
+ *-   done - alternate exit point (affects stack and exception range checking)
+ *-   break and continue - if exception ranges can be sorted out.
+ *-   foreach_start4, foreach_step4
+ *-   returnImm, returnStk
+ *-   expandStart, expandStkTop, invokeExpanded, expandDrop
+ *-   dictFirst, dictNext, dictDone
+ *-   dictUpdateStart, dictUpdateEnd
+ *-   jumpTable testing
+ *-   syntax (?)
+ *-   returnCodeBranch
+ *-   tclooNext, tclooNextClass
+ */
+
+#include "tclInt.h"
+#include "tclCompile.h"
+#include "tclOOInt.h"
+
+/*
+ * Structure that represents a range of instructions in the bytecode.
+ */
+
+typedef struct CodeRange {
+    int startOffset;           /* Start offset in the bytecode array */
+    int endOffset;             /* End offset in the bytecode array */
+} CodeRange;
+
+/*
+ * State identified for a basic block's catch context.
+ */
+
+typedef enum BasicBlockCatchState {
+    BBCS_UNKNOWN = 0,          /* Catch context has not yet been identified */
+    BBCS_NONE,                 /* Block is outside of any catch */
+    BBCS_INCATCH,              /* Block is within a catch context */
+    BBCS_CAUGHT                /* Block is within a catch context and
+                                * may be executed after an exception fires */
+} BasicBlockCatchState;
+
+/*
+ * Structure that defines a basic block - a linear sequence of bytecode
+ * instructions with no jumps in or out (including not changing the
+ * state of any exception range).
+ */
+
+typedef struct BasicBlock {
+    int originalStartOffset;   /* Instruction offset before JUMP1s were
+                                * substituted with JUMP4's */
+    int startOffset;           /* Instruction offset of the start of the
+                                * block */
+    int startLine;             /* Line number in the input script of the
+                                * instruction at the start of the block */
+    int jumpOffset;            /* Bytecode offset of the 'jump' instruction
+                                * that ends the block, or -1 if there is no
+                                * jump. */
+    int jumpLine;              /* Line number in the input script of the
+                                * 'jump' instruction that ends the block, or
+                                * -1 if there is no jump */
+    struct BasicBlock* prevPtr;        /* Immediate predecessor of this block */
+    struct BasicBlock* predecessor;
+                               /* Predecessor of this block in the spanning
+                                * tree */
+    struct BasicBlock* successor1;
+                               /* BasicBlock structure of the following
+                                * block: NULL at the end of the bytecode
+                                * sequence. */
+    Tcl_Obj* jumpTarget;       /* Jump target label if the jump target is
+                                * unresolved */
+    int initialStackDepth;     /* Absolute stack depth on entry */
+    int minStackDepth;         /* Low-water relative stack depth */
+    int maxStackDepth;         /* High-water relative stack depth */
+    int finalStackDepth;       /* Relative stack depth on exit */
+    enum BasicBlockCatchState catchState;
+                               /* State of the block for 'catch' analysis */
+    int catchDepth;            /* Number of nested catches in which the basic
+                                * block appears */
+    struct BasicBlock* enclosingCatch;
+                               /* BasicBlock structure of the last startCatch
+                                * executed on a path to this block, or NULL
+                                * if there is no enclosing catch */
+    int foreignExceptionBase;  /* Base index of foreign exceptions */
+    int foreignExceptionCount; /* Count of foreign exceptions */
+    ExceptionRange* foreignExceptions;
+                               /* ExceptionRange structures for exception
+                                * ranges belonging to embedded scripts and
+                                * expressions in this block */
+    JumptableInfo* jtPtr;      /* Jump table at the end of this basic block */
+    int flags;                 /* Boolean flags */
+} BasicBlock;
+
+/*
+ * Flags that pertain to a basic block.
+ */
+
+enum BasicBlockFlags {
+    BB_VISITED = (1 << 0),     /* Block has been visited in the current
+                                * traversal */
+    BB_FALLTHRU = (1 << 1),    /* Control may pass from this block to a
+                                * successor */
+    BB_JUMP1 = (1 << 2),       /* Basic block ends with a 1-byte-offset jump
+                                * and may need expansion */
+    BB_JUMPTABLE = (1 << 3),   /* Basic block ends with a jump table */
+    BB_BEGINCATCH = (1 << 4),  /* Block ends with a 'beginCatch' instruction,
+                                * marking it as the start of a 'catch'
+                                * sequence. The 'jumpTarget' is the exception
+                                * exit from the catch block. */
+    BB_ENDCATCH = (1 << 5)     /* Block ends with an 'endCatch' instruction,
+                                * unwinding the catch from the exception
+                                * stack. */
+};
+
+/*
+ * Source instruction type recognized by the assembler.
+ */
+
+typedef enum TalInstType {
+    ASSEM_1BYTE,               /* Fixed arity, 1-byte instruction */
+    ASSEM_BEGIN_CATCH,         /* Begin catch: one 4-byte jump offset to be
+                                * converted to appropriate exception
+                                * ranges */
+    ASSEM_BOOL,                        /* One Boolean operand */
+    ASSEM_BOOL_LVT4,           /* One Boolean, one 4-byte LVT ref. */
+    ASSEM_CLOCK_READ,          /* 1-byte unsigned-integer case number, in the
+                                * range 0-3 */
+    ASSEM_CONCAT1,             /* 1-byte unsigned-integer operand count, must
+                                * be strictly positive, consumes N, produces
+                                * 1 */
+    ASSEM_DICT_GET,            /* 'dict get' and related - consumes N+1
+                                * operands, produces 1, N > 0 */
+    ASSEM_DICT_SET,            /* specifies key count and LVT index, consumes
+                                * N+1 operands, produces 1, N > 0 */
+    ASSEM_DICT_UNSET,          /* specifies key count and LVT index, consumes
+                                * N operands, produces 1, N > 0 */
+    ASSEM_END_CATCH,           /* End catch. No args. Exception range popped
+                                * from stack and stack pointer restored. */
+    ASSEM_EVAL,                        /* 'eval' - evaluate a constant script (by
+                                * compiling it in line with the assembly
+                                * code! I love Tcl!) */
+    ASSEM_INDEX,               /* 4 byte operand, integer or end-integer */
+    ASSEM_INVOKE,              /* 1- or 4-byte operand count, must be
+                                * strictly positive, consumes N, produces
+                                * 1. */
+    ASSEM_JUMP,                        /* Jump instructions */
+    ASSEM_JUMP4,               /* Jump instructions forcing a 4-byte offset */
+    ASSEM_JUMPTABLE,           /* Jumptable (switch -exact) */
+    ASSEM_LABEL,               /* The assembly directive that defines a
+                                * label */
+    ASSEM_LINDEX_MULTI,                /* 4-byte operand count, must be strictly
+                                * positive, consumes N, produces 1 */
+    ASSEM_LIST,                        /* 4-byte operand count, must be nonnegative,
+                                * consumses N, produces 1 */
+    ASSEM_LSET_FLAT,           /* 4-byte operand count, must be >= 3,
+                                * consumes N, produces 1 */
+    ASSEM_LVT,                 /* One operand that references a local
+                                * variable */
+    ASSEM_LVT1,                        /* One 1-byte operand that references a local
+                                * variable */
+    ASSEM_LVT1_SINT1,          /* One 1-byte operand that references a local
+                                * variable, one signed-integer 1-byte
+                                * operand */
+    ASSEM_LVT4,                        /* One 4-byte operand that references a local
+                                * variable */
+    ASSEM_OVER,                        /* OVER: 4-byte operand count, consumes N+1,
+                                * produces N+2 */
+    ASSEM_PUSH,                        /* one literal operand */
+    ASSEM_REGEXP,              /* One Boolean operand, but weird mapping to
+                                * call flags */
+    ASSEM_REVERSE,             /* REVERSE: 4-byte operand count, consumes N,
+                                * produces N */
+    ASSEM_SINT1,               /* One 1-byte signed-integer operand
+                                * (INCR_STK_IMM) */
+    ASSEM_SINT4_LVT4           /* Signed 4-byte integer operand followed by
+                                * LVT entry.  Fixed arity */
+} TalInstType;
+
+/*
+ * Description of an instruction recognized by the assembler.
+ */
+
+typedef struct TalInstDesc {
+    const char *name;          /* Name of instruction. */
+    TalInstType instType;      /* The type of instruction */
+    int tclInstCode;           /* Instruction code. For instructions having
+                                * 1- and 4-byte variables, tclInstCode is
+                                * ((1byte)<<8) || (4byte) */
+    int operandsConsumed;      /* Number of operands consumed by the
+                                * operation, or INT_MIN if the operation is
+                                * variadic */
+    int operandsProduced;      /* Number of operands produced by the
+                                * operation. If negative, the operation has a
+                                * net stack effect of -1-operandsProduced */
+} TalInstDesc;
+
+/*
+ * Structure that holds the state of the assembler while generating code.
+ */
+
+typedef struct AssemblyEnv {
+    CompileEnv* envPtr;                /* Compilation environment being used for code
+                                * generation */
+    Tcl_Parse* parsePtr;       /* Parse of the current line of source */
+    Tcl_HashTable labelHash;   /* Hash table whose keys are labels and whose
+                                * values are 'label' objects storing the code
+                                * offsets of the labels. */
+    int cmdLine;               /* Current line number within the assembly
+                                * code */
+    int* clNext;               /* Invisible continuation line for
+                                * [info frame] */
+    BasicBlock* head_bb;       /* First basic block in the code */
+    BasicBlock* curr_bb;       /* Current basic block */
+    int maxDepth;              /* Maximum stack depth encountered */
+    int curCatchDepth;         /* Current depth of catches */
+    int maxCatchDepth;         /* Maximum depth of catches encountered */
+    int flags;                 /* Compilation flags (TCL_EVAL_DIRECT) */
+} AssemblyEnv;
+
+/*
+ * Static functions defined in this file.
+ */
+
+static void            AddBasicBlockRangeToErrorInfo(AssemblyEnv*,
+                           BasicBlock*);
+static BasicBlock *    AllocBB(AssemblyEnv*);
+static int             AssembleOneLine(AssemblyEnv* envPtr);
+static void            BBAdjustStackDepth(BasicBlock* bbPtr, int consumed,
+                           int produced);
+static void            BBUpdateStackReqs(BasicBlock* bbPtr, int tblIdx,
+                           int count);
+static void            BBEmitInstInt1(AssemblyEnv* assemEnvPtr, int tblIdx,
+                           int opnd, int count);
+static void            BBEmitInstInt4(AssemblyEnv* assemEnvPtr, int tblIdx,
+                           int opnd, int count);
+static void            BBEmitInst1or4(AssemblyEnv* assemEnvPtr, int tblIdx,
+                           int param, int count);
+static void            BBEmitOpcode(AssemblyEnv* assemEnvPtr, int tblIdx,
+                           int count);
+static int             BuildExceptionRanges(AssemblyEnv* assemEnvPtr);
+static int             CalculateJumpRelocations(AssemblyEnv*, int*);
+static int             CheckForUnclosedCatches(AssemblyEnv*);
+static int             CheckForThrowInWrongContext(AssemblyEnv*);
+static int             CheckNonThrowingBlock(AssemblyEnv*, BasicBlock*);
+static int             BytecodeMightThrow(unsigned char);
+static int             CheckJumpTableLabels(AssemblyEnv*, BasicBlock*);
+static int             CheckNamespaceQualifiers(Tcl_Interp*, const char*,
+                           int);
+static int             CheckNonNegative(Tcl_Interp*, int);
+static int             CheckOneByte(Tcl_Interp*, int);
+static int             CheckSignedOneByte(Tcl_Interp*, int);
+static int             CheckStack(AssemblyEnv*);
+static int             CheckStrictlyPositive(Tcl_Interp*, int);
+static ByteCode *      CompileAssembleObj(Tcl_Interp *interp,
+                           Tcl_Obj *objPtr);
+static void            CompileEmbeddedScript(AssemblyEnv*, Tcl_Token*,
+                           const TalInstDesc*);
+static int             DefineLabel(AssemblyEnv* envPtr, const char* label);
+static void            DeleteMirrorJumpTable(JumptableInfo* jtPtr);
+static void            DupAssembleCodeInternalRep(Tcl_Obj* src,
+                           Tcl_Obj* dest);
+static void            FillInJumpOffsets(AssemblyEnv*);
+static int             CreateMirrorJumpTable(AssemblyEnv* assemEnvPtr,
+                           Tcl_Obj* jumpTable);
+static int             FindLocalVar(AssemblyEnv* envPtr,
+                           Tcl_Token** tokenPtrPtr);
+static int             FinishAssembly(AssemblyEnv*);
+static void            FreeAssembleCodeInternalRep(Tcl_Obj *objPtr);
+static void            FreeAssemblyEnv(AssemblyEnv*);
+static int             GetBooleanOperand(AssemblyEnv*, Tcl_Token**, int*);
+static int             GetListIndexOperand(AssemblyEnv*, Tcl_Token**, int*);
+static int             GetIntegerOperand(AssemblyEnv*, Tcl_Token**, int*);
+static int             GetNextOperand(AssemblyEnv*, Tcl_Token**, Tcl_Obj**);
+static void            LookForFreshCatches(BasicBlock*, BasicBlock**);
+static void            MoveCodeForJumps(AssemblyEnv*, int);
+static void            MoveExceptionRangesToBasicBlock(AssemblyEnv*, int);
+static AssemblyEnv*    NewAssemblyEnv(CompileEnv*, int);
+static int             ProcessCatches(AssemblyEnv*);
+static int             ProcessCatchesInBasicBlock(AssemblyEnv*, BasicBlock*,
+                           BasicBlock*, enum BasicBlockCatchState, int);
+static void            ResetVisitedBasicBlocks(AssemblyEnv*);
+static void            ResolveJumpTableTargets(AssemblyEnv*, BasicBlock*);
+static void            ReportUndefinedLabel(AssemblyEnv*, BasicBlock*,
+                           Tcl_Obj*);
+static void            RestoreEmbeddedExceptionRanges(AssemblyEnv*);
+static int             StackCheckBasicBlock(AssemblyEnv*, BasicBlock *,
+                           BasicBlock *, int);
+static BasicBlock*     StartBasicBlock(AssemblyEnv*, int fallthrough,
+                           Tcl_Obj* jumpLabel);
+/* static int          AdvanceIp(const unsigned char *pc); */
+static int             StackCheckBasicBlock(AssemblyEnv*, BasicBlock *,
+                           BasicBlock *, int);
+static int             StackCheckExit(AssemblyEnv*);
+static void            StackFreshCatches(AssemblyEnv*, BasicBlock*, int,
+                           BasicBlock**, int*);
+static void            SyncStackDepth(AssemblyEnv*);
+static int             TclAssembleCode(CompileEnv* envPtr, const char* code,
+                           int codeLen, int flags);
+static void            UnstackExpiredCatches(CompileEnv*, BasicBlock*, int,
+                           BasicBlock**, int*);
+
+/*
+ * Tcl_ObjType that describes bytecode emitted by the assembler.
+ */
+
+static const Tcl_ObjType assembleCodeType = {
+    "assemblecode",
+    FreeAssembleCodeInternalRep, /* freeIntRepProc */
+    DupAssembleCodeInternalRep,         /* dupIntRepProc */
+    NULL,                       /* updateStringProc */
+    NULL                        /* setFromAnyProc */
+};
+
+/*
+ * Source instructions recognized in the Tcl Assembly Language (TAL)
+ */
+
+static const TalInstDesc TalInstructionTable[] = {
+    /* PUSH must be first, see the code near the end of TclAssembleCode */
+    {"push",           ASSEM_PUSH,     (INST_PUSH1<<8
+                                        | INST_PUSH4),         0,      1},
+
+    {"add",            ASSEM_1BYTE,    INST_ADD,               2,      1},
+    {"append",         ASSEM_LVT,      (INST_APPEND_SCALAR1<<8
+                                        | INST_APPEND_SCALAR4),1,      1},
+    {"appendArray",    ASSEM_LVT,      (INST_APPEND_ARRAY1<<8
+                                        | INST_APPEND_ARRAY4), 2,      1},
+    {"appendArrayStk", ASSEM_1BYTE,    INST_APPEND_ARRAY_STK,  3,      1},
+    {"appendStk",      ASSEM_1BYTE,    INST_APPEND_STK,        2,      1},
+    {"arrayExistsImm", ASSEM_LVT4,     INST_ARRAY_EXISTS_IMM,  0,      1},
+    {"arrayExistsStk", ASSEM_1BYTE,    INST_ARRAY_EXISTS_STK,  1,      1},
+    {"arrayMakeImm",   ASSEM_LVT4,     INST_ARRAY_MAKE_IMM,    0,      0},
+    {"arrayMakeStk",   ASSEM_1BYTE,    INST_ARRAY_MAKE_STK,    1,      0},
+    {"beginCatch",     ASSEM_BEGIN_CATCH,
+                                       INST_BEGIN_CATCH4,      0,      0},
+    {"bitand",         ASSEM_1BYTE,    INST_BITAND,            2,      1},
+    {"bitnot",         ASSEM_1BYTE,    INST_BITNOT,            1,      1},
+    {"bitor",          ASSEM_1BYTE,    INST_BITOR,             2,      1},
+    {"bitxor",         ASSEM_1BYTE,    INST_BITXOR,            2,      1},
+    {"clockRead",      ASSEM_CLOCK_READ, INST_CLOCK_READ,      0,      1},
+    {"concat",         ASSEM_CONCAT1,  INST_STR_CONCAT1,       INT_MIN,1},
+    {"concatStk",      ASSEM_LIST,     INST_CONCAT_STK,        INT_MIN,1},
+    {"coroName",       ASSEM_1BYTE,    INST_COROUTINE_NAME,    0,      1},
+    {"currentNamespace",ASSEM_1BYTE,   INST_NS_CURRENT,        0,      1},
+    {"dictAppend",     ASSEM_LVT4,     INST_DICT_APPEND,       2,      1},
+    {"dictExists",     ASSEM_DICT_GET, INST_DICT_EXISTS,       INT_MIN,1},
+    {"dictExpand",     ASSEM_1BYTE,    INST_DICT_EXPAND,       3,      1},
+    {"dictGet",                ASSEM_DICT_GET, INST_DICT_GET,          INT_MIN,1},
+    {"dictIncrImm",    ASSEM_SINT4_LVT4,
+                                       INST_DICT_INCR_IMM,     1,      1},
+    {"dictLappend",    ASSEM_LVT4,     INST_DICT_LAPPEND,      2,      1},
+    {"dictRecombineStk",ASSEM_1BYTE,   INST_DICT_RECOMBINE_STK,3,      0},
+    {"dictRecombineImm",ASSEM_LVT4,    INST_DICT_RECOMBINE_IMM,2,      0},
+    {"dictSet",                ASSEM_DICT_SET, INST_DICT_SET,          INT_MIN,1},
+    {"dictUnset",      ASSEM_DICT_UNSET,
+                                       INST_DICT_UNSET,        INT_MIN,1},
+    {"div",            ASSEM_1BYTE,    INST_DIV,               2,      1},
+    {"dup",            ASSEM_1BYTE,    INST_DUP,               1,      2},
+    {"endCatch",       ASSEM_END_CATCH,INST_END_CATCH,         0,      0},
+    {"eq",             ASSEM_1BYTE,    INST_EQ,                2,      1},
+    {"eval",           ASSEM_EVAL,     INST_EVAL_STK,          1,      1},
+    {"evalStk",                ASSEM_1BYTE,    INST_EVAL_STK,          1,      1},
+    {"exist",          ASSEM_LVT4,     INST_EXIST_SCALAR,      0,      1},
+    {"existArray",     ASSEM_LVT4,     INST_EXIST_ARRAY,       1,      1},
+    {"existArrayStk",  ASSEM_1BYTE,    INST_EXIST_ARRAY_STK,   2,      1},
+    {"existStk",       ASSEM_1BYTE,    INST_EXIST_STK,         1,      1},
+    {"expon",          ASSEM_1BYTE,    INST_EXPON,             2,      1},
+    {"expr",           ASSEM_EVAL,     INST_EXPR_STK,          1,      1},
+    {"exprStk",                ASSEM_1BYTE,    INST_EXPR_STK,          1,      1},
+    {"ge",             ASSEM_1BYTE,    INST_GE,                2,      1},
+    {"gt",             ASSEM_1BYTE,    INST_GT,                2,      1},
+    {"incr",           ASSEM_LVT1,     INST_INCR_SCALAR1,      1,      1},
+    {"incrArray",      ASSEM_LVT1,     INST_INCR_ARRAY1,       2,      1},
+    {"incrArrayImm",   ASSEM_LVT1_SINT1,
+                                       INST_INCR_ARRAY1_IMM,   1,      1},
+    {"incrArrayStk",   ASSEM_1BYTE,    INST_INCR_ARRAY_STK,    3,      1},
+    {"incrArrayStkImm", ASSEM_SINT1,   INST_INCR_ARRAY_STK_IMM,2,      1},
+    {"incrImm",                ASSEM_LVT1_SINT1,
+                                       INST_INCR_SCALAR1_IMM,  0,      1},
+    {"incrStk",                ASSEM_1BYTE,    INST_INCR_STK,          2,      1},
+    {"incrStkImm",     ASSEM_SINT1,    INST_INCR_STK_IMM,      1,      1},
+    {"infoLevelArgs",  ASSEM_1BYTE,    INST_INFO_LEVEL_ARGS,   1,      1},
+    {"infoLevelNumber",        ASSEM_1BYTE,    INST_INFO_LEVEL_NUM,    0,      1},
+    {"invokeStk",      ASSEM_INVOKE,   (INST_INVOKE_STK1 << 8
+                                        | INST_INVOKE_STK4),   INT_MIN,1},
+    {"jump",           ASSEM_JUMP,     INST_JUMP1,             0,      0},
+    {"jump4",          ASSEM_JUMP4,    INST_JUMP4,             0,      0},
+    {"jumpFalse",      ASSEM_JUMP,     INST_JUMP_FALSE1,       1,      0},
+    {"jumpFalse4",     ASSEM_JUMP4,    INST_JUMP_FALSE4,       1,      0},
+    {"jumpTable",      ASSEM_JUMPTABLE,INST_JUMP_TABLE,        1,      0},
+    {"jumpTrue",       ASSEM_JUMP,     INST_JUMP_TRUE1,        1,      0},
+    {"jumpTrue4",      ASSEM_JUMP4,    INST_JUMP_TRUE4,        1,      0},
+    {"label",          ASSEM_LABEL,    0,                      0,      0},
+    {"land",           ASSEM_1BYTE,    INST_LAND,              2,      1},
+    {"lappend",                ASSEM_LVT,      (INST_LAPPEND_SCALAR1<<8
+                                        | INST_LAPPEND_SCALAR4),
+                                                               1,      1},
+    {"lappendArray",   ASSEM_LVT,      (INST_LAPPEND_ARRAY1<<8
+                                        | INST_LAPPEND_ARRAY4),2,      1},
+    {"lappendArrayStk", ASSEM_1BYTE,   INST_LAPPEND_ARRAY_STK, 3,      1},
+    {"lappendList",    ASSEM_LVT4,     INST_LAPPEND_LIST,      1,      1},
+    {"lappendListArray",ASSEM_LVT4,    INST_LAPPEND_LIST_ARRAY,2,      1},
+    {"lappendListArrayStk", ASSEM_1BYTE,INST_LAPPEND_LIST_ARRAY_STK, 3,        1},
+    {"lappendListStk", ASSEM_1BYTE,    INST_LAPPEND_LIST_STK,  2,      1},
+    {"lappendStk",     ASSEM_1BYTE,    INST_LAPPEND_STK,       2,      1},
+    {"le",             ASSEM_1BYTE,    INST_LE,                2,      1},
+    {"lindexMulti",    ASSEM_LINDEX_MULTI,
+                                       INST_LIST_INDEX_MULTI,  INT_MIN,1},
+    {"list",           ASSEM_LIST,     INST_LIST,              INT_MIN,1},
+    {"listConcat",     ASSEM_1BYTE,    INST_LIST_CONCAT,       2,      1},
+    {"listIn",         ASSEM_1BYTE,    INST_LIST_IN,           2,      1},
+    {"listIndex",      ASSEM_1BYTE,    INST_LIST_INDEX,        2,      1},
+    {"listIndexImm",   ASSEM_INDEX,    INST_LIST_INDEX_IMM,    1,      1},
+    {"listLength",     ASSEM_1BYTE,    INST_LIST_LENGTH,       1,      1},
+    {"listNotIn",      ASSEM_1BYTE,    INST_LIST_NOT_IN,       2,      1},
+    {"load",           ASSEM_LVT,      (INST_LOAD_SCALAR1 << 8
+                                        | INST_LOAD_SCALAR4),  0,      1},
+    {"loadArray",      ASSEM_LVT,      (INST_LOAD_ARRAY1<<8
+                                        | INST_LOAD_ARRAY4),   1,      1},
+    {"loadArrayStk",   ASSEM_1BYTE,    INST_LOAD_ARRAY_STK,    2,      1},
+    {"loadStk",                ASSEM_1BYTE,    INST_LOAD_STK,          1,      1},
+    {"lor",            ASSEM_1BYTE,    INST_LOR,               2,      1},
+    {"lsetFlat",       ASSEM_LSET_FLAT,INST_LSET_FLAT,         INT_MIN,1},
+    {"lsetList",       ASSEM_1BYTE,    INST_LSET_LIST,         3,      1},
+    {"lshift",         ASSEM_1BYTE,    INST_LSHIFT,            2,      1},
+    {"lt",             ASSEM_1BYTE,    INST_LT,                2,      1},
+    {"mod",            ASSEM_1BYTE,    INST_MOD,               2,      1},
+    {"mult",           ASSEM_1BYTE,    INST_MULT,              2,      1},
+    {"neq",            ASSEM_1BYTE,    INST_NEQ,               2,      1},
+    {"nop",            ASSEM_1BYTE,    INST_NOP,               0,      0},
+    {"not",            ASSEM_1BYTE,    INST_LNOT,              1,      1},
+    {"nsupvar",                ASSEM_LVT4,     INST_NSUPVAR,           2,      1},
+    {"numericType",    ASSEM_1BYTE,    INST_NUM_TYPE,          1,      1},
+    {"originCmd",      ASSEM_1BYTE,    INST_ORIGIN_COMMAND,    1,      1},
+    {"over",           ASSEM_OVER,     INST_OVER,              INT_MIN,-1-1},
+    {"pop",            ASSEM_1BYTE,    INST_POP,               1,      0},
+    {"pushReturnCode", ASSEM_1BYTE,    INST_PUSH_RETURN_CODE,  0,      1},
+    {"pushReturnOpts", ASSEM_1BYTE,    INST_PUSH_RETURN_OPTIONS,
+                                                               0,      1},
+    {"pushResult",     ASSEM_1BYTE,    INST_PUSH_RESULT,       0,      1},
+    {"regexp",         ASSEM_REGEXP,   INST_REGEXP,            2,      1},
+    {"resolveCmd",     ASSEM_1BYTE,    INST_RESOLVE_COMMAND,   1,      1},
+    {"reverse",                ASSEM_REVERSE,  INST_REVERSE,           INT_MIN,-1-0},
+    {"rshift",         ASSEM_1BYTE,    INST_RSHIFT,            2,      1},
+    {"store",          ASSEM_LVT,      (INST_STORE_SCALAR1<<8
+                                        | INST_STORE_SCALAR4), 1,      1},
+    {"storeArray",     ASSEM_LVT,      (INST_STORE_ARRAY1<<8
+                                        | INST_STORE_ARRAY4),  2,      1},
+    {"storeArrayStk",  ASSEM_1BYTE,    INST_STORE_ARRAY_STK,   3,      1},
+    {"storeStk",       ASSEM_1BYTE,    INST_STORE_STK,         2,      1},
+    {"strcaseLower",   ASSEM_1BYTE,    INST_STR_LOWER,         1,      1},
+    {"strcaseTitle",   ASSEM_1BYTE,    INST_STR_TITLE,         1,      1},
+    {"strcaseUpper",   ASSEM_1BYTE,    INST_STR_UPPER,         1,      1},
+    {"strcmp",         ASSEM_1BYTE,    INST_STR_CMP,           2,      1},
+    {"strcat",         ASSEM_CONCAT1,  INST_STR_CONCAT1,       INT_MIN,1},
+    {"streq",          ASSEM_1BYTE,    INST_STR_EQ,            2,      1},
+    {"strfind",                ASSEM_1BYTE,    INST_STR_FIND,          2,      1},
+    {"strindex",       ASSEM_1BYTE,    INST_STR_INDEX,         2,      1},
+    {"strlen",         ASSEM_1BYTE,    INST_STR_LEN,           1,      1},
+    {"strmap",         ASSEM_1BYTE,    INST_STR_MAP,           3,      1},
+    {"strmatch",       ASSEM_BOOL,     INST_STR_MATCH,         2,      1},
+    {"strneq",         ASSEM_1BYTE,    INST_STR_NEQ,           2,      1},
+    {"strrange",       ASSEM_1BYTE,    INST_STR_RANGE,         3,      1},
+    {"strreplace",     ASSEM_1BYTE,    INST_STR_REPLACE,       4,      1},
+    {"strrfind",       ASSEM_1BYTE,    INST_STR_FIND_LAST,     2,      1},
+    {"strtrim",                ASSEM_1BYTE,    INST_STR_TRIM,          2,      1},
+    {"strtrimLeft",    ASSEM_1BYTE,    INST_STR_TRIM_LEFT,     2,      1},
+    {"strtrimRight",   ASSEM_1BYTE,    INST_STR_TRIM_RIGHT,    2,      1},
+    {"sub",            ASSEM_1BYTE,    INST_SUB,               2,      1},
+    {"tclooClass",     ASSEM_1BYTE,    INST_TCLOO_CLASS,       1,      1},
+    {"tclooIsObject",  ASSEM_1BYTE,    INST_TCLOO_IS_OBJECT,   1,      1},
+    {"tclooNamespace", ASSEM_1BYTE,    INST_TCLOO_NS,          1,      1},
+    {"tclooSelf",      ASSEM_1BYTE,    INST_TCLOO_SELF,        0,      1},
+    {"tryCvtToBoolean",        ASSEM_1BYTE,    INST_TRY_CVT_TO_BOOLEAN,1,      2},
+    {"tryCvtToNumeric",        ASSEM_1BYTE,    INST_TRY_CVT_TO_NUMERIC,1,      1},
+    {"uminus",         ASSEM_1BYTE,    INST_UMINUS,            1,      1},
+    {"unset",          ASSEM_BOOL_LVT4,INST_UNSET_SCALAR,      0,      0},
+    {"unsetArray",     ASSEM_BOOL_LVT4,INST_UNSET_ARRAY,       1,      0},
+    {"unsetArrayStk",  ASSEM_BOOL,     INST_UNSET_ARRAY_STK,   2,      0},
+    {"unsetStk",       ASSEM_BOOL,     INST_UNSET_STK,         1,      0},
+    {"uplus",          ASSEM_1BYTE,    INST_UPLUS,             1,      1},
+    {"upvar",          ASSEM_LVT4,     INST_UPVAR,             2,      1},
+    {"variable",       ASSEM_LVT4,     INST_VARIABLE,          1,      0},
+    {"verifyDict",     ASSEM_1BYTE,    INST_DICT_VERIFY,       1,      0},
+    {"yield",          ASSEM_1BYTE,    INST_YIELD,             1,      1},
+    {NULL,             ASSEM_1BYTE,            0,                      0,      0}
+};
+
+/*
+ * List of instructions that cannot throw an exception under any
+ * circumstances.  These instructions are the ones that are permissible after
+ * an exception is caught but before the corresponding exception range is
+ * popped from the stack.
+ * The instructions must be in ascending order by numeric operation code.
+ */
+
+static const unsigned char NonThrowingByteCodes[] = {
+    INST_PUSH1, INST_PUSH4, INST_POP, INST_DUP,                        /* 1-4 */
+    INST_JUMP1, INST_JUMP4,                                    /* 34-35 */
+    INST_END_CATCH, INST_PUSH_RESULT, INST_PUSH_RETURN_CODE,   /* 70-72 */
+    INST_STR_EQ, INST_STR_NEQ, INST_STR_CMP, INST_STR_LEN,     /* 73-76 */
+    INST_LIST,                                                 /* 79 */
+    INST_OVER,                                                 /* 95 */
+    INST_PUSH_RETURN_OPTIONS,                                  /* 108 */
+    INST_REVERSE,                                              /* 126 */
+    INST_NOP,                                                  /* 132 */
+    INST_STR_MAP,                                              /* 143 */
+    INST_STR_FIND,                                             /* 144 */
+    INST_COROUTINE_NAME,                                       /* 149 */
+    INST_NS_CURRENT,                                           /* 151 */
+    INST_INFO_LEVEL_NUM,                                       /* 152 */
+    INST_RESOLVE_COMMAND,                                      /* 154 */
+    INST_STR_TRIM, INST_STR_TRIM_LEFT, INST_STR_TRIM_RIGHT,    /* 166-168 */
+    INST_CONCAT_STK,                                           /* 169 */
+    INST_STR_UPPER, INST_STR_LOWER, INST_STR_TITLE,            /* 170-172 */
+    INST_NUM_TYPE                                              /* 180 */
+};
+
+/*
+ * Helper macros.
+ */
+
+#if defined(TCL_DEBUG_ASSEMBLY) && defined(__GNUC__) && __GNUC__ > 2
+#define DEBUG_PRINT(...)       fprintf(stderr, ##__VA_ARGS__);fflush(stderr)
+#elif defined(__GNUC__) && __GNUC__ > 2
+#define DEBUG_PRINT(...)       /* nothing */
+#else
+#define DEBUG_PRINT            /* nothing */
+#endif
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * BBAdjustStackDepth --
+ *
+ *     When an opcode is emitted, adjusts the stack information in the basic
+ *     block to reflect the number of operands produced and consumed.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Updates minimum, maximum and final stack requirements in the basic
+ *     block.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+BBAdjustStackDepth(
+    BasicBlock *bbPtr,         /* Structure describing the basic block */
+    int consumed,              /* Count of operands consumed by the
+                                * operation */
+    int produced)              /* Count of operands produced by the
+                                * operation */
+{
+    int depth = bbPtr->finalStackDepth;
+
+    depth -= consumed;
+    if (depth < bbPtr->minStackDepth) {
+       bbPtr->minStackDepth = depth;
+    }
+    depth += produced;
+    if (depth > bbPtr->maxStackDepth) {
+       bbPtr->maxStackDepth = depth;
+    }
+    bbPtr->finalStackDepth = depth;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * BBUpdateStackReqs --
+ *
+ *     Updates the stack requirements of a basic block, given the opcode
+ *     being emitted and an operand count.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Updates min, max and final stack requirements in the basic block.
+ *
+ * Notes:
+ *     This function must not be called for instructions such as REVERSE and
+ *     OVER that are variadic but do not consume all their operands. Instead,
+ *     BBAdjustStackDepth should be called directly.
+ *
+ *     count should be provided only for variadic operations. For operations
+ *     with known arity, count should be 0.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+BBUpdateStackReqs(
+    BasicBlock* bbPtr,         /* Structure describing the basic block */
+    int tblIdx,                        /* Index in TalInstructionTable of the
+                                * operation being assembled */
+    int count)                 /* Count of operands for variadic insts */
+{
+    int consumed = TalInstructionTable[tblIdx].operandsConsumed;
+    int produced = TalInstructionTable[tblIdx].operandsProduced;
+
+    if (consumed == INT_MIN) {
+       /*
+        * The instruction is variadic; it consumes 'count' operands.
+        */
+
+       consumed = count;
+    }
+    if (produced < 0) {
+       /*
+        * The instruction leaves some of its variadic operands on the stack,
+        * with net stack effect of '-1-produced'
+        */
+
+       produced = consumed - produced - 1;
+    }
+    BBAdjustStackDepth(bbPtr, consumed, produced);
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * BBEmitOpcode, BBEmitInstInt1, BBEmitInstInt4 --
+ *
+ *     Emit the opcode part of an instruction, or the entirety of an
+ *     instruction with a 1- or 4-byte operand, and adjust stack
+ *     requirements.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     Stores instruction and operand in the operand stream, and adjusts the
+ *     stack.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+BBEmitOpcode(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    int tblIdx,                        /* Table index in TalInstructionTable of op */
+    int count)                 /* Operand count for variadic ops */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* bbPtr = assemEnvPtr->curr_bb;
+                               /* Current basic block */
+    int op = TalInstructionTable[tblIdx].tclInstCode & 0xFF;
+
+    /*
+     * If this is the first instruction in a basic block, record its line
+     * number.
+     */
+
+    if (bbPtr->startOffset == envPtr->codeNext - envPtr->codeStart) {
+       bbPtr->startLine = assemEnvPtr->cmdLine;
+    }
+
+    TclEmitInt1(op, envPtr);
+    TclUpdateAtCmdStart(op, envPtr);
+    BBUpdateStackReqs(bbPtr, tblIdx, count);
+}
+
+static void
+BBEmitInstInt1(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    int tblIdx,                        /* Index in TalInstructionTable of op */
+    int opnd,                  /* 1-byte operand */
+    int count)                 /* Operand count for variadic ops */
+{
+    BBEmitOpcode(assemEnvPtr, tblIdx, count);
+    TclEmitInt1(opnd, assemEnvPtr->envPtr);
+}
+
+static void
+BBEmitInstInt4(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    int tblIdx,                        /* Index in TalInstructionTable of op */
+    int opnd,                  /* 4-byte operand */
+    int count)                 /* Operand count for variadic ops */
+{
+    BBEmitOpcode(assemEnvPtr, tblIdx, count);
+    TclEmitInt4(opnd, assemEnvPtr->envPtr);
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * BBEmitInst1or4 --
+ *
+ *     Emits a 1- or 4-byte operation according to the magnitude of the
+ *     operand.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+BBEmitInst1or4(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    int tblIdx,                        /* Index in TalInstructionTable of op */
+    int param,                 /* Variable-length parameter */
+    int count)                 /* Arity if variadic */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* bbPtr = assemEnvPtr->curr_bb;
+                               /* Current basic block */
+    int op = TalInstructionTable[tblIdx].tclInstCode;
+
+    if (param <= 0xFF) {
+       op >>= 8;
+    } else {
+       op &= 0xFF;
+    }
+    TclEmitInt1(op, envPtr);
+    if (param <= 0xFF) {
+       TclEmitInt1(param, envPtr);
+    } else {
+       TclEmitInt4(param, envPtr);
+    }
+    TclUpdateAtCmdStart(op, envPtr);
+    BBUpdateStackReqs(bbPtr, tblIdx, count);
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * Tcl_AssembleObjCmd, TclNRAssembleObjCmd --
+ *
+ *     Direct evaluation path for tcl::unsupported::assemble
+ *
+ * Results:
+ *     Returns a standard Tcl result.
+ *
+ * Side effects:
+ *     Assembles the code in objv[1], and executes it, so side effects
+ *     include whatever the code does.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+Tcl_AssembleObjCmd(
+    ClientData dummy,          /* Not used. */
+    Tcl_Interp *interp,                /* Current interpreter. */
+    int objc,                  /* Number of arguments. */
+    Tcl_Obj *const objv[])     /* Argument objects. */
+{
+    /*
+     * Boilerplate - make sure that there is an NRE trampoline on the C stack
+     * because there needs to be one in place to execute bytecode.
+     */
+
+    return Tcl_NRCallObjProc(interp, TclNRAssembleObjCmd, dummy, objc, objv);
+}
+
+int
+TclNRAssembleObjCmd(
+    ClientData dummy,          /* Not used. */
+    Tcl_Interp *interp,                /* Current interpreter. */
+    int objc,                  /* Number of arguments. */
+    Tcl_Obj *const objv[])     /* Argument objects. */
+{
+    ByteCode *codePtr;         /* Pointer to the bytecode to execute */
+    Tcl_Obj* backtrace;                /* Object where extra error information is
+                                * constructed. */
+
+    (void)dummy;
+    if (objc != 2) {
+       Tcl_WrongNumArgs(interp, 1, objv, "bytecodeList");
+       return TCL_ERROR;
+    }
+
+    /*
+     * Assemble the source to bytecode.
+     */
+
+    codePtr = CompileAssembleObj(interp, objv[1]);
+
+    /*
+     * On failure, report error line.
+     */
+
+    if (codePtr == NULL) {
+       Tcl_AddErrorInfo(interp, "\n    (\"");
+       Tcl_AppendObjToErrorInfo(interp, objv[0]);
+       Tcl_AddErrorInfo(interp, "\" body, line ");
+       TclNewIntObj(backtrace, Tcl_GetErrorLine(interp));
+       Tcl_AppendObjToErrorInfo(interp, backtrace);
+       Tcl_AddErrorInfo(interp, ")");
+       return TCL_ERROR;
+    }
+
+    /*
+     * Use NRE to evaluate the bytecode from the trampoline.
+     */
+
+    return TclNRExecuteByteCode(interp, codePtr);
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CompileAssembleObj --
+ *
+ *     Sets up and assembles Tcl bytecode for the direct-execution path in
+ *     the Tcl bytecode assembler.
+ *
+ * Results:
+ *     Returns a pointer to the assembled code. Returns NULL if the assembly
+ *     fails for any reason, with an appropriate error message in the
+ *     interpreter.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static ByteCode *
+CompileAssembleObj(
+    Tcl_Interp *interp,                /* Tcl interpreter */
+    Tcl_Obj *objPtr)           /* Source code to assemble */
+{
+    Interp *iPtr = (Interp *) interp;
+                               /* Internals of the interpreter */
+    CompileEnv compEnv;                /* Compilation environment structure */
+    ByteCode *codePtr = NULL;
+                               /* Bytecode resulting from the assembly */
+    Namespace* namespacePtr;   /* Namespace in which variable and command
+                                * names in the bytecode resolve */
+    int status;                        /* Status return from Tcl_AssembleCode */
+    const char* source;                /* String representation of the source code */
+    int sourceLen;             /* Length of the source code in bytes */
+
+    /*
+     * Get the expression ByteCode from the object. If it exists, make sure it
+     * is valid in the current context.
+     */
+
+    if (objPtr->typePtr == &assembleCodeType) {
+       namespacePtr = iPtr->varFramePtr->nsPtr;
+       codePtr = (ByteCode *)objPtr->internalRep.twoPtrValue.ptr1;
+       if (((Interp *) *codePtr->interpHandle == iPtr)
+               && (codePtr->compileEpoch == iPtr->compileEpoch)
+               && (codePtr->nsPtr == namespacePtr)
+               && (codePtr->nsEpoch == namespacePtr->resolverEpoch)
+               && (codePtr->localCachePtr
+                       == iPtr->varFramePtr->localCachePtr)) {
+           return codePtr;
+       }
+
+       /*
+        * Not valid, so free it and regenerate.
+        */
+
+       FreeAssembleCodeInternalRep(objPtr);
+    }
+
+    /*
+     * Set up the compilation environment, and assemble the code.
+     */
+
+    source = TclGetStringFromObj(objPtr, &sourceLen);
+    TclInitCompileEnv(interp, &compEnv, source, sourceLen, NULL, 0);
+    status = TclAssembleCode(&compEnv, source, sourceLen, TCL_EVAL_DIRECT);
+    if (status != TCL_OK) {
+       /*
+        * Assembly failed. Clean up and report the error.
+        */
+       TclFreeCompileEnv(&compEnv);
+       return NULL;
+    }
+
+    /*
+     * Add a "done" instruction as the last instruction and change the object
+     * into a ByteCode object. Ownership of the literal objects and aux data
+     * items is given to the ByteCode object.
+     */
+
+    TclEmitOpcode(INST_DONE, &compEnv);
+    TclInitByteCodeObj(objPtr, &compEnv);
+    objPtr->typePtr = &assembleCodeType;
+    TclFreeCompileEnv(&compEnv);
+
+    /*
+     * Record the local variable context to which the bytecode pertains
+     */
+
+    codePtr = (ByteCode *)objPtr->internalRep.twoPtrValue.ptr1;
+    if (iPtr->varFramePtr->localCachePtr) {
+       codePtr->localCachePtr = iPtr->varFramePtr->localCachePtr;
+       codePtr->localCachePtr->refCount++;
+    }
+
+    /*
+     * Report on what the assembler did.
+     */
+
+#ifdef TCL_COMPILE_DEBUG
+    if (tclTraceCompile >= 2) {
+       TclPrintByteCodeObj(interp, objPtr);
+       fflush(stdout);
+    }
+#endif /* TCL_COMPILE_DEBUG */
+
+    return codePtr;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * TclCompileAssembleCmd --
+ *
+ *     Compilation procedure for the '::tcl::unsupported::assemble' command.
+ *
+ * Results:
+ *     Returns a standard Tcl result.
+ *
+ * Side effects:
+ *     Puts the result of assembling the code into the bytecode stream in
+ *     'compileEnv'.
+ *
+ * This procedure makes sure that the command has a single arg, which is
+ * constant. If that condition is met, the procedure calls TclAssembleCode to
+ * produce bytecode for the given assembly code, and returns any error
+ * resulting from the assembly.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+int
+TclCompileAssembleCmd(
+    Tcl_Interp *interp,                /* Used for error reporting. */
+    Tcl_Parse *parsePtr,       /* Points to a parse structure for the command
+                                * created by Tcl_ParseCommand. */
+    Command *cmdPtr,           /* Points to defintion of command being
+                                * compiled. */
+    CompileEnv *envPtr)                /* Holds resulting instructions. */
+{
+    Tcl_Token *tokenPtr;       /* Token in the input script */
+
+    int numCommands = envPtr->numCommands;
+    int offset = envPtr->codeNext - envPtr->codeStart;
+    int depth = envPtr->currStackDepth;
+    (void)cmdPtr;
+    /*
+     * Make sure that the command has a single arg that is a simple word.
+     */
+
+    if (parsePtr->numWords != 2) {
+       return TCL_ERROR;
+    }
+    tokenPtr = TokenAfter(parsePtr->tokenPtr);
+    if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Compile the code and convert any error from the compilation into
+     * bytecode reporting the error;
+     */
+
+    if (TCL_ERROR == TclAssembleCode(envPtr, tokenPtr[1].start,
+           tokenPtr[1].size, TCL_EVAL_DIRECT)) {
+
+       Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
+               "\n    (\"%.*s\" body, line %d)",
+               parsePtr->tokenPtr->size, parsePtr->tokenPtr->start,
+               Tcl_GetErrorLine(interp)));
+       envPtr->numCommands = numCommands;
+       envPtr->codeNext = envPtr->codeStart + offset;
+       envPtr->currStackDepth = depth;
+       TclCompileSyntaxError(interp, envPtr);
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * TclAssembleCode --
+ *
+ *     Take a list of instructions in a Tcl_Obj, and assemble them to Tcl
+ *     bytecodes
+ *
+ * Results:
+ *     Returns TCL_OK on success, TCL_ERROR on failure.  If 'flags' includes
+ *     TCL_EVAL_DIRECT, places an error message in the interpreter result.
+ *
+ * Side effects:
+ *     Adds byte codes to the compile environment, and updates the
+ *     environment's stack depth.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+TclAssembleCode(
+    CompileEnv *envPtr,                /* Compilation environment that is to receive
+                                * the generated bytecode */
+    const char* codePtr,       /* Assembly-language code to be processed */
+    int codeLen,               /* Length of the code */
+    int flags)                 /* OR'ed combination of flags */
+{
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    /*
+     * Walk through the assembly script using the Tcl parser.  Each 'command'
+     * will be an instruction or assembly directive.
+     */
+
+    const char* instPtr = codePtr;
+                               /* Where to start looking for a line of code */
+    const char* nextPtr;       /* Pointer to the end of the line of code */
+    int bytesLeft = codeLen;   /* Number of bytes of source code remaining to
+                                * be parsed */
+    int status;                        /* Tcl status return */
+    AssemblyEnv* assemEnvPtr = NewAssemblyEnv(envPtr, flags);
+    Tcl_Parse* parsePtr = assemEnvPtr->parsePtr;
+
+    do {
+       /*
+        * Parse out one command line from the assembly script.
+        */
+
+       status = Tcl_ParseCommand(interp, instPtr, bytesLeft, 0, parsePtr);
+
+       /*
+        * Report errors in the parse.
+        */
+
+       if (status != TCL_OK) {
+           if (flags & TCL_EVAL_DIRECT) {
+               Tcl_LogCommandInfo(interp, codePtr, parsePtr->commandStart,
+                       parsePtr->term + 1 - parsePtr->commandStart);
+           }
+           FreeAssemblyEnv(assemEnvPtr);
+           return TCL_ERROR;
+       }
+
+       /*
+        * Advance the pointers around any leading commentary.
+        */
+
+       TclAdvanceLines(&assemEnvPtr->cmdLine, instPtr,
+               parsePtr->commandStart);
+       TclAdvanceContinuations(&assemEnvPtr->cmdLine, &assemEnvPtr->clNext,
+               parsePtr->commandStart - envPtr->source);
+
+       /*
+        * Process the line of code.
+        */
+
+       if (parsePtr->numWords > 0) {
+           int instLen = parsePtr->commandSize;
+                   /* Length in bytes of the current command */
+
+           if (parsePtr->term == parsePtr->commandStart + instLen - 1) {
+               --instLen;
+           }
+
+           /*
+            * If tracing, show each line assembled as it happens.
+            */
+
+#ifdef TCL_COMPILE_DEBUG
+           if ((tclTraceCompile >= 2) && (envPtr->procPtr == NULL)) {
+               printf("  %4ld Assembling: ",
+                       (long)(envPtr->codeNext - envPtr->codeStart));
+               TclPrintSource(stdout, parsePtr->commandStart,
+                       TclMin(instLen, 55));
+               printf("\n");
+           }
+#endif
+           if (AssembleOneLine(assemEnvPtr) != TCL_OK) {
+               if (flags & TCL_EVAL_DIRECT) {
+                   Tcl_LogCommandInfo(interp, codePtr,
+                           parsePtr->commandStart, instLen);
+               }
+               Tcl_FreeParse(parsePtr);
+               FreeAssemblyEnv(assemEnvPtr);
+               return TCL_ERROR;
+           }
+       }
+
+       /*
+        * Advance to the next line of code.
+        */
+
+       nextPtr = parsePtr->commandStart + parsePtr->commandSize;
+       bytesLeft -= (nextPtr - instPtr);
+       instPtr = nextPtr;
+       TclAdvanceLines(&assemEnvPtr->cmdLine, parsePtr->commandStart,
+               instPtr);
+       TclAdvanceContinuations(&assemEnvPtr->cmdLine, &assemEnvPtr->clNext,
+               instPtr - envPtr->source);
+       Tcl_FreeParse(parsePtr);
+    } while (bytesLeft > 0);
+
+    /*
+     * Done with parsing the code.
+     */
+
+    status = FinishAssembly(assemEnvPtr);
+    FreeAssemblyEnv(assemEnvPtr);
+    return status;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * NewAssemblyEnv --
+ *
+ *     Creates an environment for the assembler to run in.
+ *
+ * Results:
+ *     Allocates, initialises and returns an assembler environment
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static AssemblyEnv*
+NewAssemblyEnv(
+    CompileEnv* envPtr,                /* Compilation environment being used for code
+                                * generation*/
+    int flags)                 /* Compilation flags (TCL_EVAL_DIRECT) */
+{
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    AssemblyEnv* assemEnvPtr = (AssemblyEnv*)TclStackAlloc(interp, sizeof(AssemblyEnv));
+                               /* Assembler environment under construction */
+    Tcl_Parse* parsePtr = (Tcl_Parse*)TclStackAlloc(interp, sizeof(Tcl_Parse));
+                               /* Parse of one line of assembly code */
+
+    assemEnvPtr->envPtr = envPtr;
+    assemEnvPtr->parsePtr = parsePtr;
+    assemEnvPtr->cmdLine = 1;
+    assemEnvPtr->clNext = envPtr->clNext;
+
+    /*
+     * Make the hashtables that store symbol resolution.
+     */
+
+    Tcl_InitHashTable(&assemEnvPtr->labelHash, TCL_STRING_KEYS);
+
+    /*
+     * Start the first basic block.
+     */
+
+    assemEnvPtr->curr_bb = NULL;
+    assemEnvPtr->head_bb = AllocBB(assemEnvPtr);
+    assemEnvPtr->curr_bb = assemEnvPtr->head_bb;
+    assemEnvPtr->head_bb->startLine = 1;
+
+    /*
+     * Stash compilation flags.
+     */
+
+    assemEnvPtr->flags = flags;
+    return assemEnvPtr;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FreeAssemblyEnv --
+ *
+ *     Cleans up the assembler environment when assembly is complete.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+FreeAssemblyEnv(
+    AssemblyEnv* assemEnvPtr)  /* Environment to free */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment being used for code
+                                * generation */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    BasicBlock* thisBB;                /* Pointer to a basic block being deleted */
+    BasicBlock* nextBB;                /* Pointer to a deleted basic block's
+                                * successor */
+
+    /*
+     * Free all the basic block structures.
+     */
+
+    for (thisBB = assemEnvPtr->head_bb; thisBB != NULL; thisBB = nextBB) {
+       if (thisBB->jumpTarget != NULL) {
+           Tcl_DecrRefCount(thisBB->jumpTarget);
+       }
+       if (thisBB->foreignExceptions != NULL) {
+           ckfree(thisBB->foreignExceptions);
+       }
+       nextBB = thisBB->successor1;
+       if (thisBB->jtPtr != NULL) {
+           DeleteMirrorJumpTable(thisBB->jtPtr);
+           thisBB->jtPtr = NULL;
+       }
+       ckfree(thisBB);
+    }
+
+    /*
+     * Dispose what's left.
+     */
+
+    Tcl_DeleteHashTable(&assemEnvPtr->labelHash);
+    TclStackFree(interp, assemEnvPtr->parsePtr);
+    TclStackFree(interp, assemEnvPtr);
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * AssembleOneLine --
+ *
+ *     Assembles a single command from an assembly language source.
+ *
+ * Results:
+ *     Returns TCL_ERROR with an appropriate error message if the assembly
+ *     fails. Returns TCL_OK if the assembly succeeds. Updates the assembly
+ *     environment with the state of the assembly.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+AssembleOneLine(
+    AssemblyEnv* assemEnvPtr)  /* State of the assembly */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment being used for code
+                                * gen */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    Tcl_Parse* parsePtr = assemEnvPtr->parsePtr;
+                               /* Parse of the line of code */
+    Tcl_Token* tokenPtr;       /* Current token within the line of code */
+    Tcl_Obj* instNameObj;      /* Name of the instruction */
+    int tblIdx;                        /* Index in TalInstructionTable of the
+                                * instruction */
+    enum TalInstType instType; /* Type of the instruction */
+    Tcl_Obj* operand1Obj = NULL;
+                               /* First operand to the instruction */
+    const char* operand1;      /* String rep of the operand */
+    int operand1Len;           /* String length of the operand */
+    int opnd;                  /* Integer representation of an operand */
+    int litIndex;              /* Literal pool index of a constant */
+    int localVar;              /* LVT index of a local variable */
+    int flags;                 /* Flags for a basic block */
+    JumptableInfo* jtPtr;      /* Pointer to a jumptable */
+    int infoIndex;             /* Index of the jumptable in auxdata */
+    int status = TCL_ERROR;    /* Return value from this function */
+
+    /*
+     * Make sure that the instruction name is known at compile time.
+     */
+
+    tokenPtr = parsePtr->tokenPtr;
+    if (GetNextOperand(assemEnvPtr, &tokenPtr, &instNameObj) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Look up the instruction name.
+     */
+
+    if (Tcl_GetIndexFromObjStruct(interp, instNameObj,
+           &TalInstructionTable[0].name, sizeof(TalInstDesc), "instruction",
+           TCL_EXACT, &tblIdx) != TCL_OK) {
+       goto cleanup;
+    }
+
+    /*
+     * Vector on the type of instruction being processed.
+     */
+
+    instType = TalInstructionTable[tblIdx].instType;
+    switch (instType) {
+
+    case ASSEM_PUSH:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "value");
+           goto cleanup;
+       }
+       if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
+           goto cleanup;
+       }
+       operand1 = Tcl_GetStringFromObj(operand1Obj, &operand1Len);
+       litIndex = TclRegisterNewLiteral(envPtr, operand1, operand1Len);
+       BBEmitInst1or4(assemEnvPtr, tblIdx, litIndex, 0);
+       break;
+
+    case ASSEM_1BYTE:
+       if (parsePtr->numWords != 1) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "");
+           goto cleanup;
+       }
+       BBEmitOpcode(assemEnvPtr, tblIdx, 0);
+       break;
+
+    case ASSEM_BEGIN_CATCH:
+       /*
+        * Emit the BEGIN_CATCH instruction with the code offset of the
+        * exception branch target instead of the exception range index. The
+        * correct index will be generated and inserted later, when catches
+        * are being resolved.
+        */
+
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "label");
+           goto cleanup;
+       }
+       if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
+           goto cleanup;
+       }
+       assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
+       assemEnvPtr->curr_bb->jumpOffset = envPtr->codeNext-envPtr->codeStart;
+       BBEmitInstInt4(assemEnvPtr, tblIdx, 0, 0);
+       assemEnvPtr->curr_bb->flags |= BB_BEGINCATCH;
+       StartBasicBlock(assemEnvPtr, BB_FALLTHRU, operand1Obj);
+       break;
+
+    case ASSEM_BOOL:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "boolean");
+           goto cleanup;
+       }
+       if (GetBooleanOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, 0);
+       break;
+
+    case ASSEM_BOOL_LVT4:
+       if (parsePtr->numWords != 3) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "boolean varName");
+           goto cleanup;
+       }
+       if (GetBooleanOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
+       if (localVar < 0) {
+           goto cleanup;
+       }
+       BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, 0);
+       TclEmitInt4(localVar, envPtr);
+       break;
+
+    case ASSEM_CLOCK_READ:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "imm8");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       if (opnd < 0 || opnd > 3) {
+           Tcl_SetObjResult(interp,
+                            Tcl_NewStringObj("operand must be [0..3]", -1));
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "OPERAND<0,>3", NULL);
+           goto cleanup;
+       }
+       BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, opnd);
+       break;
+
+    case ASSEM_CONCAT1:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "imm8");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckOneByte(interp, opnd) != TCL_OK
+               || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, opnd);
+       break;
+
+    case ASSEM_DICT_GET:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd+1);
+       break;
+
+    case ASSEM_DICT_SET:
+       if (parsePtr->numWords != 3) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count varName");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
+       if (localVar < 0) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd+1);
+       TclEmitInt4(localVar, envPtr);
+       break;
+
+    case ASSEM_DICT_UNSET:
+       if (parsePtr->numWords != 3) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count varName");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
+       if (localVar < 0) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
+       TclEmitInt4(localVar, envPtr);
+       break;
+
+    case ASSEM_END_CATCH:
+       if (parsePtr->numWords != 1) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "");
+           goto cleanup;
+       }
+       assemEnvPtr->curr_bb->flags |= BB_ENDCATCH;
+       BBEmitOpcode(assemEnvPtr, tblIdx, 0);
+       StartBasicBlock(assemEnvPtr, BB_FALLTHRU, NULL);
+       break;
+
+    case ASSEM_EVAL:
+       /* TODO - Refactor this stuff into a subroutine that takes the inst
+        * code, the message ("script" or "expression") and an evaluator
+        * callback that calls TclCompileScript or TclCompileExpr. */
+
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj,
+                   ((TalInstructionTable[tblIdx].tclInstCode
+                   == INST_EVAL_STK) ? "script" : "expression"));
+           goto cleanup;
+       }
+       if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
+           CompileEmbeddedScript(assemEnvPtr, tokenPtr+1,
+                   TalInstructionTable+tblIdx);
+       } else if (GetNextOperand(assemEnvPtr, &tokenPtr,
+               &operand1Obj) != TCL_OK) {
+           goto cleanup;
+       } else {
+           operand1 = Tcl_GetStringFromObj(operand1Obj, &operand1Len);
+           litIndex = TclRegisterNewLiteral(envPtr, operand1, operand1Len);
+
+           /*
+            * Assumes that PUSH is the first slot!
+            */
+
+           BBEmitInst1or4(assemEnvPtr, 0, litIndex, 0);
+           BBEmitOpcode(assemEnvPtr, tblIdx, 0);
+       }
+       break;
+
+    case ASSEM_INVOKE:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+
+       BBEmitInst1or4(assemEnvPtr, tblIdx, opnd, opnd);
+       break;
+
+    case ASSEM_JUMP:
+    case ASSEM_JUMP4:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "label");
+           goto cleanup;
+       }
+       if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
+           goto cleanup;
+       }
+       assemEnvPtr->curr_bb->jumpOffset = envPtr->codeNext-envPtr->codeStart;
+       if (instType == ASSEM_JUMP) {
+           flags = BB_JUMP1;
+           BBEmitInstInt1(assemEnvPtr, tblIdx, 0, 0);
+       } else {
+           flags = 0;
+           BBEmitInstInt4(assemEnvPtr, tblIdx, 0, 0);
+       }
+
+       /*
+        * Start a new basic block at the instruction following the jump.
+        */
+
+       assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
+       if (TalInstructionTable[tblIdx].operandsConsumed != 0) {
+           flags |= BB_FALLTHRU;
+       }
+       StartBasicBlock(assemEnvPtr, flags, operand1Obj);
+       break;
+
+    case ASSEM_JUMPTABLE:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "table");
+           goto cleanup;
+       }
+       if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
+           goto cleanup;
+       }
+
+       jtPtr = (JumptableInfo*)ckalloc(sizeof(JumptableInfo));
+
+       Tcl_InitHashTable(&jtPtr->hashTable, TCL_STRING_KEYS);
+       assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
+       assemEnvPtr->curr_bb->jumpOffset = envPtr->codeNext-envPtr->codeStart;
+       DEBUG_PRINT("bb %p jumpLine %d jumpOffset %d\n",
+               assemEnvPtr->curr_bb, assemEnvPtr->cmdLine,
+               envPtr->codeNext - envPtr->codeStart);
+
+       infoIndex = TclCreateAuxData(jtPtr, &tclJumptableInfoType, envPtr);
+       DEBUG_PRINT("auxdata index=%d\n", infoIndex);
+
+       BBEmitInstInt4(assemEnvPtr, tblIdx, infoIndex, 0);
+       if (CreateMirrorJumpTable(assemEnvPtr, operand1Obj) != TCL_OK) {
+           goto cleanup;
+       }
+       StartBasicBlock(assemEnvPtr, BB_JUMPTABLE|BB_FALLTHRU, NULL);
+       break;
+
+    case ASSEM_LABEL:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "name");
+           goto cleanup;
+       }
+       if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
+           goto cleanup;
+       }
+
+       /*
+        * Add the (label_name, address) pair to the hash table.
+        */
+
+       if (DefineLabel(assemEnvPtr, Tcl_GetString(operand1Obj)) != TCL_OK) {
+           goto cleanup;
+       }
+       break;
+
+    case ASSEM_LINDEX_MULTI:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
+       break;
+
+    case ASSEM_LIST:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckNonNegative(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
+       break;
+
+    case ASSEM_INDEX:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
+           goto cleanup;
+       }
+       if (GetListIndexOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
+       break;
+
+    case ASSEM_LSET_FLAT:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       if (opnd < 2) {
+           if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+               Tcl_SetObjResult(interp,
+                       Tcl_NewStringObj("operand must be >=2", -1));
+               Tcl_SetErrorCode(interp, "TCL", "ASSEM", "OPERAND>=2", NULL);
+           }
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
+       break;
+
+    case ASSEM_LVT:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "varname");
+           goto cleanup;
+       }
+       localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
+       if (localVar < 0) {
+           goto cleanup;
+       }
+       BBEmitInst1or4(assemEnvPtr, tblIdx, localVar, 0);
+       break;
+
+    case ASSEM_LVT1:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "varname");
+           goto cleanup;
+       }
+       localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
+       if (localVar < 0 || CheckOneByte(interp, localVar)) {
+           goto cleanup;
+       }
+       BBEmitInstInt1(assemEnvPtr, tblIdx, localVar, 0);
+       break;
+
+    case ASSEM_LVT1_SINT1:
+       if (parsePtr->numWords != 3) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "varName imm8");
+           goto cleanup;
+       }
+       localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
+       if (localVar < 0 || CheckOneByte(interp, localVar)
+               || GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckSignedOneByte(interp, opnd)) {
+           goto cleanup;
+       }
+       BBEmitInstInt1(assemEnvPtr, tblIdx, localVar, 0);
+       TclEmitInt1(opnd, envPtr);
+       break;
+
+    case ASSEM_LVT4:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "varname");
+           goto cleanup;
+       }
+       localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
+       if (localVar < 0) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, localVar, 0);
+       break;
+
+    case ASSEM_OVER:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckNonNegative(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd+1);
+       break;
+
+    case ASSEM_REGEXP:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "boolean");
+           goto cleanup;
+       }
+       if (GetBooleanOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       {
+           BBEmitInstInt1(assemEnvPtr, tblIdx, TCL_REG_ADVANCED | (opnd ? TCL_REG_NOCASE : 0), 0);
+       }
+       break;
+
+    case ASSEM_REVERSE:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckNonNegative(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
+       break;
+
+    case ASSEM_SINT1:
+       if (parsePtr->numWords != 2) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "imm8");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
+               || CheckSignedOneByte(interp, opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, 0);
+       break;
+
+    case ASSEM_SINT4_LVT4:
+       if (parsePtr->numWords != 3) {
+           Tcl_WrongNumArgs(interp, 1, &instNameObj, "count varName");
+           goto cleanup;
+       }
+       if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
+           goto cleanup;
+       }
+       localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
+       if (localVar < 0) {
+           goto cleanup;
+       }
+       BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, 0);
+       TclEmitInt4(localVar, envPtr);
+       break;
+
+    default:
+       Tcl_Panic("Instruction \"%s\" could not be found, can't happen\n",
+               Tcl_GetString(instNameObj));
+    }
+
+    status = TCL_OK;
+ cleanup:
+    Tcl_DecrRefCount(instNameObj);
+    if (operand1Obj) {
+       Tcl_DecrRefCount(operand1Obj);
+    }
+    return status;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CompileEmbeddedScript --
+ *
+ *     Compile an embedded 'eval' or 'expr' that appears in assembly code.
+ *
+ * This procedure is called when the 'eval' or 'expr' assembly directive is
+ * encountered, and the argument to the directive is a simple word that
+ * requires no substitution. The appropriate compiler (TclCompileScript or
+ * TclCompileExpr) is invoked recursively, and emits bytecode.
+ *
+ * Before the compiler is invoked, the compilation environment's stack
+ * consumption is reset to zero. Upon return from the compilation, the net
+ * stack effect of the compilation is in the compiler env, and this stack
+ * effect is posted to the assembler environment. The compile environment's
+ * stack consumption is then restored to what it was before (which is actually
+ * the state of the stack on entry to the block of assembly code).
+ *
+ * Any exception ranges pushed by the compilation are copied to the basic
+ * block and removed from the compiler environment. They will be rebuilt at
+ * the end of assembly, when the exception stack depth is actually known.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+CompileEmbeddedScript(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    Tcl_Token* tokenPtr,       /* Tcl_Token containing the script */
+    const TalInstDesc* instPtr)        /* Instruction that determines whether
+                                * the script is 'expr' or 'eval' */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+
+    /*
+     * The expression or script is not only known at compile time, but
+     * actually a "simple word". It can be compiled inline by invoking the
+     * compiler recursively.
+     *
+     * Save away the stack depth and reset it before compiling the script.
+     * We'll record the stack usage of the script in the BasicBlock, and
+     * accumulate it together with the stack usage of the enclosing assembly
+     * code.
+     */
+
+    int savedStackDepth = envPtr->currStackDepth;
+    int savedMaxStackDepth = envPtr->maxStackDepth;
+    int savedExceptArrayNext = envPtr->exceptArrayNext;
+
+    envPtr->currStackDepth = 0;
+    envPtr->maxStackDepth = 0;
+
+    StartBasicBlock(assemEnvPtr, BB_FALLTHRU, NULL);
+    switch(instPtr->tclInstCode) {
+    case INST_EVAL_STK:
+       TclCompileScript(interp, tokenPtr->start, tokenPtr->size, envPtr);
+       break;
+    case INST_EXPR_STK:
+       TclCompileExpr(interp, tokenPtr->start, tokenPtr->size, envPtr, 1);
+       break;
+    default:
+       Tcl_Panic("no ASSEM_EVAL case for %s (%d), can't happen",
+               instPtr->name, instPtr->tclInstCode);
+    }
+
+    /*
+     * Roll up the stack usage of the embedded block into the assembler
+     * environment.
+     */
+
+    SyncStackDepth(assemEnvPtr);
+    envPtr->currStackDepth = savedStackDepth;
+    envPtr->maxStackDepth = savedMaxStackDepth;
+
+    /*
+     * Save any exception ranges that were pushed by the compiler; they will
+     * need to be fixed up once the stack depth is known.
+     */
+
+    MoveExceptionRangesToBasicBlock(assemEnvPtr, savedExceptArrayNext);
+
+    /*
+     * Flush the current basic block.
+     */
+
+    StartBasicBlock(assemEnvPtr, BB_FALLTHRU, NULL);
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * SyncStackDepth --
+ *
+ *     Copies the stack depth from the compile environment to a basic block.
+ *
+ * Side effects:
+ *     Current and max stack depth in the current basic block are adjusted.
+ *
+ * This procedure is called on return from invoking the compiler for the
+ * 'eval' and 'expr' operations. It adjusts the stack depth of the current
+ * basic block to reflect the stack required by the just-compiled code.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+SyncStackDepth(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* curr_bb = assemEnvPtr->curr_bb;
+                               /* Current basic block */
+    int maxStackDepth = curr_bb->finalStackDepth + envPtr->maxStackDepth;
+                               /* Max stack depth in the basic block */
+
+    if (maxStackDepth > curr_bb->maxStackDepth) {
+       curr_bb->maxStackDepth = maxStackDepth;
+    }
+    curr_bb->finalStackDepth += envPtr->currStackDepth;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MoveExceptionRangesToBasicBlock --
+ *
+ *     Removes exception ranges that were created by compiling an embedded
+ *     script from the CompileEnv, and stores them in the BasicBlock. They
+ *     will be reinstalled, at the correct stack depth, after control flow
+ *     analysis is complete on the assembly code.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+MoveExceptionRangesToBasicBlock(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    int savedExceptArrayNext)  /* Saved index of the end of the exception
+                                * range array */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* curr_bb = assemEnvPtr->curr_bb;
+                               /* Current basic block */
+    int exceptionCount = envPtr->exceptArrayNext - savedExceptArrayNext;
+                               /* Number of ranges that must be moved */
+    int i;
+
+    if (exceptionCount == 0) {
+       /* Nothing to do */
+       return;
+    }
+
+    /*
+     * Save the exception ranges in the basic block. They will be re-added at
+     * the conclusion of assembly; at this time, the INST_BEGIN_CATCH
+     * instructions in the block will be adjusted from whatever range indices
+     * they have [savedExceptArrayNext .. envPtr->exceptArrayNext) to the
+     * indices that the exceptions acquire. The saved exception ranges are
+     * converted to a relative nesting depth. The depth will be recomputed
+     * once flow analysis has determined the actual stack depth of the block.
+     */
+
+    DEBUG_PRINT("basic block %p has %d exceptions starting at %d\n",
+           curr_bb, exceptionCount, savedExceptArrayNext);
+    curr_bb->foreignExceptionBase = savedExceptArrayNext;
+    curr_bb->foreignExceptionCount = exceptionCount;
+    curr_bb->foreignExceptions =
+               (ExceptionRange*)ckalloc(exceptionCount * sizeof(ExceptionRange));
+    memcpy(curr_bb->foreignExceptions,
+           envPtr->exceptArrayPtr + savedExceptArrayNext,
+           exceptionCount * sizeof(ExceptionRange));
+    for (i = 0; i < exceptionCount; ++i) {
+       curr_bb->foreignExceptions[i].nestingLevel -= envPtr->exceptDepth;
+    }
+    envPtr->exceptArrayNext = savedExceptArrayNext;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CreateMirrorJumpTable --
+ *
+ *     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
+ *     JumptableInfo. The keys in the JumptableInfo are the comparison
+ *     strings. The values, instead of being jump displacements, are
+ *     Tcl_Obj's with the code labels.
+ */
+
+static int
+CreateMirrorJumpTable(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    Tcl_Obj* jumps)            /* List of alternating keywords and labels */
+{
+    int 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 */
+    JumptableInfo* jtPtr;
+    Tcl_HashTable* jtHashPtr;  /* Hashtable in the JumptableInfo */
+    Tcl_HashEntry* hashEntry;  /* Entry for a key in the hashtable */
+    int isNew;                 /* Flag==1 if the key is not yet in the
+                                * table. */
+    int i;
+
+    if (Tcl_ListObjGetElements(interp, jumps, &objc, &objv) != 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", NULL);
+       }
+       return TCL_ERROR;
+    }
+
+    /*
+     * Allocate the jumptable.
+     */
+
+    jtPtr = (JumptableInfo*)ckalloc(sizeof(JumptableInfo));
+    jtHashPtr = &jtPtr->hashTable;
+    Tcl_InitHashTable(jtHashPtr, TCL_STRING_KEYS);
+
+    /*
+     * 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", Tcl_GetString(objv[i]),
+               Tcl_GetString(objv[i+1]));
+       hashEntry = Tcl_CreateHashEntry(jtHashPtr, Tcl_GetString(objv[i]),
+               &isNew);
+       if (!isNew) {
+           if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+               Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+                       "duplicate entry in jump table for \"%s\"",
+                       Tcl_GetString(objv[i])));
+               Tcl_SetErrorCode(interp, "TCL", "ASSEM", "DUPJUMPTABLEENTRY");
+               DeleteMirrorJumpTable(jtPtr);
+               return TCL_ERROR;
+           }
+       }
+       Tcl_SetHashValue(hashEntry, objv[i+1]);
+       Tcl_IncrRefCount(objv[i+1]);
+    }
+    DEBUG_PRINT("}\n");
+
+    /*
+     * Put the mirror jumptable in the basic block struct.
+     */
+
+    bbPtr->jtPtr = jtPtr;
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DeleteMirrorJumpTable --
+ *
+ *     Cleans up a jump table when the basic block is deleted.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+DeleteMirrorJumpTable(
+    JumptableInfo* jtPtr)
+{
+    Tcl_HashTable* jtHashPtr = &jtPtr->hashTable;
+                               /* 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);
+    }
+    Tcl_DeleteHashTable(jtHashPtr);
+    ckfree(jtPtr);
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GetNextOperand --
+ *
+ *     Retrieves the next operand in sequence from an assembly instruction,
+ *     and makes sure that its value is known at compile time.
+ *
+ * Results:
+ *     If successful, returns TCL_OK and leaves a Tcl_Obj with the operand
+ *     text in *operandObjPtr. In case of failure, returns TCL_ERROR and
+ *     leaves *operandObjPtr untouched.
+ *
+ * Side effects:
+ *     Advances *tokenPtrPtr around the token just processed.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+GetNextOperand(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    Tcl_Token** tokenPtrPtr,   /* INPUT/OUTPUT: Pointer to the token holding
+                                * the operand */
+    Tcl_Obj** operandObjPtr)   /* OUTPUT: Tcl object holding the operand text
+                                * with \-substitutions done. */
+{
+    Tcl_Interp* interp = (Tcl_Interp*) assemEnvPtr->envPtr->iPtr;
+    Tcl_Obj* operandObj;
+
+    TclNewObj(operandObj);
+    if (!TclWordKnownAtCompileTime(*tokenPtrPtr, operandObj)) {
+       Tcl_DecrRefCount(operandObj);
+       if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+           Tcl_SetObjResult(interp, Tcl_NewStringObj(
+                   "assembly code may not contain substitutions", -1));
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "NOSUBST", NULL);
+       }
+       return TCL_ERROR;
+    }
+    *tokenPtrPtr = TokenAfter(*tokenPtrPtr);
+    Tcl_IncrRefCount(operandObj);
+    *operandObjPtr = operandObj;
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GetBooleanOperand --
+ *
+ *     Retrieves a Boolean operand from the input stream and advances
+ *     the token pointer.
+ *
+ * Results:
+ *     Returns a standard Tcl result (with an error message in the
+ *     interpreter on failure).
+ *
+ * Side effects:
+ *     Stores the Boolean value in (*result) and advances (*tokenPtrPtr)
+ *     to the next token.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+GetBooleanOperand(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    Tcl_Token** tokenPtrPtr,   /* Current token from the parser */
+    int* result)               /* OUTPUT: Integer extracted from the token */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    Tcl_Token* tokenPtr = *tokenPtrPtr;
+                               /* INOUT: Pointer to the next token in the
+                                * source code */
+    Tcl_Obj* intObj;           /* Integer from the source code */
+    int status;                        /* Tcl status return */
+
+    /*
+     * Extract the next token as a string.
+     */
+
+    if (GetNextOperand(assemEnvPtr, tokenPtrPtr, &intObj) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Convert to an integer, advance to the next token and return.
+     */
+
+    status = Tcl_GetBooleanFromObj(interp, intObj, result);
+    Tcl_DecrRefCount(intObj);
+    *tokenPtrPtr = TokenAfter(tokenPtr);
+    return status;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GetIntegerOperand --
+ *
+ *     Retrieves an integer operand from the input stream and advances the
+ *     token pointer.
+ *
+ * Results:
+ *     Returns a standard Tcl result (with an error message in the
+ *     interpreter on failure).
+ *
+ * Side effects:
+ *     Stores the integer value in (*result) and advances (*tokenPtrPtr) to
+ *     the next token.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+GetIntegerOperand(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    Tcl_Token** tokenPtrPtr,   /* Current token from the parser */
+    int* result)               /* OUTPUT: Integer extracted from the token */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    Tcl_Token* tokenPtr = *tokenPtrPtr;
+                               /* INOUT: Pointer to the next token in the
+                                * source code */
+    Tcl_Obj* intObj;           /* Integer from the source code */
+    int status;                        /* Tcl status return */
+
+    /*
+     * Extract the next token as a string.
+     */
+
+    if (GetNextOperand(assemEnvPtr, tokenPtrPtr, &intObj) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Convert to an integer, advance to the next token and return.
+     */
+
+    status = Tcl_GetIntFromObj(interp, intObj, result);
+    Tcl_DecrRefCount(intObj);
+    *tokenPtrPtr = TokenAfter(tokenPtr);
+    return status;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * GetListIndexOperand --
+ *
+ *     Gets the value of an operand intended to serve as a list index.
+ *
+ * Results:
+ *     Returns a standard Tcl result: TCL_OK if the parse is successful and
+ *     TCL_ERROR (with an appropriate error message) if the parse fails.
+ *
+ * Side effects:
+ *     Stores the list index at '*index'. Values between -1 and 0x7FFFFFFF
+ *     have their natural meaning; values between -2 and -0x80000000
+ *     represent 'end-2-N'.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+GetListIndexOperand(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    Tcl_Token** tokenPtrPtr,   /* Current token from the parser */
+    int* result)               /* OUTPUT: Integer extracted from the token */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    Tcl_Token* tokenPtr = *tokenPtrPtr;
+                               /* INOUT: Pointer to the next token in the
+                                * source code */
+    Tcl_Obj *value;
+    int status;
+
+    /* General operand validity check */
+    if (GetNextOperand(assemEnvPtr, tokenPtrPtr, &value) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /* Convert to an integer, advance to the next token and return. */
+    /*
+     * NOTE: Indexing a list with an index before it yields the
+     * same result as indexing after it, and might be more easily portable
+     * when list size limits grow.
+     */
+    status = TclIndexEncode(interp, value,
+           TCL_INDEX_BEFORE,TCL_INDEX_BEFORE, result);
+
+    Tcl_DecrRefCount(value);
+    *tokenPtrPtr = TokenAfter(tokenPtr);
+    return status;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FindLocalVar --
+ *
+ *     Gets the name of a local variable from the input stream and advances
+ *     the token pointer.
+ *
+ * Results:
+ *     Returns the LVT index of the local variable.  Returns -1 if the
+ *     variable is non-local, not known at compile time, or cannot be
+ *     installed in the LVT (leaving an error message in the interpreter
+ *     result if necessary).
+ *
+ * Side effects:
+ *     Advances the token pointer.  May define a new LVT slot if the variable
+ *     has not yet been seen and the execution context allows for it.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+FindLocalVar(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    Tcl_Token** tokenPtrPtr)
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    Tcl_Token* tokenPtr = *tokenPtrPtr;
+                               /* INOUT: Pointer to the next token in the
+                                * source code. */
+    Tcl_Obj* varNameObj;       /* Name of the variable */
+    const char* varNameStr;
+    int varNameLen;
+    int localVar;              /* Index of the variable in the LVT */
+
+    if (GetNextOperand(assemEnvPtr, tokenPtrPtr, &varNameObj) != TCL_OK) {
+       return -1;
+    }
+    varNameStr = Tcl_GetStringFromObj(varNameObj, &varNameLen);
+    if (CheckNamespaceQualifiers(interp, varNameStr, varNameLen)) {
+       Tcl_DecrRefCount(varNameObj);
+       return -1;
+    }
+    localVar = TclFindCompiledLocal(varNameStr, varNameLen, 1, envPtr);
+    Tcl_DecrRefCount(varNameObj);
+    if (localVar == -1) {
+       if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+           Tcl_SetObjResult(interp, Tcl_NewStringObj(
+                   "cannot use this instruction to create a variable"
+                   " in a non-proc context", -1));
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "LVT", NULL);
+       }
+       return -1;
+    }
+    *tokenPtrPtr = TokenAfter(tokenPtr);
+    return localVar;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckNamespaceQualifiers --
+ *
+ *     Verify that a variable name has no namespace qualifiers before
+ *     attempting to install it in the LVT.
+ *
+ * Results:
+ *     On success, returns TCL_OK. On failure, returns TCL_ERROR and stores
+ *     an error message in the interpreter result.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckNamespaceQualifiers(
+    Tcl_Interp* interp,                /* Tcl interpreter for error reporting */
+    const char* name,          /* Variable name to check */
+    int nameLen)               /* Length of the variable */
+{
+    const char* p;
+
+    for (p = name; p+2 < name+nameLen;  p++) {
+       if ((*p == ':') && (p[1] == ':')) {
+           Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+                   "variable \"%s\" is not local", name));
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "NONLOCAL", name, NULL);
+           return TCL_ERROR;
+       }
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckOneByte --
+ *
+ *     Verify that a constant fits in a single byte in the instruction
+ *     stream.
+ *
+ * Results:
+ *     On success, returns TCL_OK. On failure, returns TCL_ERROR and stores
+ *     an error message in the interpreter result.
+ *
+ * This code is here primarily to verify that instructions like INCR_SCALAR1
+ * are possible on a given local variable. The fact that there is no
+ * INCR_SCALAR4 is puzzling.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckOneByte(
+    Tcl_Interp* interp,                /* Tcl interpreter for error reporting */
+    int value)                 /* Value to check */
+{
+    Tcl_Obj* result;           /* Error message */
+
+    if (value < 0 || value > 0xFF) {
+       result = Tcl_NewStringObj("operand does not fit in one byte", -1);
+       Tcl_SetObjResult(interp, result);
+       Tcl_SetErrorCode(interp, "TCL", "ASSEM", "1BYTE", NULL);
+       return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckSignedOneByte --
+ *
+ *     Verify that a constant fits in a single signed byte in the instruction
+ *     stream.
+ *
+ * Results:
+ *     On success, returns TCL_OK. On failure, returns TCL_ERROR and stores
+ *     an error message in the interpreter result.
+ *
+ * This code is here primarily to verify that instructions like INCR_SCALAR1
+ * are possible on a given local variable. The fact that there is no
+ * INCR_SCALAR4 is puzzling.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckSignedOneByte(
+    Tcl_Interp* interp,                /* Tcl interpreter for error reporting */
+    int value)                 /* Value to check */
+{
+    Tcl_Obj* result;           /* Error message */
+
+    if (value > 0x7F || value < -0x80) {
+       result = Tcl_NewStringObj("operand does not fit in one byte", -1);
+       Tcl_SetObjResult(interp, result);
+       Tcl_SetErrorCode(interp, "TCL", "ASSEM", "1BYTE", NULL);
+       return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckNonNegative --
+ *
+ *     Verify that a constant is nonnegative
+ *
+ * Results:
+ *     On success, returns TCL_OK. On failure, returns TCL_ERROR and stores
+ *     an error message in the interpreter result.
+ *
+ * This code is here primarily to verify that instructions like INCR_INVOKE
+ * are consuming a positive number of operands
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckNonNegative(
+    Tcl_Interp* interp,                /* Tcl interpreter for error reporting */
+    int value)                 /* Value to check */
+{
+    Tcl_Obj* result;           /* Error message */
+
+    if (value < 0) {
+       result = Tcl_NewStringObj("operand must be nonnegative", -1);
+       Tcl_SetObjResult(interp, result);
+       Tcl_SetErrorCode(interp, "TCL", "ASSEM", "NONNEGATIVE", NULL);
+       return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckStrictlyPositive --
+ *
+ *     Verify that a constant is positive
+ *
+ * Results:
+ *     On success, returns TCL_OK. On failure, returns TCL_ERROR and
+ *     stores an error message in the interpreter result.
+ *
+ * This code is here primarily to verify that instructions like INCR_INVOKE
+ * are consuming a positive number of operands
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckStrictlyPositive(
+    Tcl_Interp* interp,                /* Tcl interpreter for error reporting */
+    int value)                 /* Value to check */
+{
+    Tcl_Obj* result;           /* Error message */
+
+    if (value <= 0) {
+       result = Tcl_NewStringObj("operand must be positive", -1);
+       Tcl_SetObjResult(interp, result);
+       Tcl_SetErrorCode(interp, "TCL", "ASSEM", "POSITIVE", NULL);
+       return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DefineLabel --
+ *
+ *     Defines a label appearing in the assembly sequence.
+ *
+ * Results:
+ *     Returns a standard Tcl result. Returns TCL_OK and an empty result if
+ *     the definition succeeds; returns TCL_ERROR and an appropriate message
+ *     if a duplicate definition is found.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+DefineLabel(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    const char* labelName)     /* Label being defined */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    Tcl_HashEntry* entry;      /* Label's entry in the symbol table */
+    int isNew;                 /* Flag == 1 iff the label was previously
+                                * undefined */
+
+    /* TODO - This can now be simplified! */
+
+    StartBasicBlock(assemEnvPtr, BB_FALLTHRU, NULL);
+
+    /*
+     * Look up the newly-defined label in the symbol table.
+     */
+
+    entry = Tcl_CreateHashEntry(&assemEnvPtr->labelHash, labelName, &isNew);
+    if (!isNew) {
+       /*
+        * This is a duplicate label.
+        */
+
+       if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+           Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+                   "duplicate definition of label \"%s\"", labelName));
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "DUPLABEL", labelName,
+                   NULL);
+       }
+       return TCL_ERROR;
+    }
+
+    /*
+     * This is the first appearance of the label in the code.
+     */
+
+    Tcl_SetHashValue(entry, assemEnvPtr->curr_bb);
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StartBasicBlock --
+ *
+ *     Starts a new basic block when a label or jump is encountered.
+ *
+ * Results:
+ *     Returns a pointer to the BasicBlock structure of the new
+ *     basic block.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static BasicBlock*
+StartBasicBlock(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    int flags,                 /* Flags to apply to the basic block being
+                                * closed, if there is one. */
+    Tcl_Obj* jumpLabel)                /* Label of the location that the block jumps
+                                * to, or NULL if the block does not jump */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* newBB;         /* BasicBlock structure for the new block */
+    BasicBlock* currBB = assemEnvPtr->curr_bb;
+
+    /*
+     * Coalesce zero-length blocks.
+     */
+
+    if (currBB->startOffset == envPtr->codeNext - envPtr->codeStart) {
+       currBB->startLine = assemEnvPtr->cmdLine;
+       return currBB;
+    }
+
+    /*
+     * Make the new basic block.
+     */
+
+    newBB = AllocBB(assemEnvPtr);
+
+    /*
+     * Record the jump target if there is one.
+     */
+
+    currBB->jumpTarget = jumpLabel;
+    if (jumpLabel != NULL) {
+       Tcl_IncrRefCount(currBB->jumpTarget);
+    }
+
+    /*
+     * Record the fallthrough if there is one.
+     */
+
+    currBB->flags |= flags;
+
+    /*
+     * Record the successor block.
+     */
+
+    currBB->successor1 = newBB;
+    assemEnvPtr->curr_bb = newBB;
+    return newBB;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * AllocBB --
+ *
+ *     Allocates a new basic block
+ *
+ * Results:
+ *     Returns a pointer to the newly allocated block, which is initialized
+ *     to contain no code and begin at the current instruction pointer.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static BasicBlock *
+AllocBB(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+    BasicBlock *bb = (BasicBlock*)ckalloc(sizeof(BasicBlock));
+
+    bb->originalStartOffset =
+           bb->startOffset = envPtr->codeNext - envPtr->codeStart;
+    bb->startLine = assemEnvPtr->cmdLine + 1;
+    bb->jumpOffset = -1;
+    bb->jumpLine = -1;
+    bb->prevPtr = assemEnvPtr->curr_bb;
+    bb->predecessor = NULL;
+    bb->successor1 = NULL;
+    bb->jumpTarget = NULL;
+    bb->initialStackDepth = 0;
+    bb->minStackDepth = 0;
+    bb->maxStackDepth = 0;
+    bb->finalStackDepth = 0;
+    bb->catchDepth = 0;
+    bb->enclosingCatch = NULL;
+    bb->foreignExceptionBase = -1;
+    bb->foreignExceptionCount = 0;
+    bb->foreignExceptions = NULL;
+    bb->jtPtr = NULL;
+    bb->flags = 0;
+
+    return bb;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FinishAssembly --
+ *
+ *     Postprocessing after all bytecode has been generated for a block of
+ *     assembly code.
+ *
+ * Results:
+ *     Returns a standard Tcl result, with an error message left in the
+ *     interpreter if appropriate.
+ *
+ * Side effects:
+ *     The program is checked to see if any undefined labels remain.  The
+ *     initial stack depth of all the basic blocks in the flow graph is
+ *     calculated and saved.  The stack balance on exit is computed, checked
+ *     and saved.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+FinishAssembly(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    int mustMove;              /* Amount by which the code needs to be grown
+                                * because of expanding jumps */
+
+    /*
+     * Resolve the targets of all jumps and determine whether code needs to be
+     * moved around.
+     */
+
+    if (CalculateJumpRelocations(assemEnvPtr, &mustMove)) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Move the code if necessary.
+     */
+
+    if (mustMove) {
+       MoveCodeForJumps(assemEnvPtr, mustMove);
+    }
+
+    /*
+     * Resolve jump target labels to bytecode offsets.
+     */
+
+    FillInJumpOffsets(assemEnvPtr);
+
+    /*
+     * Label each basic block with its catch context. Quit on inconsistency.
+     */
+
+    if (ProcessCatches(assemEnvPtr) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Make sure that no block accessible from a catch's error exit that hasn't
+     * popped the exception stack can throw an exception.
+     */
+
+    if (CheckForThrowInWrongContext(assemEnvPtr) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Compute stack balance throughout the program.
+     */
+
+    if (CheckStack(assemEnvPtr) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * TODO - Check for unreachable code. Or maybe not; unreachable code is
+     * Mostly Harmless.
+     */
+
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CalculateJumpRelocations --
+ *
+ *     Calculate any movement that has to be done in the assembly code to
+ *     expand JUMP1 instructions to JUMP4 (because they jump more than a
+ *     1-byte range).
+ *
+ * Results:
+ *     Returns a standard Tcl result, with an appropriate error message if
+ *     anything fails.
+ *
+ * Side effects:
+ *     Sets the 'startOffset' pointer in every basic block to the new origin
+ *     of the block, and turns off JUMP1 flags on instructions that must be
+ *     expanded (and adjusts them to the corresponding JUMP4's).  Does *not*
+ *     store the jump offsets at this point.
+ *
+ *     Sets *mustMove to 1 if and only if at least one instruction changed
+ *     size so the code must be moved.
+ *
+ *     As a side effect, also checks for undefined labels and reports them.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CalculateJumpRelocations(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    int* mustMove)             /* OUTPUT: Number of bytes that have been
+                                * added to the code */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* bbPtr;         /* Pointer to a basic block being checked */
+    Tcl_HashEntry* entry;      /* Exit label's entry in the symbol table */
+    BasicBlock* jumpTarget;    /* Basic block where the jump goes */
+    int motion;                        /* Amount by which the code has expanded */
+    int offset;                        /* Offset in the bytecode from a jump
+                                * instruction to its target */
+    unsigned opcode;           /* Opcode in the bytecode being adjusted */
+
+    /*
+     * Iterate through basic blocks as long as a change results in code
+     * expansion.
+     */
+
+    *mustMove = 0;
+    do {
+       motion = 0;
+       for (bbPtr = assemEnvPtr->head_bb;
+               bbPtr != NULL;
+               bbPtr = bbPtr->successor1) {
+           /*
+            * Advance the basic block start offset by however many bytes we
+            * have inserted in the code up to this point
+            */
+
+           bbPtr->startOffset += motion;
+
+           /*
+            * If the basic block references a label (and hence performs a
+            * jump), find the location of the label. Report an error if the
+            * label is missing.
+            */
+
+           if (bbPtr->jumpTarget != NULL) {
+               entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+                       Tcl_GetString(bbPtr->jumpTarget));
+               if (entry == NULL) {
+                   ReportUndefinedLabel(assemEnvPtr, bbPtr,
+                           bbPtr->jumpTarget);
+                   return TCL_ERROR;
+               }
+
+               /*
+                * If the instruction is a JUMP1, turn it into a JUMP4 if its
+                * target is out of range.
+                */
+
+               jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
+               if (bbPtr->flags & BB_JUMP1) {
+                   offset = jumpTarget->startOffset
+                           - (bbPtr->jumpOffset + motion);
+                   if (offset < -0x80 || offset > 0x7F) {
+                       opcode = TclGetUInt1AtPtr(envPtr->codeStart
+                               + bbPtr->jumpOffset);
+                       ++opcode;
+                       TclStoreInt1AtPtr(opcode,
+                               envPtr->codeStart + bbPtr->jumpOffset);
+                       motion += 3;
+                       bbPtr->flags &= ~BB_JUMP1;
+                   }
+               }
+           }
+
+           /*
+            * If the basic block references a jump table, that doesn't affect
+            * the code locations, but resolve the labels now, and store basic
+            * block pointers in the jumptable hash.
+            */
+
+           if (bbPtr->flags & BB_JUMPTABLE) {
+               if (CheckJumpTableLabels(assemEnvPtr, bbPtr) != TCL_OK) {
+                   return TCL_ERROR;
+               }
+           }
+       }
+       *mustMove += motion;
+    } while (motion != 0);
+
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckJumpTableLabels --
+ *
+ *     Make sure that all the labels in a jump table are defined.
+ *
+ * Results:
+ *     Returns TCL_OK if they are, TCL_ERROR if they aren't.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+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_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 */
+
+    /*
+     * Look up every jump target in the jump hash.
+     */
+
+    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,
+               Tcl_GetString(symbolObj));
+       DEBUG_PRINT("  %s -> %s (%d)\n",
+               (char*) Tcl_GetHashKey(symHash, symEntryPtr),
+               Tcl_GetString(symbolObj), (valEntryPtr != NULL));
+       if (valEntryPtr == NULL) {
+           ReportUndefinedLabel(assemEnvPtr, bbPtr, symbolObj);
+           return TCL_ERROR;
+       }
+    }
+    DEBUG_PRINT("}\n");
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ReportUndefinedLabel --
+ *
+ *     Report that a basic block refers to an undefined jump label
+ *
+ * Side effects:
+ *     Stores an error message, error code, and line number information in
+ *     the assembler's Tcl interpreter.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+ReportUndefinedLabel(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    BasicBlock* bbPtr,         /* Basic block that contains the undefined
+                                * label */
+    Tcl_Obj* jumpTarget)       /* Label of a jump target */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+
+    if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+       Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+               "undefined label \"%s\"", Tcl_GetString(jumpTarget)));
+       Tcl_SetErrorCode(interp, "TCL", "ASSEM", "NOLABEL",
+               Tcl_GetString(jumpTarget), NULL);
+       Tcl_SetErrorLine(interp, bbPtr->jumpLine);
+    }
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * MoveCodeForJumps --
+ *
+ *     Move bytecodes in memory to accommodate JUMP1 instructions that have
+ *     expanded to become JUMP4's.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+MoveCodeForJumps(
+    AssemblyEnv* assemEnvPtr,  /* Assembler environment */
+    int mustMove)              /* Number of bytes of added code */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* bbPtr;         /* Pointer to a basic block being checked */
+    int topOffset;             /* Bytecode offset of the following basic
+                                * block before code motion */
+
+    /*
+     * Make sure that there is enough space in the bytecode array to
+     * accommodate the expanded code.
+     */
+
+    while (envPtr->codeEnd < envPtr->codeNext + mustMove) {
+       TclExpandCodeArray(envPtr);
+    }
+
+    /*
+     * Iterate through the bytecodes in reverse order, and move them upward to
+     * their new homes.
+     */
+
+    topOffset = envPtr->codeNext - envPtr->codeStart;
+    for (bbPtr = assemEnvPtr->curr_bb; bbPtr != NULL; bbPtr = bbPtr->prevPtr) {
+       DEBUG_PRINT("move code from %d to %d\n",
+               bbPtr->originalStartOffset, bbPtr->startOffset);
+       memmove(envPtr->codeStart + bbPtr->startOffset,
+               envPtr->codeStart + bbPtr->originalStartOffset,
+               topOffset - bbPtr->originalStartOffset);
+       topOffset = bbPtr->originalStartOffset;
+       bbPtr->jumpOffset += (bbPtr->startOffset - bbPtr->originalStartOffset);
+    }
+    envPtr->codeNext += mustMove;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FillInJumpOffsets --
+ *
+ *     Fill in the final offsets of all jump instructions once bytecode
+ *     locations have been completely determined.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+FillInJumpOffsets(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* bbPtr;         /* Pointer to a basic block being checked */
+    Tcl_HashEntry* entry;      /* Hashtable entry for a jump target label */
+    BasicBlock* jumpTarget;    /* Basic block where a jump goes */
+    int fromOffset;            /* Bytecode location of a jump instruction */
+    int targetOffset;          /* Bytecode location of a jump instruction's
+                                * target */
+
+    for (bbPtr = assemEnvPtr->head_bb;
+           bbPtr != NULL;
+           bbPtr = bbPtr->successor1) {
+       if (bbPtr->jumpTarget != NULL) {
+           entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+                   Tcl_GetString(bbPtr->jumpTarget));
+           jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
+           fromOffset = bbPtr->jumpOffset;
+           targetOffset = jumpTarget->startOffset;
+           if (bbPtr->flags & BB_JUMP1) {
+               TclStoreInt1AtPtr(targetOffset - fromOffset,
+                       envPtr->codeStart + fromOffset + 1);
+           } else {
+               TclStoreInt4AtPtr(targetOffset - fromOffset,
+                       envPtr->codeStart + fromOffset + 1);
+           }
+       }
+       if (bbPtr->flags & BB_JUMPTABLE) {
+           ResolveJumpTableTargets(assemEnvPtr, bbPtr);
+       }
+    }
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ResolveJumpTableTargets --
+ *
+ *     Puts bytecode addresses for the targets of a jumptable into the
+ *     table
+ *
+ * Results:
+ *     Returns TCL_OK if they are, TCL_ERROR if they aren't.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+ResolveJumpTableTargets(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    BasicBlock* bbPtr)         /* Basic block that ends in a jump table */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_HashTable* symHash = &bbPtr->jtPtr->hashTable;
+                               /* 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;
+                               /* Entry in the jump table hash in
+                                * the actual code */
+    BasicBlock* jumpTargetBBPtr;
+                               /* 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;
+
+    /*
+     * 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", Tcl_GetString(symbolObj));
+
+       valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+               Tcl_GetString(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),
+               Tcl_GetString(symbolObj), jumpTargetBBPtr,
+               jumpTargetBBPtr->startOffset, realJumpEntryPtr);
+
+       Tcl_SetHashValue(realJumpEntryPtr,
+               INT2PTR(jumpTargetBBPtr->startOffset - bbPtr->jumpOffset));
+    }
+    DEBUG_PRINT("}\n");
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckForThrowInWrongContext --
+ *
+ *     Verify that no beginCatch/endCatch sequence can throw an exception
+ *     after an original exception is caught and before its exception context
+ *     is removed from the stack.
+ *
+ * Results:
+ *     Returns a standard Tcl result.
+ *
+ * Side effects:
+ *     Stores an appropriate error message in the interpreter as needed.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckForThrowInWrongContext(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    BasicBlock* blockPtr;      /* Current basic block */
+
+    /*
+     * Walk through the basic blocks in turn, checking all the ones that have
+     * caught an exception and not disposed of it properly.
+     */
+
+    for (blockPtr = assemEnvPtr->head_bb;
+           blockPtr != NULL;
+           blockPtr = blockPtr->successor1) {
+       if (blockPtr->catchState == BBCS_CAUGHT) {
+           /*
+            * Walk through the instructions in the basic block.
+            */
+
+           if (CheckNonThrowingBlock(assemEnvPtr, blockPtr) != TCL_OK) {
+               return TCL_ERROR;
+           }
+       }
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckNonThrowingBlock --
+ *
+ *     Check that a basic block cannot throw an exception.
+ *
+ * Results:
+ *     Returns TCL_ERROR if the block cannot be proven to be nonthrowing.
+ *
+ * Side effects:
+ *     Stashes an error message in the interpreter result.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckNonThrowingBlock(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    BasicBlock* blockPtr)      /* Basic block where exceptions are not
+                                * allowed */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    BasicBlock* nextPtr;       /* Pointer to the succeeding basic block */
+    int offset;                        /* Bytecode offset of the current
+                                * instruction */
+    int bound;                 /* Bytecode offset following the last
+                                * instruction of the block. */
+    unsigned char opcode;      /* Current bytecode instruction */
+
+    /*
+     * Determine where in the code array the basic block ends.
+     */
+
+    nextPtr = blockPtr->successor1;
+    if (nextPtr == NULL) {
+       bound = envPtr->codeNext - envPtr->codeStart;
+    } else {
+       bound = nextPtr->startOffset;
+    }
+
+    /*
+     * Walk through the instructions of the block.
+     */
+
+    offset = blockPtr->startOffset;
+    while (offset < bound) {
+       /*
+        * Determine whether an instruction is nonthrowing.
+        */
+
+       opcode = (envPtr->codeStart)[offset];
+       if (BytecodeMightThrow(opcode)) {
+           /*
+            * Report an error for a throw in the wrong context.
+            */
+
+           if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+               Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+                       "\"%s\" instruction may not appear in "
+                       "a context where an exception has been "
+                       "caught and not disposed of.",
+                       tclInstructionTable[opcode].name));
+               Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADTHROW", NULL);
+               AddBasicBlockRangeToErrorInfo(assemEnvPtr, blockPtr);
+           }
+           return TCL_ERROR;
+       }
+       offset += tclInstructionTable[opcode].numBytes;
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * BytecodeMightThrow --
+ *
+ *     Tests if a given bytecode instruction might throw an exception.
+ *
+ * Results:
+ *     Returns 1 if the bytecode might throw an exception, 0 if the
+ *     instruction is known never to throw.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+BytecodeMightThrow(
+    unsigned char opcode)
+{
+    /*
+     * Binary search on the non-throwing bytecode list.
+     */
+
+    int min = 0;
+    int max = sizeof(NonThrowingByteCodes) - 1;
+    int mid;
+    unsigned char c;
+
+    while (max >= min) {
+       mid = (min + max) / 2;
+       c = NonThrowingByteCodes[mid];
+       if (opcode < c) {
+           max = mid-1;
+       } else if (opcode > c) {
+           min = mid+1;
+       } else {
+           /*
+            * Opcode is nonthrowing.
+            */
+
+           return 0;
+       }
+    }
+
+    return 1;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckStack --
+ *
+ *     Audit stack usage in a block of assembly code.
+ *
+ * Results:
+ *     Returns a standard Tcl result.
+ *
+ * Side effects:
+ *     Updates stack depth on entry for all basic blocks in the flowgraph.
+ *     Calculates the max stack depth used in the program, and updates the
+ *     compilation environment to reflect it.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckStack(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    int maxDepth;              /* Maximum stack depth overall */
+
+    /*
+     * Checking the head block will check all the other blocks recursively.
+     */
+
+    assemEnvPtr->maxDepth = 0;
+    if (StackCheckBasicBlock(assemEnvPtr, assemEnvPtr->head_bb, NULL,
+           0) == TCL_ERROR) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Post the max stack depth back to the compilation environment.
+     */
+
+    maxDepth = assemEnvPtr->maxDepth + envPtr->currStackDepth;
+    if (maxDepth > envPtr->maxStackDepth) {
+       envPtr->maxStackDepth = maxDepth;
+    }
+
+    /*
+     * If the exit is reachable, make sure that the program exits with 1
+     * operand on the stack.
+     */
+
+    if (StackCheckExit(assemEnvPtr) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Reset the visited state on all basic blocks.
+     */
+
+    ResetVisitedBasicBlocks(assemEnvPtr);
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StackCheckBasicBlock --
+ *
+ *     Checks stack consumption for a basic block (and recursively for its
+ *     successors).
+ *
+ * Results:
+ *     Returns a standard Tcl result.
+ *
+ * Side effects:
+ *     Updates initial stack depth for the basic block and its successors.
+ *     (Final and maximum stack depth are relative to initial, and are not
+ *     touched).
+ *
+ * This procedure eventually checks, for the entire flow graph, whether stack
+ * balance is consistent.  It is an error for a given basic block to be
+ * reachable along multiple flow paths with different stack depths.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+StackCheckBasicBlock(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    BasicBlock* blockPtr,      /* Pointer to the basic block being checked */
+    BasicBlock* predecessor,   /* Pointer to the block that passed control to
+                                * this one. */
+    int initialStackDepth)     /* Stack depth on entry to the block */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    BasicBlock* jumpTarget;    /* Basic block where a jump goes */
+    int stackDepth;            /* Current stack depth */
+    int maxDepth;              /* Maximum stack depth so far */
+    int result;                        /* Tcl status return */
+    Tcl_HashSearch jtSearch;   /* Search structure for the jump table */
+    Tcl_HashEntry* jtEntry;    /* Hash entry in the jump table */
+    Tcl_Obj* targetLabel;      /* Target label from the jump table */
+    Tcl_HashEntry* entry;      /* Hash entry in the label table */
+
+    if (blockPtr->flags & BB_VISITED) {
+       /*
+        * If the block is already visited, check stack depth for consistency
+        * among the paths that reach it.
+        */
+
+       if (blockPtr->initialStackDepth == initialStackDepth) {
+           return TCL_OK;
+       }
+       if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+           Tcl_SetObjResult(interp, Tcl_NewStringObj(
+                   "inconsistent stack depths on two execution paths", -1));
+
+           /*
+            * TODO - add execution trace of both paths
+            */
+
+           Tcl_SetErrorLine(interp, blockPtr->startLine);
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADSTACK", NULL);
+       }
+       return TCL_ERROR;
+    }
+
+    /*
+     * If the block is not already visited, set the 'predecessor' link to
+     * indicate how control got to it. Set the initial stack depth to the
+     * current stack depth in the flow of control.
+     */
+
+    blockPtr->flags |= BB_VISITED;
+    blockPtr->predecessor = predecessor;
+    blockPtr->initialStackDepth = initialStackDepth;
+
+    /*
+     * Calculate minimum stack depth, and flag an error if the block
+     * underflows the stack.
+     */
+
+    if (initialStackDepth + blockPtr->minStackDepth < 0) {
+       if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+           Tcl_SetObjResult(interp, Tcl_NewStringObj("stack underflow", -1));
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADSTACK", NULL);
+           AddBasicBlockRangeToErrorInfo(assemEnvPtr, blockPtr);
+           Tcl_SetErrorLine(interp, blockPtr->startLine);
+       }
+       return TCL_ERROR;
+    }
+
+    /*
+     * Make sure that the block doesn't try to pop below the stack level of an
+     * enclosing catch.
+     */
+
+    if (blockPtr->enclosingCatch != 0 &&
+           initialStackDepth + blockPtr->minStackDepth
+           < (blockPtr->enclosingCatch->initialStackDepth
+               + blockPtr->enclosingCatch->finalStackDepth)) {
+       if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+           Tcl_SetObjResult(interp, Tcl_NewStringObj(
+                   "code pops stack below level of enclosing catch", -1));
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADSTACKINCATCH", -1);
+           AddBasicBlockRangeToErrorInfo(assemEnvPtr, blockPtr);
+           Tcl_SetErrorLine(interp, blockPtr->startLine);
+       }
+       return TCL_ERROR;
+    }
+
+    /*
+     * Update maximum stgack depth.
+     */
+
+    maxDepth = initialStackDepth + blockPtr->maxStackDepth;
+    if (maxDepth > assemEnvPtr->maxDepth) {
+       assemEnvPtr->maxDepth = maxDepth;
+    }
+
+    /*
+     * Calculate stack depth on exit from the block, and invoke this procedure
+     * recursively to check successor blocks.
+     */
+
+    stackDepth = initialStackDepth + blockPtr->finalStackDepth;
+    result = TCL_OK;
+    if (blockPtr->flags & BB_FALLTHRU) {
+       result = StackCheckBasicBlock(assemEnvPtr, blockPtr->successor1,
+               blockPtr, stackDepth);
+    }
+
+    if (result == TCL_OK && blockPtr->jumpTarget != NULL) {
+       entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+               Tcl_GetString(blockPtr->jumpTarget));
+       jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
+       result = StackCheckBasicBlock(assemEnvPtr, jumpTarget, blockPtr,
+               stackDepth);
+    }
+
+    /*
+     * All blocks referenced in a jump table are successors.
+     */
+
+    if (blockPtr->flags & BB_JUMPTABLE) {
+       for (jtEntry = Tcl_FirstHashEntry(&blockPtr->jtPtr->hashTable,
+                   &jtSearch);
+               result == TCL_OK && jtEntry != NULL;
+               jtEntry = Tcl_NextHashEntry(&jtSearch)) {
+           targetLabel = (Tcl_Obj*)Tcl_GetHashValue(jtEntry);
+           entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+                   Tcl_GetString(targetLabel));
+           jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
+           result = StackCheckBasicBlock(assemEnvPtr, jumpTarget,
+                   blockPtr, stackDepth);
+       }
+    }
+
+    return result;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StackCheckExit --
+ *
+ *     Makes sure that the net stack effect of an entire assembly language
+ *     script is to push 1 result.
+ *
+ * Results:
+ *     Returns a standard Tcl result, with an error message in the
+ *     interpreter result if the stack is wrong.
+ *
+ * Side effects:
+ *     If the assembly code had a net stack effect of zero, emits code to the
+ *     concluding block to push a null result. In any case, updates the stack
+ *     depth in the compile environment to reflect the net effect of the
+ *     assembly code.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+StackCheckExit(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    int depth;                 /* Net stack effect */
+    int litIndex;              /* Index in the literal pool of the empty
+                                * string */
+    BasicBlock* curr_bb = assemEnvPtr->curr_bb;
+                               /* Final basic block in the assembly */
+
+    /*
+     * Don't perform these checks if execution doesn't reach the exit (either
+     * because of an infinite loop or because the only return is from the
+     * middle.
+     */
+
+    if (curr_bb->flags & BB_VISITED) {
+       /*
+        * Exit with no operands; push an empty one.
+        */
+
+       depth = curr_bb->finalStackDepth + curr_bb->initialStackDepth;
+       if (depth == 0) {
+           /*
+            * Emit a 'push' of the empty literal.
+            */
+
+           litIndex = TclRegisterNewLiteral(envPtr, "", 0);
+
+           /*
+            * Assumes that 'push' is at slot 0 in TalInstructionTable.
+            */
+
+           BBEmitInst1or4(assemEnvPtr, 0, litIndex, 0);
+           ++depth;
+       }
+
+       /*
+        * Exit with unbalanced stack.
+        */
+
+       if (depth != 1) {
+           if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+               Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+                       "stack is unbalanced on exit from the code (depth=%d)",
+                       depth));
+               Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADSTACK", NULL);
+           }
+           return TCL_ERROR;
+       }
+
+       /*
+        * Record stack usage.
+        */
+
+       envPtr->currStackDepth += depth;
+    }
+
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ProcessCatches --
+ *
+ *     First pass of 'catch' processing.
+ *
+ * Results:
+ *     Returns a standard Tcl result, with an appropriate error message if
+ *     the result is TCL_ERROR.
+ *
+ * Side effects:
+ *     Labels all basic blocks with their enclosing catches.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+ProcessCatches(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    BasicBlock* blockPtr;      /* Pointer to a basic block */
+
+    /*
+     * Clear the catch state of all basic blocks.
+     */
+
+    for (blockPtr = assemEnvPtr->head_bb;
+           blockPtr != NULL;
+           blockPtr = blockPtr->successor1) {
+       blockPtr->catchState = BBCS_UNKNOWN;
+       blockPtr->enclosingCatch = NULL;
+    }
+
+    /*
+     * Start the check recursively from the first basic block, which is
+     * outside any exception context
+     */
+
+    if (ProcessCatchesInBasicBlock(assemEnvPtr, assemEnvPtr->head_bb,
+           NULL, BBCS_NONE, 0) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Check for unclosed catch on exit.
+     */
+
+    if (CheckForUnclosedCatches(assemEnvPtr) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Now there's enough information to build the exception ranges.
+     */
+
+    if (BuildExceptionRanges(assemEnvPtr) != TCL_OK) {
+       return TCL_ERROR;
+    }
+
+    /*
+     * Finally, restore any exception ranges from embedded scripts.
+     */
+
+    RestoreEmbeddedExceptionRanges(assemEnvPtr);
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ProcessCatchesInBasicBlock --
+ *
+ *     First-pass catch processing for one basic block.
+ *
+ * Results:
+ *     Returns a standard Tcl result, with error message in the interpreter
+ *     result if an error occurs.
+ *
+ * This procedure checks consistency of the exception context through the
+ * assembler program, and records the enclosing 'catch' for every basic block.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+ProcessCatchesInBasicBlock(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    BasicBlock* bbPtr,         /* Basic block being processed */
+    BasicBlock* enclosing,     /* Start basic block of the enclosing catch */
+    enum BasicBlockCatchState state,
+                               /* BBCS_NONE, BBCS_INCATCH, or BBCS_CAUGHT */
+    int catchDepth)            /* Depth of nesting of catches */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    int result;                        /* Return value from this procedure */
+    BasicBlock* fallThruEnclosing;
+                               /* Enclosing catch if execution falls thru */
+    enum BasicBlockCatchState fallThruState;
+                               /* Catch state of the successor block */
+    BasicBlock* jumpEnclosing; /* Enclosing catch if execution goes to jump
+                                * target */
+    enum BasicBlockCatchState jumpState;
+                               /* Catch state of the jump target */
+    int changed = 0;           /* Flag == 1 iff successor blocks need to be
+                                * checked because the state of this block has
+                                * changed. */
+    BasicBlock* jumpTarget;    /* Basic block where a jump goes */
+    Tcl_HashSearch jtSearch;   /* Hash search control for a jumptable */
+    Tcl_HashEntry* jtEntry;    /* Entry in a jumptable */
+    Tcl_Obj* targetLabel;      /* Target label from a jumptable */
+    Tcl_HashEntry* entry;      /* Entry from the label table */
+
+    /*
+     * Update the state of the current block, checking for consistency.  Set
+     * 'changed' to 1 if the state changes and successor blocks need to be
+     * rechecked.
+     */
+
+    if (bbPtr->catchState == BBCS_UNKNOWN) {
+       bbPtr->enclosingCatch = enclosing;
+    } else if (bbPtr->enclosingCatch != enclosing) {
+       if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+           Tcl_SetObjResult(interp, Tcl_NewStringObj(
+                   "execution reaches an instruction in inconsistent "
+                   "exception contexts", -1));
+           Tcl_SetErrorLine(interp, bbPtr->startLine);
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADCATCH", NULL);
+       }
+       return TCL_ERROR;
+    }
+    if (state > bbPtr->catchState) {
+       bbPtr->catchState = state;
+       changed = 1;
+    }
+
+    /*
+     * If this block has been visited before, and its state hasn't changed,
+     * we're done with it for now.
+     */
+
+    if (!changed) {
+       return TCL_OK;
+    }
+    bbPtr->catchDepth = catchDepth;
+
+    /*
+     * Determine enclosing catch and 'caught' state for the fallthrough and
+     * the jump target. Default for both is the state of the current block.
+     */
+
+    fallThruEnclosing = enclosing;
+    fallThruState = state;
+    jumpEnclosing = enclosing;
+    jumpState = state;
+
+    /*
+     * TODO: Make sure that the test cases include validating that a natural
+     * loop can't include 'beginCatch' or 'endCatch'
+     */
+
+    if (bbPtr->flags & BB_BEGINCATCH) {
+       /*
+        * If the block begins a catch, the state for the successor is 'in
+        * catch'. The jump target is the exception exit, and the state of the
+        * jump target is 'caught.'
+        */
+
+       fallThruEnclosing = bbPtr;
+       fallThruState = BBCS_INCATCH;
+       jumpEnclosing = bbPtr;
+       jumpState = BBCS_CAUGHT;
+       ++catchDepth;
+    }
+
+    if (bbPtr->flags & BB_ENDCATCH) {
+       /*
+        * If the block ends a catch, the state for the successor is whatever
+        * the state was on entry to the catch.
+        */
+
+       if (enclosing == NULL) {
+           if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+               Tcl_SetObjResult(interp, Tcl_NewStringObj(
+                       "endCatch without a corresponding beginCatch", -1));
+               Tcl_SetErrorLine(interp, bbPtr->startLine);
+               Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADENDCATCH", NULL);
+           }
+           return TCL_ERROR;
+       }
+       fallThruEnclosing = enclosing->enclosingCatch;
+       fallThruState = enclosing->catchState;
+       --catchDepth;
+    }
+
+    /*
+     * Visit any successor blocks with the appropriate exception context
+     */
+
+    result = TCL_OK;
+    if (bbPtr->flags & BB_FALLTHRU) {
+       result = ProcessCatchesInBasicBlock(assemEnvPtr, bbPtr->successor1,
+               fallThruEnclosing, fallThruState, catchDepth);
+    }
+    if (result == TCL_OK && bbPtr->jumpTarget != NULL) {
+       entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+               Tcl_GetString(bbPtr->jumpTarget));
+       jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
+       result = ProcessCatchesInBasicBlock(assemEnvPtr, jumpTarget,
+               jumpEnclosing, jumpState, catchDepth);
+    }
+
+    /*
+     * All blocks referenced in a jump table are successors.
+     */
+
+    if (bbPtr->flags & BB_JUMPTABLE) {
+       for (jtEntry = Tcl_FirstHashEntry(&bbPtr->jtPtr->hashTable,&jtSearch);
+               result == TCL_OK && jtEntry != NULL;
+               jtEntry = Tcl_NextHashEntry(&jtSearch)) {
+           targetLabel = (Tcl_Obj*)Tcl_GetHashValue(jtEntry);
+           entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+                   Tcl_GetString(targetLabel));
+           jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
+           result = ProcessCatchesInBasicBlock(assemEnvPtr, jumpTarget,
+                   jumpEnclosing, jumpState, catchDepth);
+       }
+    }
+
+    return result;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * CheckForUnclosedCatches --
+ *
+ *     Checks that a sequence of assembly code has no unclosed catches on
+ *     exit.
+ *
+ * Results:
+ *     Returns a standard Tcl result, with an error message for unclosed
+ *     catches.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+CheckForUnclosedCatches(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+
+    if (assemEnvPtr->curr_bb->catchState >= BBCS_INCATCH) {
+       if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
+           Tcl_SetObjResult(interp, Tcl_NewStringObj(
+                   "catch still active on exit from assembly code", -1));
+           Tcl_SetErrorLine(interp,
+                   assemEnvPtr->curr_bb->enclosingCatch->startLine);
+           Tcl_SetErrorCode(interp, "TCL", "ASSEM", "UNCLOSEDCATCH", NULL);
+       }
+       return TCL_ERROR;
+    }
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * BuildExceptionRanges --
+ *
+ *     Walks through the assembly code and builds exception ranges for the
+ *     catches embedded therein.
+ *
+ * Results:
+ *     Returns a standard Tcl result with an error message in the interpreter
+ *     if anything is unsuccessful.
+ *
+ * Side effects:
+ *     Each contiguous block of code with a given catch exit is assigned an
+ *     exception range at the appropriate level.
+ *     Exception ranges in embedded blocks have their levels corrected and
+ *     collated into the table.
+ *     Blocks that end with 'beginCatch' are associated with the innermost
+ *     exception range of the following block.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static int
+BuildExceptionRanges(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* bbPtr;         /* Current basic block */
+    BasicBlock* prevPtr = NULL;        /* Previous basic block */
+    int catchDepth = 0;                /* Current catch depth */
+    int maxCatchDepth = 0;     /* Maximum catch depth in the program */
+    BasicBlock** catches;      /* Stack of catches in progress */
+    int* catchIndices;         /* Indices of the exception ranges of catches
+                                * in progress */
+    int i;
+
+    /*
+     * Determine the max catch depth for the entire assembly script
+     * (excluding embedded eval's and expr's, which will be handled later).
+     */
+
+    for (bbPtr=assemEnvPtr->head_bb; bbPtr != NULL; bbPtr=bbPtr->successor1) {
+       if (bbPtr->catchDepth > maxCatchDepth) {
+           maxCatchDepth = bbPtr->catchDepth;
+       }
+    }
+
+    /*
+     * Allocate memory for a stack of active catches.
+     */
+
+    catches = (BasicBlock**)ckalloc(maxCatchDepth * sizeof(BasicBlock*));
+    catchIndices = (int *)ckalloc(maxCatchDepth * sizeof(int));
+    for (i = 0; i < maxCatchDepth; ++i) {
+       catches[i] = NULL;
+       catchIndices[i] = -1;
+    }
+
+    /*
+     * Walk through the basic blocks and manage exception ranges.
+     */
+
+    for (bbPtr=assemEnvPtr->head_bb; bbPtr != NULL; bbPtr=bbPtr->successor1) {
+       UnstackExpiredCatches(envPtr, bbPtr, catchDepth, catches,
+               catchIndices);
+       LookForFreshCatches(bbPtr, catches);
+       StackFreshCatches(assemEnvPtr, bbPtr, catchDepth, catches,
+               catchIndices);
+
+       /*
+        * If the last block was a 'begin catch', fill in the exception range.
+        */
+
+       catchDepth = bbPtr->catchDepth;
+       if (prevPtr != NULL && (prevPtr->flags & BB_BEGINCATCH)) {
+           TclStoreInt4AtPtr(catchIndices[catchDepth-1],
+                   envPtr->codeStart + bbPtr->startOffset - 4);
+       }
+
+       prevPtr = bbPtr;
+    }
+
+    /* Make sure that all catches are closed */
+
+    if (catchDepth != 0) {
+       Tcl_Panic("unclosed catch at end of code in "
+               "tclAssembly.c:BuildExceptionRanges, can't happen");
+    }
+
+    /* Free temp storage */
+
+    ckfree(catchIndices);
+    ckfree(catches);
+
+    return TCL_OK;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * UnstackExpiredCatches --
+ *
+ *     Unstacks and closes the exception ranges for any catch contexts that
+ *     were active in the previous basic block but are inactive in the
+ *     current one.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+UnstackExpiredCatches(
+    CompileEnv* envPtr,                /* Compilation environment */
+    BasicBlock* bbPtr,         /* Basic block being processed */
+    int catchDepth,            /* Depth of nesting of catches prior to entry
+                                * to this block */
+    BasicBlock** catches,      /* Array of catch contexts */
+    int* catchIndices)         /* Indices of the exception ranges
+                                * corresponding to the catch contexts */
+{
+    ExceptionRange* range;     /* Exception range for a specific catch */
+    BasicBlock* block;         /* Catch block being examined */
+    BasicBlockCatchState catchState;
+                               /* State of the code relative to the catch
+                                * block being examined ("in catch" or
+                                * "caught"). */
+
+    /*
+     * Unstack any catches that are deeper than the nesting level of the basic
+     * block being entered.
+     */
+
+    while (catchDepth > bbPtr->catchDepth) {
+       --catchDepth;
+       if (catches[catchDepth] != NULL) {
+           range = envPtr->exceptArrayPtr + catchIndices[catchDepth];
+           range->numCodeBytes = bbPtr->startOffset - range->codeOffset;
+           catches[catchDepth] = NULL;
+           catchIndices[catchDepth] = -1;
+       }
+    }
+
+    /*
+     * Unstack any catches that don't match the basic block being entered,
+     * either because they are no longer part of the context, or because the
+     * context has changed from INCATCH to CAUGHT.
+     */
+
+    catchState = bbPtr->catchState;
+    block = bbPtr->enclosingCatch;
+    while (catchDepth > 0) {
+       --catchDepth;
+       if (catches[catchDepth] != NULL) {
+           if (catches[catchDepth] != block || catchState >= BBCS_CAUGHT) {
+               range = envPtr->exceptArrayPtr + catchIndices[catchDepth];
+               range->numCodeBytes = bbPtr->startOffset - range->codeOffset;
+               catches[catchDepth] = NULL;
+               catchIndices[catchDepth] = -1;
+           }
+           catchState = block->catchState;
+           block = block->enclosingCatch;
+       }
+    }
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * LookForFreshCatches --
+ *
+ *     Determines whether a basic block being entered needs any exception
+ *     ranges that are not already stacked.
+ *
+ * Does not create the ranges: this procedure iterates from the innermost
+ * catch outward, but exception ranges must be created from the outermost
+ * catch inward.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+LookForFreshCatches(
+    BasicBlock* bbPtr,         /* Basic block being entered */
+    BasicBlock** catches)      /* Array of catch contexts that are already
+                                * entered */
+{
+    BasicBlockCatchState catchState;
+                               /* State ("in catch" or "caught") of the
+                                * current catch. */
+    BasicBlock* block;         /* Current enclosing catch */
+    int catchDepth;            /* Nesting depth of the current catch */
+
+    catchState = bbPtr->catchState;
+    block = bbPtr->enclosingCatch;
+    catchDepth = bbPtr->catchDepth;
+    while (catchDepth > 0) {
+       --catchDepth;
+       if (catches[catchDepth] != block && catchState < BBCS_CAUGHT) {
+           catches[catchDepth] = block;
+       }
+       catchState = block->catchState;
+       block = block->enclosingCatch;
+    }
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * StackFreshCatches --
+ *
+ *     Make ExceptionRange records for any catches that are in the basic
+ *     block being entered and were not in the previous basic block.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+StackFreshCatches(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    BasicBlock* bbPtr,         /* Basic block being processed */
+    int catchDepth,            /* Depth of nesting of catches prior to entry
+                                * to this block */
+    BasicBlock** catches,      /* Array of catch contexts */
+    int* catchIndices)         /* Indices of the exception ranges
+                                * corresponding to the catch contexts */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    ExceptionRange* range;     /* Exception range for a specific catch */
+    BasicBlock* block;         /* Catch block being examined */
+    BasicBlock* errorExit;     /* Error exit from the catch block */
+    Tcl_HashEntry* entryPtr;
+
+    catchDepth = 0;
+
+    /*
+     * Iterate through the enclosing catch blocks from the outside in,
+     * looking for ones that don't have exception ranges (and are uncaught)
+     */
+
+    for (catchDepth = 0; catchDepth < bbPtr->catchDepth; ++catchDepth) {
+       if (catchIndices[catchDepth] == -1 && catches[catchDepth] != NULL) {
+           /*
+            * Create an exception range for a block that needs one.
+            */
+
+           block = catches[catchDepth];
+           catchIndices[catchDepth] =
+                   TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
+           range = envPtr->exceptArrayPtr + catchIndices[catchDepth];
+           range->nestingLevel = envPtr->exceptDepth + catchDepth;
+           envPtr->maxExceptDepth =
+                   TclMax(range->nestingLevel + 1, envPtr->maxExceptDepth);
+           range->codeOffset = bbPtr->startOffset;
+
+           entryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
+                   Tcl_GetString(block->jumpTarget));
+           if (entryPtr == NULL) {
+               Tcl_Panic("undefined label in tclAssembly.c:"
+                       "BuildExceptionRanges, can't happen");
+           }
+
+           errorExit = (BasicBlock*)Tcl_GetHashValue(entryPtr);
+           range->catchOffset = errorExit->startOffset;
+       }
+    }
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * RestoreEmbeddedExceptionRanges --
+ *
+ *     Processes an assembly script, replacing any exception ranges that
+ *     were present in embedded code.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+RestoreEmbeddedExceptionRanges(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    BasicBlock* bbPtr;         /* Current basic block */
+    int rangeBase;             /* Base of the foreign exception ranges when
+                                * they are reinstalled */
+    int rangeIndex;            /* Index of the current foreign exception
+                                * range as reinstalled */
+    ExceptionRange* range;     /* Current foreign exception range */
+    unsigned char opcode;      /* Current instruction's opcode */
+    int catchIndex;            /* Index of the exception range to which the
+                                * current instruction refers */
+    int i;
+
+    /*
+     * Walk the basic blocks looking for exceptions in embedded scripts.
+     */
+
+    for (bbPtr = assemEnvPtr->head_bb;
+           bbPtr != NULL;
+           bbPtr = bbPtr->successor1) {
+       if (bbPtr->foreignExceptionCount != 0) {
+           /*
+            * Reinstall the embedded exceptions and track their nesting level
+            */
+
+           rangeBase = envPtr->exceptArrayNext;
+           for (i = 0; i < bbPtr->foreignExceptionCount; ++i) {
+               range = bbPtr->foreignExceptions + i;
+               rangeIndex = TclCreateExceptRange(range->type, envPtr);
+               range->nestingLevel += envPtr->exceptDepth + bbPtr->catchDepth;
+               memcpy(envPtr->exceptArrayPtr + rangeIndex, range,
+                       sizeof(ExceptionRange));
+               if (range->nestingLevel >= envPtr->maxExceptDepth) {
+                   envPtr->maxExceptDepth = range->nestingLevel + 1;
+               }
+           }
+
+           /*
+            * Walk through the bytecode of the basic block, and relocate
+            * INST_BEGIN_CATCH4 instructions to the new locations
+            */
+
+           i = bbPtr->startOffset;
+           while (i < bbPtr->successor1->startOffset) {
+               opcode = envPtr->codeStart[i];
+               if (opcode == INST_BEGIN_CATCH4) {
+                   catchIndex = TclGetUInt4AtPtr(envPtr->codeStart + i + 1);
+                   if (catchIndex >= bbPtr->foreignExceptionBase
+                           && catchIndex < (bbPtr->foreignExceptionBase +
+                           bbPtr->foreignExceptionCount)) {
+                       catchIndex -= bbPtr->foreignExceptionBase;
+                       catchIndex += rangeBase;
+                       TclStoreInt4AtPtr(catchIndex, envPtr->codeStart+i+1);
+                   }
+               }
+               i += tclInstructionTable[opcode].numBytes;
+           }
+       }
+    }
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * ResetVisitedBasicBlocks --
+ *
+ *     Turns off the 'visited' flag in all basic blocks at the conclusion
+ *     of a pass.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+ResetVisitedBasicBlocks(
+    AssemblyEnv* assemEnvPtr)  /* Assembly environment */
+{
+    BasicBlock* block;
+
+    for (block = assemEnvPtr->head_bb; block != NULL;
+           block = block->successor1) {
+       block->flags &= ~BB_VISITED;
+    }
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * AddBasicBlockRangeToErrorInfo --
+ *
+ *     Updates the error info of the Tcl interpreter to show a given basic
+ *     block in the code.
+ *
+ * This procedure is used to label the callstack with source location
+ * information when reporting an error in stack checking.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+AddBasicBlockRangeToErrorInfo(
+    AssemblyEnv* assemEnvPtr,  /* Assembly environment */
+    BasicBlock* bbPtr)         /* Basic block in which the error is found */
+{
+    CompileEnv* envPtr = assemEnvPtr->envPtr;
+                               /* Compilation environment */
+    Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
+                               /* Tcl interpreter */
+    Tcl_Obj* lineNo;           /* Line number in the source */
+
+    Tcl_AddErrorInfo(interp, "\n    in assembly code between lines ");
+    TclNewIntObj(lineNo, bbPtr->startLine);
+    Tcl_IncrRefCount(lineNo);
+    Tcl_AppendObjToErrorInfo(interp, lineNo);
+    Tcl_AddErrorInfo(interp, " and ");
+    if (bbPtr->successor1 != NULL) {
+       Tcl_SetIntObj(lineNo, bbPtr->successor1->startLine);
+       Tcl_AppendObjToErrorInfo(interp, lineNo);
+    } else {
+       Tcl_AddErrorInfo(interp, "end of assembly code");
+    }
+    Tcl_DecrRefCount(lineNo);
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * DupAssembleCodeInternalRep --
+ *
+ *     Part of the Tcl object type implementation for Tcl assembly language
+ *     bytecode. We do not copy the bytecode internalrep. Instead, we return
+ *     without setting copyPtr->typePtr, so the copy is a plain string copy
+ *     of the assembly source, and if it is to be used as a compiled
+ *     expression, it will need to be reprocessed.
+ *
+ *     This makes sense, because with Tcl's copy-on-write practices, the
+ *     usual (only?) time Tcl_DuplicateObj() will be called is when the copy
+ *     is about to be modified, which would invalidate any copied bytecode
+ *     anyway. The only reason it might make sense to copy the bytecode is if
+ *     we had some modifying routines that operated directly on the internalrep,
+ *     as we do for lists and dicts.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     None.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+DupAssembleCodeInternalRep(
+    Tcl_Obj *srcPtr,
+    Tcl_Obj *copyPtr)
+{
+    (void)srcPtr;
+    (void)copyPtr;
+
+    return;
+}
+\f
+/*
+ *-----------------------------------------------------------------------------
+ *
+ * FreeAssembleCodeInternalRep --
+ *
+ *     Part of the Tcl object type implementation for Tcl expression
+ *     bytecode. Frees the storage allocated to hold the internal rep, unless
+ *     ref counts indicate bytecode execution is still in progress.
+ *
+ * Results:
+ *     None.
+ *
+ * Side effects:
+ *     May free allocated memory. Leaves objPtr untyped.
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void
+FreeAssembleCodeInternalRep(
+    Tcl_Obj *objPtr)
+{
+    ByteCode *codePtr = (ByteCode *)objPtr->internalRep.twoPtrValue.ptr1;
+
+    if (codePtr->refCount-- <= 1) {
+       TclCleanupByteCode(codePtr);
+    }
+    objPtr->typePtr = NULL;
+}
+\f
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * End:
+ */