4 * Assembler for Tcl bytecodes.
6 * This file contains the procedures that convert Tcl Assembly Language (TAL)
7 * to a sequence of bytecode instructions for the Tcl execution engine.
9 * Copyright (c) 2010 by Ozgur Dogan Ugurlu.
10 * Copyright (c) 2010 by Kevin B. Kenny.
12 * See the file "license.terms" for information on usage and redistribution of
13 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
19 *- done - alternate exit point (affects stack and exception range checking)
20 *- break and continue - if exception ranges can be sorted out.
21 *- foreach_start4, foreach_step4
22 *- returnImm, returnStk
23 *- expandStart, expandStkTop, invokeExpanded, expandDrop
24 *- dictFirst, dictNext, dictDone
25 *- dictUpdateStart, dictUpdateEnd
29 *- tclooNext, tclooNextClass
33 #include "tclCompile.h"
37 * Structure that represents a range of instructions in the bytecode.
40 typedef struct CodeRange {
41 int startOffset; /* Start offset in the bytecode array */
42 int endOffset; /* End offset in the bytecode array */
46 * State identified for a basic block's catch context.
49 typedef enum BasicBlockCatchState {
50 BBCS_UNKNOWN = 0, /* Catch context has not yet been identified */
51 BBCS_NONE, /* Block is outside of any catch */
52 BBCS_INCATCH, /* Block is within a catch context */
53 BBCS_CAUGHT /* Block is within a catch context and
54 * may be executed after an exception fires */
55 } BasicBlockCatchState;
58 * Structure that defines a basic block - a linear sequence of bytecode
59 * instructions with no jumps in or out (including not changing the
60 * state of any exception range).
63 typedef struct BasicBlock {
64 int originalStartOffset; /* Instruction offset before JUMP1s were
65 * substituted with JUMP4's */
66 int startOffset; /* Instruction offset of the start of the
68 int startLine; /* Line number in the input script of the
69 * instruction at the start of the block */
70 int jumpOffset; /* Bytecode offset of the 'jump' instruction
71 * that ends the block, or -1 if there is no
73 int jumpLine; /* Line number in the input script of the
74 * 'jump' instruction that ends the block, or
75 * -1 if there is no jump */
76 struct BasicBlock* prevPtr; /* Immediate predecessor of this block */
77 struct BasicBlock* predecessor;
78 /* Predecessor of this block in the spanning
80 struct BasicBlock* successor1;
81 /* BasicBlock structure of the following
82 * block: NULL at the end of the bytecode
84 Tcl_Obj* jumpTarget; /* Jump target label if the jump target is
86 int initialStackDepth; /* Absolute stack depth on entry */
87 int minStackDepth; /* Low-water relative stack depth */
88 int maxStackDepth; /* High-water relative stack depth */
89 int finalStackDepth; /* Relative stack depth on exit */
90 enum BasicBlockCatchState catchState;
91 /* State of the block for 'catch' analysis */
92 int catchDepth; /* Number of nested catches in which the basic
94 struct BasicBlock* enclosingCatch;
95 /* BasicBlock structure of the last startCatch
96 * executed on a path to this block, or NULL
97 * if there is no enclosing catch */
98 int foreignExceptionBase; /* Base index of foreign exceptions */
99 int foreignExceptionCount; /* Count of foreign exceptions */
100 ExceptionRange* foreignExceptions;
101 /* ExceptionRange structures for exception
102 * ranges belonging to embedded scripts and
103 * expressions in this block */
104 JumptableInfo* jtPtr; /* Jump table at the end of this basic block */
105 int flags; /* Boolean flags */
109 * Flags that pertain to a basic block.
112 enum BasicBlockFlags {
113 BB_VISITED = (1 << 0), /* Block has been visited in the current
115 BB_FALLTHRU = (1 << 1), /* Control may pass from this block to a
117 BB_JUMP1 = (1 << 2), /* Basic block ends with a 1-byte-offset jump
118 * and may need expansion */
119 BB_JUMPTABLE = (1 << 3), /* Basic block ends with a jump table */
120 BB_BEGINCATCH = (1 << 4), /* Block ends with a 'beginCatch' instruction,
121 * marking it as the start of a 'catch'
122 * sequence. The 'jumpTarget' is the exception
123 * exit from the catch block. */
124 BB_ENDCATCH = (1 << 5) /* Block ends with an 'endCatch' instruction,
125 * unwinding the catch from the exception
130 * Source instruction type recognized by the assembler.
133 typedef enum TalInstType {
134 ASSEM_1BYTE, /* Fixed arity, 1-byte instruction */
135 ASSEM_BEGIN_CATCH, /* Begin catch: one 4-byte jump offset to be
136 * converted to appropriate exception
138 ASSEM_BOOL, /* One Boolean operand */
139 ASSEM_BOOL_LVT4, /* One Boolean, one 4-byte LVT ref. */
140 ASSEM_CLOCK_READ, /* 1-byte unsigned-integer case number, in the
142 ASSEM_CONCAT1, /* 1-byte unsigned-integer operand count, must
143 * be strictly positive, consumes N, produces
145 ASSEM_DICT_GET, /* 'dict get' and related - consumes N+1
146 * operands, produces 1, N > 0 */
147 ASSEM_DICT_SET, /* specifies key count and LVT index, consumes
148 * N+1 operands, produces 1, N > 0 */
149 ASSEM_DICT_UNSET, /* specifies key count and LVT index, consumes
150 * N operands, produces 1, N > 0 */
151 ASSEM_END_CATCH, /* End catch. No args. Exception range popped
152 * from stack and stack pointer restored. */
153 ASSEM_EVAL, /* 'eval' - evaluate a constant script (by
154 * compiling it in line with the assembly
155 * code! I love Tcl!) */
156 ASSEM_INDEX, /* 4 byte operand, integer or end-integer */
157 ASSEM_INVOKE, /* 1- or 4-byte operand count, must be
158 * strictly positive, consumes N, produces
160 ASSEM_JUMP, /* Jump instructions */
161 ASSEM_JUMP4, /* Jump instructions forcing a 4-byte offset */
162 ASSEM_JUMPTABLE, /* Jumptable (switch -exact) */
163 ASSEM_LABEL, /* The assembly directive that defines a
165 ASSEM_LINDEX_MULTI, /* 4-byte operand count, must be strictly
166 * positive, consumes N, produces 1 */
167 ASSEM_LIST, /* 4-byte operand count, must be nonnegative,
168 * consumses N, produces 1 */
169 ASSEM_LSET_FLAT, /* 4-byte operand count, must be >= 3,
170 * consumes N, produces 1 */
171 ASSEM_LVT, /* One operand that references a local
173 ASSEM_LVT1, /* One 1-byte operand that references a local
175 ASSEM_LVT1_SINT1, /* One 1-byte operand that references a local
176 * variable, one signed-integer 1-byte
178 ASSEM_LVT4, /* One 4-byte operand that references a local
180 ASSEM_OVER, /* OVER: 4-byte operand count, consumes N+1,
182 ASSEM_PUSH, /* one literal operand */
183 ASSEM_REGEXP, /* One Boolean operand, but weird mapping to
185 ASSEM_REVERSE, /* REVERSE: 4-byte operand count, consumes N,
187 ASSEM_SINT1, /* One 1-byte signed-integer operand
189 ASSEM_SINT4_LVT4 /* Signed 4-byte integer operand followed by
190 * LVT entry. Fixed arity */
194 * Description of an instruction recognized by the assembler.
197 typedef struct TalInstDesc {
198 const char *name; /* Name of instruction. */
199 TalInstType instType; /* The type of instruction */
200 int tclInstCode; /* Instruction code. For instructions having
201 * 1- and 4-byte variables, tclInstCode is
202 * ((1byte)<<8) || (4byte) */
203 int operandsConsumed; /* Number of operands consumed by the
204 * operation, or INT_MIN if the operation is
206 int operandsProduced; /* Number of operands produced by the
207 * operation. If negative, the operation has a
208 * net stack effect of -1-operandsProduced */
212 * Structure that holds the state of the assembler while generating code.
215 typedef struct AssemblyEnv {
216 CompileEnv* envPtr; /* Compilation environment being used for code
218 Tcl_Parse* parsePtr; /* Parse of the current line of source */
219 Tcl_HashTable labelHash; /* Hash table whose keys are labels and whose
220 * values are 'label' objects storing the code
221 * offsets of the labels. */
222 int cmdLine; /* Current line number within the assembly
224 int* clNext; /* Invisible continuation line for
226 BasicBlock* head_bb; /* First basic block in the code */
227 BasicBlock* curr_bb; /* Current basic block */
228 int maxDepth; /* Maximum stack depth encountered */
229 int curCatchDepth; /* Current depth of catches */
230 int maxCatchDepth; /* Maximum depth of catches encountered */
231 int flags; /* Compilation flags (TCL_EVAL_DIRECT) */
235 * Static functions defined in this file.
238 static void AddBasicBlockRangeToErrorInfo(AssemblyEnv*,
240 static BasicBlock * AllocBB(AssemblyEnv*);
241 static int AssembleOneLine(AssemblyEnv* envPtr);
242 static void BBAdjustStackDepth(BasicBlock* bbPtr, int consumed,
244 static void BBUpdateStackReqs(BasicBlock* bbPtr, int tblIdx,
246 static void BBEmitInstInt1(AssemblyEnv* assemEnvPtr, int tblIdx,
247 int opnd, int count);
248 static void BBEmitInstInt4(AssemblyEnv* assemEnvPtr, int tblIdx,
249 int opnd, int count);
250 static void BBEmitInst1or4(AssemblyEnv* assemEnvPtr, int tblIdx,
251 int param, int count);
252 static void BBEmitOpcode(AssemblyEnv* assemEnvPtr, int tblIdx,
254 static int BuildExceptionRanges(AssemblyEnv* assemEnvPtr);
255 static int CalculateJumpRelocations(AssemblyEnv*, int*);
256 static int CheckForUnclosedCatches(AssemblyEnv*);
257 static int CheckForThrowInWrongContext(AssemblyEnv*);
258 static int CheckNonThrowingBlock(AssemblyEnv*, BasicBlock*);
259 static int BytecodeMightThrow(unsigned char);
260 static int CheckJumpTableLabels(AssemblyEnv*, BasicBlock*);
261 static int CheckNamespaceQualifiers(Tcl_Interp*, const char*,
263 static int CheckNonNegative(Tcl_Interp*, int);
264 static int CheckOneByte(Tcl_Interp*, int);
265 static int CheckSignedOneByte(Tcl_Interp*, int);
266 static int CheckStack(AssemblyEnv*);
267 static int CheckStrictlyPositive(Tcl_Interp*, int);
268 static ByteCode * CompileAssembleObj(Tcl_Interp *interp,
270 static void CompileEmbeddedScript(AssemblyEnv*, Tcl_Token*,
272 static int DefineLabel(AssemblyEnv* envPtr, const char* label);
273 static void DeleteMirrorJumpTable(JumptableInfo* jtPtr);
274 static void DupAssembleCodeInternalRep(Tcl_Obj* src,
276 static void FillInJumpOffsets(AssemblyEnv*);
277 static int CreateMirrorJumpTable(AssemblyEnv* assemEnvPtr,
279 static int FindLocalVar(AssemblyEnv* envPtr,
280 Tcl_Token** tokenPtrPtr);
281 static int FinishAssembly(AssemblyEnv*);
282 static void FreeAssembleCodeInternalRep(Tcl_Obj *objPtr);
283 static void FreeAssemblyEnv(AssemblyEnv*);
284 static int GetBooleanOperand(AssemblyEnv*, Tcl_Token**, int*);
285 static int GetListIndexOperand(AssemblyEnv*, Tcl_Token**, int*);
286 static int GetIntegerOperand(AssemblyEnv*, Tcl_Token**, int*);
287 static int GetNextOperand(AssemblyEnv*, Tcl_Token**, Tcl_Obj**);
288 static void LookForFreshCatches(BasicBlock*, BasicBlock**);
289 static void MoveCodeForJumps(AssemblyEnv*, int);
290 static void MoveExceptionRangesToBasicBlock(AssemblyEnv*, int);
291 static AssemblyEnv* NewAssemblyEnv(CompileEnv*, int);
292 static int ProcessCatches(AssemblyEnv*);
293 static int ProcessCatchesInBasicBlock(AssemblyEnv*, BasicBlock*,
294 BasicBlock*, enum BasicBlockCatchState, int);
295 static void ResetVisitedBasicBlocks(AssemblyEnv*);
296 static void ResolveJumpTableTargets(AssemblyEnv*, BasicBlock*);
297 static void ReportUndefinedLabel(AssemblyEnv*, BasicBlock*,
299 static void RestoreEmbeddedExceptionRanges(AssemblyEnv*);
300 static int StackCheckBasicBlock(AssemblyEnv*, BasicBlock *,
302 static BasicBlock* StartBasicBlock(AssemblyEnv*, int fallthrough,
304 /* static int AdvanceIp(const unsigned char *pc); */
305 static int StackCheckBasicBlock(AssemblyEnv*, BasicBlock *,
307 static int StackCheckExit(AssemblyEnv*);
308 static void StackFreshCatches(AssemblyEnv*, BasicBlock*, int,
310 static void SyncStackDepth(AssemblyEnv*);
311 static int TclAssembleCode(CompileEnv* envPtr, const char* code,
312 int codeLen, int flags);
313 static void UnstackExpiredCatches(CompileEnv*, BasicBlock*, int,
317 * Tcl_ObjType that describes bytecode emitted by the assembler.
320 static const Tcl_ObjType assembleCodeType = {
322 FreeAssembleCodeInternalRep, /* freeIntRepProc */
323 DupAssembleCodeInternalRep, /* dupIntRepProc */
324 NULL, /* updateStringProc */
325 NULL /* setFromAnyProc */
329 * Source instructions recognized in the Tcl Assembly Language (TAL)
332 static const TalInstDesc TalInstructionTable[] = {
333 /* PUSH must be first, see the code near the end of TclAssembleCode */
334 {"push", ASSEM_PUSH, (INST_PUSH1<<8
335 | INST_PUSH4), 0, 1},
337 {"add", ASSEM_1BYTE, INST_ADD, 2, 1},
338 {"append", ASSEM_LVT, (INST_APPEND_SCALAR1<<8
339 | INST_APPEND_SCALAR4),1, 1},
340 {"appendArray", ASSEM_LVT, (INST_APPEND_ARRAY1<<8
341 | INST_APPEND_ARRAY4), 2, 1},
342 {"appendArrayStk", ASSEM_1BYTE, INST_APPEND_ARRAY_STK, 3, 1},
343 {"appendStk", ASSEM_1BYTE, INST_APPEND_STK, 2, 1},
344 {"arrayExistsImm", ASSEM_LVT4, INST_ARRAY_EXISTS_IMM, 0, 1},
345 {"arrayExistsStk", ASSEM_1BYTE, INST_ARRAY_EXISTS_STK, 1, 1},
346 {"arrayMakeImm", ASSEM_LVT4, INST_ARRAY_MAKE_IMM, 0, 0},
347 {"arrayMakeStk", ASSEM_1BYTE, INST_ARRAY_MAKE_STK, 1, 0},
348 {"beginCatch", ASSEM_BEGIN_CATCH,
349 INST_BEGIN_CATCH4, 0, 0},
350 {"bitand", ASSEM_1BYTE, INST_BITAND, 2, 1},
351 {"bitnot", ASSEM_1BYTE, INST_BITNOT, 1, 1},
352 {"bitor", ASSEM_1BYTE, INST_BITOR, 2, 1},
353 {"bitxor", ASSEM_1BYTE, INST_BITXOR, 2, 1},
354 {"clockRead", ASSEM_CLOCK_READ, INST_CLOCK_READ, 0, 1},
355 {"concat", ASSEM_CONCAT1, INST_STR_CONCAT1, INT_MIN,1},
356 {"concatStk", ASSEM_LIST, INST_CONCAT_STK, INT_MIN,1},
357 {"coroName", ASSEM_1BYTE, INST_COROUTINE_NAME, 0, 1},
358 {"currentNamespace",ASSEM_1BYTE, INST_NS_CURRENT, 0, 1},
359 {"dictAppend", ASSEM_LVT4, INST_DICT_APPEND, 2, 1},
360 {"dictExists", ASSEM_DICT_GET, INST_DICT_EXISTS, INT_MIN,1},
361 {"dictExpand", ASSEM_1BYTE, INST_DICT_EXPAND, 3, 1},
362 {"dictGet", ASSEM_DICT_GET, INST_DICT_GET, INT_MIN,1},
363 {"dictIncrImm", ASSEM_SINT4_LVT4,
364 INST_DICT_INCR_IMM, 1, 1},
365 {"dictLappend", ASSEM_LVT4, INST_DICT_LAPPEND, 2, 1},
366 {"dictRecombineStk",ASSEM_1BYTE, INST_DICT_RECOMBINE_STK,3, 0},
367 {"dictRecombineImm",ASSEM_LVT4, INST_DICT_RECOMBINE_IMM,2, 0},
368 {"dictSet", ASSEM_DICT_SET, INST_DICT_SET, INT_MIN,1},
369 {"dictUnset", ASSEM_DICT_UNSET,
370 INST_DICT_UNSET, INT_MIN,1},
371 {"div", ASSEM_1BYTE, INST_DIV, 2, 1},
372 {"dup", ASSEM_1BYTE, INST_DUP, 1, 2},
373 {"endCatch", ASSEM_END_CATCH,INST_END_CATCH, 0, 0},
374 {"eq", ASSEM_1BYTE, INST_EQ, 2, 1},
375 {"eval", ASSEM_EVAL, INST_EVAL_STK, 1, 1},
376 {"evalStk", ASSEM_1BYTE, INST_EVAL_STK, 1, 1},
377 {"exist", ASSEM_LVT4, INST_EXIST_SCALAR, 0, 1},
378 {"existArray", ASSEM_LVT4, INST_EXIST_ARRAY, 1, 1},
379 {"existArrayStk", ASSEM_1BYTE, INST_EXIST_ARRAY_STK, 2, 1},
380 {"existStk", ASSEM_1BYTE, INST_EXIST_STK, 1, 1},
381 {"expon", ASSEM_1BYTE, INST_EXPON, 2, 1},
382 {"expr", ASSEM_EVAL, INST_EXPR_STK, 1, 1},
383 {"exprStk", ASSEM_1BYTE, INST_EXPR_STK, 1, 1},
384 {"ge", ASSEM_1BYTE, INST_GE, 2, 1},
385 {"gt", ASSEM_1BYTE, INST_GT, 2, 1},
386 {"incr", ASSEM_LVT1, INST_INCR_SCALAR1, 1, 1},
387 {"incrArray", ASSEM_LVT1, INST_INCR_ARRAY1, 2, 1},
388 {"incrArrayImm", ASSEM_LVT1_SINT1,
389 INST_INCR_ARRAY1_IMM, 1, 1},
390 {"incrArrayStk", ASSEM_1BYTE, INST_INCR_ARRAY_STK, 3, 1},
391 {"incrArrayStkImm", ASSEM_SINT1, INST_INCR_ARRAY_STK_IMM,2, 1},
392 {"incrImm", ASSEM_LVT1_SINT1,
393 INST_INCR_SCALAR1_IMM, 0, 1},
394 {"incrStk", ASSEM_1BYTE, INST_INCR_STK, 2, 1},
395 {"incrStkImm", ASSEM_SINT1, INST_INCR_STK_IMM, 1, 1},
396 {"infoLevelArgs", ASSEM_1BYTE, INST_INFO_LEVEL_ARGS, 1, 1},
397 {"infoLevelNumber", ASSEM_1BYTE, INST_INFO_LEVEL_NUM, 0, 1},
398 {"invokeStk", ASSEM_INVOKE, (INST_INVOKE_STK1 << 8
399 | INST_INVOKE_STK4), INT_MIN,1},
400 {"jump", ASSEM_JUMP, INST_JUMP1, 0, 0},
401 {"jump4", ASSEM_JUMP4, INST_JUMP4, 0, 0},
402 {"jumpFalse", ASSEM_JUMP, INST_JUMP_FALSE1, 1, 0},
403 {"jumpFalse4", ASSEM_JUMP4, INST_JUMP_FALSE4, 1, 0},
404 {"jumpTable", ASSEM_JUMPTABLE,INST_JUMP_TABLE, 1, 0},
405 {"jumpTrue", ASSEM_JUMP, INST_JUMP_TRUE1, 1, 0},
406 {"jumpTrue4", ASSEM_JUMP4, INST_JUMP_TRUE4, 1, 0},
407 {"label", ASSEM_LABEL, 0, 0, 0},
408 {"land", ASSEM_1BYTE, INST_LAND, 2, 1},
409 {"lappend", ASSEM_LVT, (INST_LAPPEND_SCALAR1<<8
410 | INST_LAPPEND_SCALAR4),
412 {"lappendArray", ASSEM_LVT, (INST_LAPPEND_ARRAY1<<8
413 | INST_LAPPEND_ARRAY4),2, 1},
414 {"lappendArrayStk", ASSEM_1BYTE, INST_LAPPEND_ARRAY_STK, 3, 1},
415 {"lappendList", ASSEM_LVT4, INST_LAPPEND_LIST, 1, 1},
416 {"lappendListArray",ASSEM_LVT4, INST_LAPPEND_LIST_ARRAY,2, 1},
417 {"lappendListArrayStk", ASSEM_1BYTE,INST_LAPPEND_LIST_ARRAY_STK, 3, 1},
418 {"lappendListStk", ASSEM_1BYTE, INST_LAPPEND_LIST_STK, 2, 1},
419 {"lappendStk", ASSEM_1BYTE, INST_LAPPEND_STK, 2, 1},
420 {"le", ASSEM_1BYTE, INST_LE, 2, 1},
421 {"lindexMulti", ASSEM_LINDEX_MULTI,
422 INST_LIST_INDEX_MULTI, INT_MIN,1},
423 {"list", ASSEM_LIST, INST_LIST, INT_MIN,1},
424 {"listConcat", ASSEM_1BYTE, INST_LIST_CONCAT, 2, 1},
425 {"listIn", ASSEM_1BYTE, INST_LIST_IN, 2, 1},
426 {"listIndex", ASSEM_1BYTE, INST_LIST_INDEX, 2, 1},
427 {"listIndexImm", ASSEM_INDEX, INST_LIST_INDEX_IMM, 1, 1},
428 {"listLength", ASSEM_1BYTE, INST_LIST_LENGTH, 1, 1},
429 {"listNotIn", ASSEM_1BYTE, INST_LIST_NOT_IN, 2, 1},
430 {"load", ASSEM_LVT, (INST_LOAD_SCALAR1 << 8
431 | INST_LOAD_SCALAR4), 0, 1},
432 {"loadArray", ASSEM_LVT, (INST_LOAD_ARRAY1<<8
433 | INST_LOAD_ARRAY4), 1, 1},
434 {"loadArrayStk", ASSEM_1BYTE, INST_LOAD_ARRAY_STK, 2, 1},
435 {"loadStk", ASSEM_1BYTE, INST_LOAD_STK, 1, 1},
436 {"lor", ASSEM_1BYTE, INST_LOR, 2, 1},
437 {"lsetFlat", ASSEM_LSET_FLAT,INST_LSET_FLAT, INT_MIN,1},
438 {"lsetList", ASSEM_1BYTE, INST_LSET_LIST, 3, 1},
439 {"lshift", ASSEM_1BYTE, INST_LSHIFT, 2, 1},
440 {"lt", ASSEM_1BYTE, INST_LT, 2, 1},
441 {"mod", ASSEM_1BYTE, INST_MOD, 2, 1},
442 {"mult", ASSEM_1BYTE, INST_MULT, 2, 1},
443 {"neq", ASSEM_1BYTE, INST_NEQ, 2, 1},
444 {"nop", ASSEM_1BYTE, INST_NOP, 0, 0},
445 {"not", ASSEM_1BYTE, INST_LNOT, 1, 1},
446 {"nsupvar", ASSEM_LVT4, INST_NSUPVAR, 2, 1},
447 {"numericType", ASSEM_1BYTE, INST_NUM_TYPE, 1, 1},
448 {"originCmd", ASSEM_1BYTE, INST_ORIGIN_COMMAND, 1, 1},
449 {"over", ASSEM_OVER, INST_OVER, INT_MIN,-1-1},
450 {"pop", ASSEM_1BYTE, INST_POP, 1, 0},
451 {"pushReturnCode", ASSEM_1BYTE, INST_PUSH_RETURN_CODE, 0, 1},
452 {"pushReturnOpts", ASSEM_1BYTE, INST_PUSH_RETURN_OPTIONS,
454 {"pushResult", ASSEM_1BYTE, INST_PUSH_RESULT, 0, 1},
455 {"regexp", ASSEM_REGEXP, INST_REGEXP, 2, 1},
456 {"resolveCmd", ASSEM_1BYTE, INST_RESOLVE_COMMAND, 1, 1},
457 {"reverse", ASSEM_REVERSE, INST_REVERSE, INT_MIN,-1-0},
458 {"rshift", ASSEM_1BYTE, INST_RSHIFT, 2, 1},
459 {"store", ASSEM_LVT, (INST_STORE_SCALAR1<<8
460 | INST_STORE_SCALAR4), 1, 1},
461 {"storeArray", ASSEM_LVT, (INST_STORE_ARRAY1<<8
462 | INST_STORE_ARRAY4), 2, 1},
463 {"storeArrayStk", ASSEM_1BYTE, INST_STORE_ARRAY_STK, 3, 1},
464 {"storeStk", ASSEM_1BYTE, INST_STORE_STK, 2, 1},
465 {"strcaseLower", ASSEM_1BYTE, INST_STR_LOWER, 1, 1},
466 {"strcaseTitle", ASSEM_1BYTE, INST_STR_TITLE, 1, 1},
467 {"strcaseUpper", ASSEM_1BYTE, INST_STR_UPPER, 1, 1},
468 {"strcmp", ASSEM_1BYTE, INST_STR_CMP, 2, 1},
469 {"strcat", ASSEM_CONCAT1, INST_STR_CONCAT1, INT_MIN,1},
470 {"streq", ASSEM_1BYTE, INST_STR_EQ, 2, 1},
471 {"strfind", ASSEM_1BYTE, INST_STR_FIND, 2, 1},
472 {"strindex", ASSEM_1BYTE, INST_STR_INDEX, 2, 1},
473 {"strlen", ASSEM_1BYTE, INST_STR_LEN, 1, 1},
474 {"strmap", ASSEM_1BYTE, INST_STR_MAP, 3, 1},
475 {"strmatch", ASSEM_BOOL, INST_STR_MATCH, 2, 1},
476 {"strneq", ASSEM_1BYTE, INST_STR_NEQ, 2, 1},
477 {"strrange", ASSEM_1BYTE, INST_STR_RANGE, 3, 1},
478 {"strreplace", ASSEM_1BYTE, INST_STR_REPLACE, 4, 1},
479 {"strrfind", ASSEM_1BYTE, INST_STR_FIND_LAST, 2, 1},
480 {"strtrim", ASSEM_1BYTE, INST_STR_TRIM, 2, 1},
481 {"strtrimLeft", ASSEM_1BYTE, INST_STR_TRIM_LEFT, 2, 1},
482 {"strtrimRight", ASSEM_1BYTE, INST_STR_TRIM_RIGHT, 2, 1},
483 {"sub", ASSEM_1BYTE, INST_SUB, 2, 1},
484 {"tclooClass", ASSEM_1BYTE, INST_TCLOO_CLASS, 1, 1},
485 {"tclooIsObject", ASSEM_1BYTE, INST_TCLOO_IS_OBJECT, 1, 1},
486 {"tclooNamespace", ASSEM_1BYTE, INST_TCLOO_NS, 1, 1},
487 {"tclooSelf", ASSEM_1BYTE, INST_TCLOO_SELF, 0, 1},
488 {"tryCvtToBoolean", ASSEM_1BYTE, INST_TRY_CVT_TO_BOOLEAN,1, 2},
489 {"tryCvtToNumeric", ASSEM_1BYTE, INST_TRY_CVT_TO_NUMERIC,1, 1},
490 {"uminus", ASSEM_1BYTE, INST_UMINUS, 1, 1},
491 {"unset", ASSEM_BOOL_LVT4,INST_UNSET_SCALAR, 0, 0},
492 {"unsetArray", ASSEM_BOOL_LVT4,INST_UNSET_ARRAY, 1, 0},
493 {"unsetArrayStk", ASSEM_BOOL, INST_UNSET_ARRAY_STK, 2, 0},
494 {"unsetStk", ASSEM_BOOL, INST_UNSET_STK, 1, 0},
495 {"uplus", ASSEM_1BYTE, INST_UPLUS, 1, 1},
496 {"upvar", ASSEM_LVT4, INST_UPVAR, 2, 1},
497 {"variable", ASSEM_LVT4, INST_VARIABLE, 1, 0},
498 {"verifyDict", ASSEM_1BYTE, INST_DICT_VERIFY, 1, 0},
499 {"yield", ASSEM_1BYTE, INST_YIELD, 1, 1},
500 {NULL, ASSEM_1BYTE, 0, 0, 0}
504 * List of instructions that cannot throw an exception under any
505 * circumstances. These instructions are the ones that are permissible after
506 * an exception is caught but before the corresponding exception range is
507 * popped from the stack.
508 * The instructions must be in ascending order by numeric operation code.
511 static const unsigned char NonThrowingByteCodes[] = {
512 INST_PUSH1, INST_PUSH4, INST_POP, INST_DUP, /* 1-4 */
513 INST_JUMP1, INST_JUMP4, /* 34-35 */
514 INST_END_CATCH, INST_PUSH_RESULT, INST_PUSH_RETURN_CODE, /* 70-72 */
515 INST_STR_EQ, INST_STR_NEQ, INST_STR_CMP, INST_STR_LEN, /* 73-76 */
518 INST_PUSH_RETURN_OPTIONS, /* 108 */
519 INST_REVERSE, /* 126 */
521 INST_STR_MAP, /* 143 */
522 INST_STR_FIND, /* 144 */
523 INST_COROUTINE_NAME, /* 149 */
524 INST_NS_CURRENT, /* 151 */
525 INST_INFO_LEVEL_NUM, /* 152 */
526 INST_RESOLVE_COMMAND, /* 154 */
527 INST_STR_TRIM, INST_STR_TRIM_LEFT, INST_STR_TRIM_RIGHT, /* 166-168 */
528 INST_CONCAT_STK, /* 169 */
529 INST_STR_UPPER, INST_STR_LOWER, INST_STR_TITLE, /* 170-172 */
530 INST_NUM_TYPE /* 180 */
537 #if defined(TCL_DEBUG_ASSEMBLY) && defined(__GNUC__) && __GNUC__ > 2
538 #define DEBUG_PRINT(...) fprintf(stderr, ##__VA_ARGS__);fflush(stderr)
539 #elif defined(__GNUC__) && __GNUC__ > 2
540 #define DEBUG_PRINT(...) /* nothing */
542 #define DEBUG_PRINT /* nothing */
546 *-----------------------------------------------------------------------------
548 * BBAdjustStackDepth --
550 * When an opcode is emitted, adjusts the stack information in the basic
551 * block to reflect the number of operands produced and consumed.
557 * Updates minimum, maximum and final stack requirements in the basic
560 *-----------------------------------------------------------------------------
565 BasicBlock *bbPtr, /* Structure describing the basic block */
566 int consumed, /* Count of operands consumed by the
568 int produced) /* Count of operands produced by the
571 int depth = bbPtr->finalStackDepth;
574 if (depth < bbPtr->minStackDepth) {
575 bbPtr->minStackDepth = depth;
578 if (depth > bbPtr->maxStackDepth) {
579 bbPtr->maxStackDepth = depth;
581 bbPtr->finalStackDepth = depth;
585 *-----------------------------------------------------------------------------
587 * BBUpdateStackReqs --
589 * Updates the stack requirements of a basic block, given the opcode
590 * being emitted and an operand count.
596 * Updates min, max and final stack requirements in the basic block.
599 * This function must not be called for instructions such as REVERSE and
600 * OVER that are variadic but do not consume all their operands. Instead,
601 * BBAdjustStackDepth should be called directly.
603 * count should be provided only for variadic operations. For operations
604 * with known arity, count should be 0.
606 *-----------------------------------------------------------------------------
611 BasicBlock* bbPtr, /* Structure describing the basic block */
612 int tblIdx, /* Index in TalInstructionTable of the
613 * operation being assembled */
614 int count) /* Count of operands for variadic insts */
616 int consumed = TalInstructionTable[tblIdx].operandsConsumed;
617 int produced = TalInstructionTable[tblIdx].operandsProduced;
619 if (consumed == INT_MIN) {
621 * The instruction is variadic; it consumes 'count' operands.
628 * The instruction leaves some of its variadic operands on the stack,
629 * with net stack effect of '-1-produced'
632 produced = consumed - produced - 1;
634 BBAdjustStackDepth(bbPtr, consumed, produced);
638 *-----------------------------------------------------------------------------
640 * BBEmitOpcode, BBEmitInstInt1, BBEmitInstInt4 --
642 * Emit the opcode part of an instruction, or the entirety of an
643 * instruction with a 1- or 4-byte operand, and adjust stack
650 * Stores instruction and operand in the operand stream, and adjusts the
653 *-----------------------------------------------------------------------------
658 AssemblyEnv* assemEnvPtr, /* Assembly environment */
659 int tblIdx, /* Table index in TalInstructionTable of op */
660 int count) /* Operand count for variadic ops */
662 CompileEnv* envPtr = assemEnvPtr->envPtr;
663 /* Compilation environment */
664 BasicBlock* bbPtr = assemEnvPtr->curr_bb;
665 /* Current basic block */
666 int op = TalInstructionTable[tblIdx].tclInstCode & 0xFF;
669 * If this is the first instruction in a basic block, record its line
673 if (bbPtr->startOffset == envPtr->codeNext - envPtr->codeStart) {
674 bbPtr->startLine = assemEnvPtr->cmdLine;
677 TclEmitInt1(op, envPtr);
678 TclUpdateAtCmdStart(op, envPtr);
679 BBUpdateStackReqs(bbPtr, tblIdx, count);
684 AssemblyEnv* assemEnvPtr, /* Assembly environment */
685 int tblIdx, /* Index in TalInstructionTable of op */
686 int opnd, /* 1-byte operand */
687 int count) /* Operand count for variadic ops */
689 BBEmitOpcode(assemEnvPtr, tblIdx, count);
690 TclEmitInt1(opnd, assemEnvPtr->envPtr);
695 AssemblyEnv* assemEnvPtr, /* Assembly environment */
696 int tblIdx, /* Index in TalInstructionTable of op */
697 int opnd, /* 4-byte operand */
698 int count) /* Operand count for variadic ops */
700 BBEmitOpcode(assemEnvPtr, tblIdx, count);
701 TclEmitInt4(opnd, assemEnvPtr->envPtr);
705 *-----------------------------------------------------------------------------
709 * Emits a 1- or 4-byte operation according to the magnitude of the
712 *-----------------------------------------------------------------------------
717 AssemblyEnv* assemEnvPtr, /* Assembly environment */
718 int tblIdx, /* Index in TalInstructionTable of op */
719 int param, /* Variable-length parameter */
720 int count) /* Arity if variadic */
722 CompileEnv* envPtr = assemEnvPtr->envPtr;
723 /* Compilation environment */
724 BasicBlock* bbPtr = assemEnvPtr->curr_bb;
725 /* Current basic block */
726 int op = TalInstructionTable[tblIdx].tclInstCode;
733 TclEmitInt1(op, envPtr);
735 TclEmitInt1(param, envPtr);
737 TclEmitInt4(param, envPtr);
739 TclUpdateAtCmdStart(op, envPtr);
740 BBUpdateStackReqs(bbPtr, tblIdx, count);
744 *-----------------------------------------------------------------------------
746 * Tcl_AssembleObjCmd, TclNRAssembleObjCmd --
748 * Direct evaluation path for tcl::unsupported::assemble
751 * Returns a standard Tcl result.
754 * Assembles the code in objv[1], and executes it, so side effects
755 * include whatever the code does.
757 *-----------------------------------------------------------------------------
762 ClientData dummy, /* Not used. */
763 Tcl_Interp *interp, /* Current interpreter. */
764 int objc, /* Number of arguments. */
765 Tcl_Obj *const objv[]) /* Argument objects. */
768 * Boilerplate - make sure that there is an NRE trampoline on the C stack
769 * because there needs to be one in place to execute bytecode.
772 return Tcl_NRCallObjProc(interp, TclNRAssembleObjCmd, dummy, objc, objv);
777 ClientData dummy, /* Not used. */
778 Tcl_Interp *interp, /* Current interpreter. */
779 int objc, /* Number of arguments. */
780 Tcl_Obj *const objv[]) /* Argument objects. */
782 ByteCode *codePtr; /* Pointer to the bytecode to execute */
783 Tcl_Obj* backtrace; /* Object where extra error information is
788 Tcl_WrongNumArgs(interp, 1, objv, "bytecodeList");
793 * Assemble the source to bytecode.
796 codePtr = CompileAssembleObj(interp, objv[1]);
799 * On failure, report error line.
802 if (codePtr == NULL) {
803 Tcl_AddErrorInfo(interp, "\n (\"");
804 Tcl_AppendObjToErrorInfo(interp, objv[0]);
805 Tcl_AddErrorInfo(interp, "\" body, line ");
806 TclNewIntObj(backtrace, Tcl_GetErrorLine(interp));
807 Tcl_AppendObjToErrorInfo(interp, backtrace);
808 Tcl_AddErrorInfo(interp, ")");
813 * Use NRE to evaluate the bytecode from the trampoline.
816 return TclNRExecuteByteCode(interp, codePtr);
820 *-----------------------------------------------------------------------------
822 * CompileAssembleObj --
824 * Sets up and assembles Tcl bytecode for the direct-execution path in
825 * the Tcl bytecode assembler.
828 * Returns a pointer to the assembled code. Returns NULL if the assembly
829 * fails for any reason, with an appropriate error message in the
832 *-----------------------------------------------------------------------------
837 Tcl_Interp *interp, /* Tcl interpreter */
838 Tcl_Obj *objPtr) /* Source code to assemble */
840 Interp *iPtr = (Interp *) interp;
841 /* Internals of the interpreter */
842 CompileEnv compEnv; /* Compilation environment structure */
843 ByteCode *codePtr = NULL;
844 /* Bytecode resulting from the assembly */
845 Namespace* namespacePtr; /* Namespace in which variable and command
846 * names in the bytecode resolve */
847 int status; /* Status return from Tcl_AssembleCode */
848 const char* source; /* String representation of the source code */
849 int sourceLen; /* Length of the source code in bytes */
852 * Get the expression ByteCode from the object. If it exists, make sure it
853 * is valid in the current context.
856 if (objPtr->typePtr == &assembleCodeType) {
857 namespacePtr = iPtr->varFramePtr->nsPtr;
858 codePtr = (ByteCode *)objPtr->internalRep.twoPtrValue.ptr1;
859 if (((Interp *) *codePtr->interpHandle == iPtr)
860 && (codePtr->compileEpoch == iPtr->compileEpoch)
861 && (codePtr->nsPtr == namespacePtr)
862 && (codePtr->nsEpoch == namespacePtr->resolverEpoch)
863 && (codePtr->localCachePtr
864 == iPtr->varFramePtr->localCachePtr)) {
869 * Not valid, so free it and regenerate.
872 FreeAssembleCodeInternalRep(objPtr);
876 * Set up the compilation environment, and assemble the code.
879 source = TclGetStringFromObj(objPtr, &sourceLen);
880 TclInitCompileEnv(interp, &compEnv, source, sourceLen, NULL, 0);
881 status = TclAssembleCode(&compEnv, source, sourceLen, TCL_EVAL_DIRECT);
882 if (status != TCL_OK) {
884 * Assembly failed. Clean up and report the error.
886 TclFreeCompileEnv(&compEnv);
891 * Add a "done" instruction as the last instruction and change the object
892 * into a ByteCode object. Ownership of the literal objects and aux data
893 * items is given to the ByteCode object.
896 TclEmitOpcode(INST_DONE, &compEnv);
897 TclInitByteCodeObj(objPtr, &compEnv);
898 objPtr->typePtr = &assembleCodeType;
899 TclFreeCompileEnv(&compEnv);
902 * Record the local variable context to which the bytecode pertains
905 codePtr = (ByteCode *)objPtr->internalRep.twoPtrValue.ptr1;
906 if (iPtr->varFramePtr->localCachePtr) {
907 codePtr->localCachePtr = iPtr->varFramePtr->localCachePtr;
908 codePtr->localCachePtr->refCount++;
912 * Report on what the assembler did.
915 #ifdef TCL_COMPILE_DEBUG
916 if (tclTraceCompile >= 2) {
917 TclPrintByteCodeObj(interp, objPtr);
920 #endif /* TCL_COMPILE_DEBUG */
926 *-----------------------------------------------------------------------------
928 * TclCompileAssembleCmd --
930 * Compilation procedure for the '::tcl::unsupported::assemble' command.
933 * Returns a standard Tcl result.
936 * Puts the result of assembling the code into the bytecode stream in
939 * This procedure makes sure that the command has a single arg, which is
940 * constant. If that condition is met, the procedure calls TclAssembleCode to
941 * produce bytecode for the given assembly code, and returns any error
942 * resulting from the assembly.
944 *-----------------------------------------------------------------------------
948 TclCompileAssembleCmd(
949 Tcl_Interp *interp, /* Used for error reporting. */
950 Tcl_Parse *parsePtr, /* Points to a parse structure for the command
951 * created by Tcl_ParseCommand. */
952 Command *cmdPtr, /* Points to defintion of command being
954 CompileEnv *envPtr) /* Holds resulting instructions. */
956 Tcl_Token *tokenPtr; /* Token in the input script */
958 int numCommands = envPtr->numCommands;
959 int offset = envPtr->codeNext - envPtr->codeStart;
960 int depth = envPtr->currStackDepth;
963 * Make sure that the command has a single arg that is a simple word.
966 if (parsePtr->numWords != 2) {
969 tokenPtr = TokenAfter(parsePtr->tokenPtr);
970 if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
975 * Compile the code and convert any error from the compilation into
976 * bytecode reporting the error;
979 if (TCL_ERROR == TclAssembleCode(envPtr, tokenPtr[1].start,
980 tokenPtr[1].size, TCL_EVAL_DIRECT)) {
982 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
983 "\n (\"%.*s\" body, line %d)",
984 parsePtr->tokenPtr->size, parsePtr->tokenPtr->start,
985 Tcl_GetErrorLine(interp)));
986 envPtr->numCommands = numCommands;
987 envPtr->codeNext = envPtr->codeStart + offset;
988 envPtr->currStackDepth = depth;
989 TclCompileSyntaxError(interp, envPtr);
995 *-----------------------------------------------------------------------------
999 * Take a list of instructions in a Tcl_Obj, and assemble them to Tcl
1003 * Returns TCL_OK on success, TCL_ERROR on failure. If 'flags' includes
1004 * TCL_EVAL_DIRECT, places an error message in the interpreter result.
1007 * Adds byte codes to the compile environment, and updates the
1008 * environment's stack depth.
1010 *-----------------------------------------------------------------------------
1015 CompileEnv *envPtr, /* Compilation environment that is to receive
1016 * the generated bytecode */
1017 const char* codePtr, /* Assembly-language code to be processed */
1018 int codeLen, /* Length of the code */
1019 int flags) /* OR'ed combination of flags */
1021 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
1022 /* Tcl interpreter */
1024 * Walk through the assembly script using the Tcl parser. Each 'command'
1025 * will be an instruction or assembly directive.
1028 const char* instPtr = codePtr;
1029 /* Where to start looking for a line of code */
1030 const char* nextPtr; /* Pointer to the end of the line of code */
1031 int bytesLeft = codeLen; /* Number of bytes of source code remaining to
1033 int status; /* Tcl status return */
1034 AssemblyEnv* assemEnvPtr = NewAssemblyEnv(envPtr, flags);
1035 Tcl_Parse* parsePtr = assemEnvPtr->parsePtr;
1039 * Parse out one command line from the assembly script.
1042 status = Tcl_ParseCommand(interp, instPtr, bytesLeft, 0, parsePtr);
1045 * Report errors in the parse.
1048 if (status != TCL_OK) {
1049 if (flags & TCL_EVAL_DIRECT) {
1050 Tcl_LogCommandInfo(interp, codePtr, parsePtr->commandStart,
1051 parsePtr->term + 1 - parsePtr->commandStart);
1053 FreeAssemblyEnv(assemEnvPtr);
1058 * Advance the pointers around any leading commentary.
1061 TclAdvanceLines(&assemEnvPtr->cmdLine, instPtr,
1062 parsePtr->commandStart);
1063 TclAdvanceContinuations(&assemEnvPtr->cmdLine, &assemEnvPtr->clNext,
1064 parsePtr->commandStart - envPtr->source);
1067 * Process the line of code.
1070 if (parsePtr->numWords > 0) {
1071 int instLen = parsePtr->commandSize;
1072 /* Length in bytes of the current command */
1074 if (parsePtr->term == parsePtr->commandStart + instLen - 1) {
1079 * If tracing, show each line assembled as it happens.
1082 #ifdef TCL_COMPILE_DEBUG
1083 if ((tclTraceCompile >= 2) && (envPtr->procPtr == NULL)) {
1084 printf(" %4ld Assembling: ",
1085 (long)(envPtr->codeNext - envPtr->codeStart));
1086 TclPrintSource(stdout, parsePtr->commandStart,
1087 TclMin(instLen, 55));
1091 if (AssembleOneLine(assemEnvPtr) != TCL_OK) {
1092 if (flags & TCL_EVAL_DIRECT) {
1093 Tcl_LogCommandInfo(interp, codePtr,
1094 parsePtr->commandStart, instLen);
1096 Tcl_FreeParse(parsePtr);
1097 FreeAssemblyEnv(assemEnvPtr);
1103 * Advance to the next line of code.
1106 nextPtr = parsePtr->commandStart + parsePtr->commandSize;
1107 bytesLeft -= (nextPtr - instPtr);
1109 TclAdvanceLines(&assemEnvPtr->cmdLine, parsePtr->commandStart,
1111 TclAdvanceContinuations(&assemEnvPtr->cmdLine, &assemEnvPtr->clNext,
1112 instPtr - envPtr->source);
1113 Tcl_FreeParse(parsePtr);
1114 } while (bytesLeft > 0);
1117 * Done with parsing the code.
1120 status = FinishAssembly(assemEnvPtr);
1121 FreeAssemblyEnv(assemEnvPtr);
1126 *-----------------------------------------------------------------------------
1130 * Creates an environment for the assembler to run in.
1133 * Allocates, initialises and returns an assembler environment
1135 *-----------------------------------------------------------------------------
1140 CompileEnv* envPtr, /* Compilation environment being used for code
1142 int flags) /* Compilation flags (TCL_EVAL_DIRECT) */
1144 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
1145 /* Tcl interpreter */
1146 AssemblyEnv* assemEnvPtr = (AssemblyEnv*)TclStackAlloc(interp, sizeof(AssemblyEnv));
1147 /* Assembler environment under construction */
1148 Tcl_Parse* parsePtr = (Tcl_Parse*)TclStackAlloc(interp, sizeof(Tcl_Parse));
1149 /* Parse of one line of assembly code */
1151 assemEnvPtr->envPtr = envPtr;
1152 assemEnvPtr->parsePtr = parsePtr;
1153 assemEnvPtr->cmdLine = 1;
1154 assemEnvPtr->clNext = envPtr->clNext;
1157 * Make the hashtables that store symbol resolution.
1160 Tcl_InitHashTable(&assemEnvPtr->labelHash, TCL_STRING_KEYS);
1163 * Start the first basic block.
1166 assemEnvPtr->curr_bb = NULL;
1167 assemEnvPtr->head_bb = AllocBB(assemEnvPtr);
1168 assemEnvPtr->curr_bb = assemEnvPtr->head_bb;
1169 assemEnvPtr->head_bb->startLine = 1;
1172 * Stash compilation flags.
1175 assemEnvPtr->flags = flags;
1180 *-----------------------------------------------------------------------------
1182 * FreeAssemblyEnv --
1184 * Cleans up the assembler environment when assembly is complete.
1186 *-----------------------------------------------------------------------------
1191 AssemblyEnv* assemEnvPtr) /* Environment to free */
1193 CompileEnv* envPtr = assemEnvPtr->envPtr;
1194 /* Compilation environment being used for code
1196 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
1197 /* Tcl interpreter */
1198 BasicBlock* thisBB; /* Pointer to a basic block being deleted */
1199 BasicBlock* nextBB; /* Pointer to a deleted basic block's
1203 * Free all the basic block structures.
1206 for (thisBB = assemEnvPtr->head_bb; thisBB != NULL; thisBB = nextBB) {
1207 if (thisBB->jumpTarget != NULL) {
1208 Tcl_DecrRefCount(thisBB->jumpTarget);
1210 if (thisBB->foreignExceptions != NULL) {
1211 ckfree(thisBB->foreignExceptions);
1213 nextBB = thisBB->successor1;
1214 if (thisBB->jtPtr != NULL) {
1215 DeleteMirrorJumpTable(thisBB->jtPtr);
1216 thisBB->jtPtr = NULL;
1222 * Dispose what's left.
1225 Tcl_DeleteHashTable(&assemEnvPtr->labelHash);
1226 TclStackFree(interp, assemEnvPtr->parsePtr);
1227 TclStackFree(interp, assemEnvPtr);
1231 *-----------------------------------------------------------------------------
1233 * AssembleOneLine --
1235 * Assembles a single command from an assembly language source.
1238 * Returns TCL_ERROR with an appropriate error message if the assembly
1239 * fails. Returns TCL_OK if the assembly succeeds. Updates the assembly
1240 * environment with the state of the assembly.
1242 *-----------------------------------------------------------------------------
1247 AssemblyEnv* assemEnvPtr) /* State of the assembly */
1249 CompileEnv* envPtr = assemEnvPtr->envPtr;
1250 /* Compilation environment being used for code
1252 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
1253 /* Tcl interpreter */
1254 Tcl_Parse* parsePtr = assemEnvPtr->parsePtr;
1255 /* Parse of the line of code */
1256 Tcl_Token* tokenPtr; /* Current token within the line of code */
1257 Tcl_Obj* instNameObj; /* Name of the instruction */
1258 int tblIdx; /* Index in TalInstructionTable of the
1260 enum TalInstType instType; /* Type of the instruction */
1261 Tcl_Obj* operand1Obj = NULL;
1262 /* First operand to the instruction */
1263 const char* operand1; /* String rep of the operand */
1264 int operand1Len; /* String length of the operand */
1265 int opnd; /* Integer representation of an operand */
1266 int litIndex; /* Literal pool index of a constant */
1267 int localVar; /* LVT index of a local variable */
1268 int flags; /* Flags for a basic block */
1269 JumptableInfo* jtPtr; /* Pointer to a jumptable */
1270 int infoIndex; /* Index of the jumptable in auxdata */
1271 int status = TCL_ERROR; /* Return value from this function */
1274 * Make sure that the instruction name is known at compile time.
1277 tokenPtr = parsePtr->tokenPtr;
1278 if (GetNextOperand(assemEnvPtr, &tokenPtr, &instNameObj) != TCL_OK) {
1283 * Look up the instruction name.
1286 if (Tcl_GetIndexFromObjStruct(interp, instNameObj,
1287 &TalInstructionTable[0].name, sizeof(TalInstDesc), "instruction",
1288 TCL_EXACT, &tblIdx) != TCL_OK) {
1293 * Vector on the type of instruction being processed.
1296 instType = TalInstructionTable[tblIdx].instType;
1300 if (parsePtr->numWords != 2) {
1301 Tcl_WrongNumArgs(interp, 1, &instNameObj, "value");
1304 if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
1307 operand1 = Tcl_GetStringFromObj(operand1Obj, &operand1Len);
1308 litIndex = TclRegisterNewLiteral(envPtr, operand1, operand1Len);
1309 BBEmitInst1or4(assemEnvPtr, tblIdx, litIndex, 0);
1313 if (parsePtr->numWords != 1) {
1314 Tcl_WrongNumArgs(interp, 1, &instNameObj, "");
1317 BBEmitOpcode(assemEnvPtr, tblIdx, 0);
1320 case ASSEM_BEGIN_CATCH:
1322 * Emit the BEGIN_CATCH instruction with the code offset of the
1323 * exception branch target instead of the exception range index. The
1324 * correct index will be generated and inserted later, when catches
1325 * are being resolved.
1328 if (parsePtr->numWords != 2) {
1329 Tcl_WrongNumArgs(interp, 1, &instNameObj, "label");
1332 if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
1335 assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
1336 assemEnvPtr->curr_bb->jumpOffset = envPtr->codeNext-envPtr->codeStart;
1337 BBEmitInstInt4(assemEnvPtr, tblIdx, 0, 0);
1338 assemEnvPtr->curr_bb->flags |= BB_BEGINCATCH;
1339 StartBasicBlock(assemEnvPtr, BB_FALLTHRU, operand1Obj);
1343 if (parsePtr->numWords != 2) {
1344 Tcl_WrongNumArgs(interp, 1, &instNameObj, "boolean");
1347 if (GetBooleanOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
1350 BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, 0);
1353 case ASSEM_BOOL_LVT4:
1354 if (parsePtr->numWords != 3) {
1355 Tcl_WrongNumArgs(interp, 1, &instNameObj, "boolean varName");
1358 if (GetBooleanOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
1361 localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
1365 BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, 0);
1366 TclEmitInt4(localVar, envPtr);
1369 case ASSEM_CLOCK_READ:
1370 if (parsePtr->numWords != 2) {
1371 Tcl_WrongNumArgs(interp, 1, &instNameObj, "imm8");
1374 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
1377 if (opnd < 0 || opnd > 3) {
1378 Tcl_SetObjResult(interp,
1379 Tcl_NewStringObj("operand must be [0..3]", -1));
1380 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "OPERAND<0,>3", NULL);
1383 BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, opnd);
1387 if (parsePtr->numWords != 2) {
1388 Tcl_WrongNumArgs(interp, 1, &instNameObj, "imm8");
1391 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1392 || CheckOneByte(interp, opnd) != TCL_OK
1393 || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
1396 BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, opnd);
1399 case ASSEM_DICT_GET:
1400 if (parsePtr->numWords != 2) {
1401 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
1404 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1405 || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
1408 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd+1);
1411 case ASSEM_DICT_SET:
1412 if (parsePtr->numWords != 3) {
1413 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count varName");
1416 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1417 || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
1420 localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
1424 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd+1);
1425 TclEmitInt4(localVar, envPtr);
1428 case ASSEM_DICT_UNSET:
1429 if (parsePtr->numWords != 3) {
1430 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count varName");
1433 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1434 || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
1437 localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
1441 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
1442 TclEmitInt4(localVar, envPtr);
1445 case ASSEM_END_CATCH:
1446 if (parsePtr->numWords != 1) {
1447 Tcl_WrongNumArgs(interp, 1, &instNameObj, "");
1450 assemEnvPtr->curr_bb->flags |= BB_ENDCATCH;
1451 BBEmitOpcode(assemEnvPtr, tblIdx, 0);
1452 StartBasicBlock(assemEnvPtr, BB_FALLTHRU, NULL);
1456 /* TODO - Refactor this stuff into a subroutine that takes the inst
1457 * code, the message ("script" or "expression") and an evaluator
1458 * callback that calls TclCompileScript or TclCompileExpr. */
1460 if (parsePtr->numWords != 2) {
1461 Tcl_WrongNumArgs(interp, 1, &instNameObj,
1462 ((TalInstructionTable[tblIdx].tclInstCode
1463 == INST_EVAL_STK) ? "script" : "expression"));
1466 if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
1467 CompileEmbeddedScript(assemEnvPtr, tokenPtr+1,
1468 TalInstructionTable+tblIdx);
1469 } else if (GetNextOperand(assemEnvPtr, &tokenPtr,
1470 &operand1Obj) != TCL_OK) {
1473 operand1 = Tcl_GetStringFromObj(operand1Obj, &operand1Len);
1474 litIndex = TclRegisterNewLiteral(envPtr, operand1, operand1Len);
1477 * Assumes that PUSH is the first slot!
1480 BBEmitInst1or4(assemEnvPtr, 0, litIndex, 0);
1481 BBEmitOpcode(assemEnvPtr, tblIdx, 0);
1486 if (parsePtr->numWords != 2) {
1487 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
1490 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1491 || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
1495 BBEmitInst1or4(assemEnvPtr, tblIdx, opnd, opnd);
1500 if (parsePtr->numWords != 2) {
1501 Tcl_WrongNumArgs(interp, 1, &instNameObj, "label");
1504 if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
1507 assemEnvPtr->curr_bb->jumpOffset = envPtr->codeNext-envPtr->codeStart;
1508 if (instType == ASSEM_JUMP) {
1510 BBEmitInstInt1(assemEnvPtr, tblIdx, 0, 0);
1513 BBEmitInstInt4(assemEnvPtr, tblIdx, 0, 0);
1517 * Start a new basic block at the instruction following the jump.
1520 assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
1521 if (TalInstructionTable[tblIdx].operandsConsumed != 0) {
1522 flags |= BB_FALLTHRU;
1524 StartBasicBlock(assemEnvPtr, flags, operand1Obj);
1527 case ASSEM_JUMPTABLE:
1528 if (parsePtr->numWords != 2) {
1529 Tcl_WrongNumArgs(interp, 1, &instNameObj, "table");
1532 if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
1536 jtPtr = (JumptableInfo*)ckalloc(sizeof(JumptableInfo));
1538 Tcl_InitHashTable(&jtPtr->hashTable, TCL_STRING_KEYS);
1539 assemEnvPtr->curr_bb->jumpLine = assemEnvPtr->cmdLine;
1540 assemEnvPtr->curr_bb->jumpOffset = envPtr->codeNext-envPtr->codeStart;
1541 DEBUG_PRINT("bb %p jumpLine %d jumpOffset %d\n",
1542 assemEnvPtr->curr_bb, assemEnvPtr->cmdLine,
1543 envPtr->codeNext - envPtr->codeStart);
1545 infoIndex = TclCreateAuxData(jtPtr, &tclJumptableInfoType, envPtr);
1546 DEBUG_PRINT("auxdata index=%d\n", infoIndex);
1548 BBEmitInstInt4(assemEnvPtr, tblIdx, infoIndex, 0);
1549 if (CreateMirrorJumpTable(assemEnvPtr, operand1Obj) != TCL_OK) {
1552 StartBasicBlock(assemEnvPtr, BB_JUMPTABLE|BB_FALLTHRU, NULL);
1556 if (parsePtr->numWords != 2) {
1557 Tcl_WrongNumArgs(interp, 1, &instNameObj, "name");
1560 if (GetNextOperand(assemEnvPtr, &tokenPtr, &operand1Obj) != TCL_OK) {
1565 * Add the (label_name, address) pair to the hash table.
1568 if (DefineLabel(assemEnvPtr, Tcl_GetString(operand1Obj)) != TCL_OK) {
1573 case ASSEM_LINDEX_MULTI:
1574 if (parsePtr->numWords != 2) {
1575 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
1578 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1579 || CheckStrictlyPositive(interp, opnd) != TCL_OK) {
1582 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
1586 if (parsePtr->numWords != 2) {
1587 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
1590 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1591 || CheckNonNegative(interp, opnd) != TCL_OK) {
1594 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
1598 if (parsePtr->numWords != 2) {
1599 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
1602 if (GetListIndexOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
1605 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
1608 case ASSEM_LSET_FLAT:
1609 if (parsePtr->numWords != 2) {
1610 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
1613 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
1617 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
1618 Tcl_SetObjResult(interp,
1619 Tcl_NewStringObj("operand must be >=2", -1));
1620 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "OPERAND>=2", NULL);
1624 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
1628 if (parsePtr->numWords != 2) {
1629 Tcl_WrongNumArgs(interp, 1, &instNameObj, "varname");
1632 localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
1636 BBEmitInst1or4(assemEnvPtr, tblIdx, localVar, 0);
1640 if (parsePtr->numWords != 2) {
1641 Tcl_WrongNumArgs(interp, 1, &instNameObj, "varname");
1644 localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
1645 if (localVar < 0 || CheckOneByte(interp, localVar)) {
1648 BBEmitInstInt1(assemEnvPtr, tblIdx, localVar, 0);
1651 case ASSEM_LVT1_SINT1:
1652 if (parsePtr->numWords != 3) {
1653 Tcl_WrongNumArgs(interp, 1, &instNameObj, "varName imm8");
1656 localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
1657 if (localVar < 0 || CheckOneByte(interp, localVar)
1658 || GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1659 || CheckSignedOneByte(interp, opnd)) {
1662 BBEmitInstInt1(assemEnvPtr, tblIdx, localVar, 0);
1663 TclEmitInt1(opnd, envPtr);
1667 if (parsePtr->numWords != 2) {
1668 Tcl_WrongNumArgs(interp, 1, &instNameObj, "varname");
1671 localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
1675 BBEmitInstInt4(assemEnvPtr, tblIdx, localVar, 0);
1679 if (parsePtr->numWords != 2) {
1680 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
1683 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1684 || CheckNonNegative(interp, opnd) != TCL_OK) {
1687 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd+1);
1691 if (parsePtr->numWords != 2) {
1692 Tcl_WrongNumArgs(interp, 1, &instNameObj, "boolean");
1695 if (GetBooleanOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
1699 BBEmitInstInt1(assemEnvPtr, tblIdx, TCL_REG_ADVANCED | (opnd ? TCL_REG_NOCASE : 0), 0);
1704 if (parsePtr->numWords != 2) {
1705 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count");
1708 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1709 || CheckNonNegative(interp, opnd) != TCL_OK) {
1712 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, opnd);
1716 if (parsePtr->numWords != 2) {
1717 Tcl_WrongNumArgs(interp, 1, &instNameObj, "imm8");
1720 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK
1721 || CheckSignedOneByte(interp, opnd) != TCL_OK) {
1724 BBEmitInstInt1(assemEnvPtr, tblIdx, opnd, 0);
1727 case ASSEM_SINT4_LVT4:
1728 if (parsePtr->numWords != 3) {
1729 Tcl_WrongNumArgs(interp, 1, &instNameObj, "count varName");
1732 if (GetIntegerOperand(assemEnvPtr, &tokenPtr, &opnd) != TCL_OK) {
1735 localVar = FindLocalVar(assemEnvPtr, &tokenPtr);
1739 BBEmitInstInt4(assemEnvPtr, tblIdx, opnd, 0);
1740 TclEmitInt4(localVar, envPtr);
1744 Tcl_Panic("Instruction \"%s\" could not be found, can't happen\n",
1745 Tcl_GetString(instNameObj));
1750 Tcl_DecrRefCount(instNameObj);
1752 Tcl_DecrRefCount(operand1Obj);
1758 *-----------------------------------------------------------------------------
1760 * CompileEmbeddedScript --
1762 * Compile an embedded 'eval' or 'expr' that appears in assembly code.
1764 * This procedure is called when the 'eval' or 'expr' assembly directive is
1765 * encountered, and the argument to the directive is a simple word that
1766 * requires no substitution. The appropriate compiler (TclCompileScript or
1767 * TclCompileExpr) is invoked recursively, and emits bytecode.
1769 * Before the compiler is invoked, the compilation environment's stack
1770 * consumption is reset to zero. Upon return from the compilation, the net
1771 * stack effect of the compilation is in the compiler env, and this stack
1772 * effect is posted to the assembler environment. The compile environment's
1773 * stack consumption is then restored to what it was before (which is actually
1774 * the state of the stack on entry to the block of assembly code).
1776 * Any exception ranges pushed by the compilation are copied to the basic
1777 * block and removed from the compiler environment. They will be rebuilt at
1778 * the end of assembly, when the exception stack depth is actually known.
1780 *-----------------------------------------------------------------------------
1784 CompileEmbeddedScript(
1785 AssemblyEnv* assemEnvPtr, /* Assembly environment */
1786 Tcl_Token* tokenPtr, /* Tcl_Token containing the script */
1787 const TalInstDesc* instPtr) /* Instruction that determines whether
1788 * the script is 'expr' or 'eval' */
1790 CompileEnv* envPtr = assemEnvPtr->envPtr;
1791 /* Compilation environment */
1792 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
1793 /* Tcl interpreter */
1796 * The expression or script is not only known at compile time, but
1797 * actually a "simple word". It can be compiled inline by invoking the
1798 * compiler recursively.
1800 * Save away the stack depth and reset it before compiling the script.
1801 * We'll record the stack usage of the script in the BasicBlock, and
1802 * accumulate it together with the stack usage of the enclosing assembly
1806 int savedStackDepth = envPtr->currStackDepth;
1807 int savedMaxStackDepth = envPtr->maxStackDepth;
1808 int savedExceptArrayNext = envPtr->exceptArrayNext;
1810 envPtr->currStackDepth = 0;
1811 envPtr->maxStackDepth = 0;
1813 StartBasicBlock(assemEnvPtr, BB_FALLTHRU, NULL);
1814 switch(instPtr->tclInstCode) {
1816 TclCompileScript(interp, tokenPtr->start, tokenPtr->size, envPtr);
1819 TclCompileExpr(interp, tokenPtr->start, tokenPtr->size, envPtr, 1);
1822 Tcl_Panic("no ASSEM_EVAL case for %s (%d), can't happen",
1823 instPtr->name, instPtr->tclInstCode);
1827 * Roll up the stack usage of the embedded block into the assembler
1831 SyncStackDepth(assemEnvPtr);
1832 envPtr->currStackDepth = savedStackDepth;
1833 envPtr->maxStackDepth = savedMaxStackDepth;
1836 * Save any exception ranges that were pushed by the compiler; they will
1837 * need to be fixed up once the stack depth is known.
1840 MoveExceptionRangesToBasicBlock(assemEnvPtr, savedExceptArrayNext);
1843 * Flush the current basic block.
1846 StartBasicBlock(assemEnvPtr, BB_FALLTHRU, NULL);
1850 *-----------------------------------------------------------------------------
1854 * Copies the stack depth from the compile environment to a basic block.
1857 * Current and max stack depth in the current basic block are adjusted.
1859 * This procedure is called on return from invoking the compiler for the
1860 * 'eval' and 'expr' operations. It adjusts the stack depth of the current
1861 * basic block to reflect the stack required by the just-compiled code.
1863 *-----------------------------------------------------------------------------
1868 AssemblyEnv* assemEnvPtr) /* Assembly environment */
1870 CompileEnv* envPtr = assemEnvPtr->envPtr;
1871 /* Compilation environment */
1872 BasicBlock* curr_bb = assemEnvPtr->curr_bb;
1873 /* Current basic block */
1874 int maxStackDepth = curr_bb->finalStackDepth + envPtr->maxStackDepth;
1875 /* Max stack depth in the basic block */
1877 if (maxStackDepth > curr_bb->maxStackDepth) {
1878 curr_bb->maxStackDepth = maxStackDepth;
1880 curr_bb->finalStackDepth += envPtr->currStackDepth;
1884 *-----------------------------------------------------------------------------
1886 * MoveExceptionRangesToBasicBlock --
1888 * Removes exception ranges that were created by compiling an embedded
1889 * script from the CompileEnv, and stores them in the BasicBlock. They
1890 * will be reinstalled, at the correct stack depth, after control flow
1891 * analysis is complete on the assembly code.
1893 *-----------------------------------------------------------------------------
1897 MoveExceptionRangesToBasicBlock(
1898 AssemblyEnv* assemEnvPtr, /* Assembly environment */
1899 int savedExceptArrayNext) /* Saved index of the end of the exception
1902 CompileEnv* envPtr = assemEnvPtr->envPtr;
1903 /* Compilation environment */
1904 BasicBlock* curr_bb = assemEnvPtr->curr_bb;
1905 /* Current basic block */
1906 int exceptionCount = envPtr->exceptArrayNext - savedExceptArrayNext;
1907 /* Number of ranges that must be moved */
1910 if (exceptionCount == 0) {
1916 * Save the exception ranges in the basic block. They will be re-added at
1917 * the conclusion of assembly; at this time, the INST_BEGIN_CATCH
1918 * instructions in the block will be adjusted from whatever range indices
1919 * they have [savedExceptArrayNext .. envPtr->exceptArrayNext) to the
1920 * indices that the exceptions acquire. The saved exception ranges are
1921 * converted to a relative nesting depth. The depth will be recomputed
1922 * once flow analysis has determined the actual stack depth of the block.
1925 DEBUG_PRINT("basic block %p has %d exceptions starting at %d\n",
1926 curr_bb, exceptionCount, savedExceptArrayNext);
1927 curr_bb->foreignExceptionBase = savedExceptArrayNext;
1928 curr_bb->foreignExceptionCount = exceptionCount;
1929 curr_bb->foreignExceptions =
1930 (ExceptionRange*)ckalloc(exceptionCount * sizeof(ExceptionRange));
1931 memcpy(curr_bb->foreignExceptions,
1932 envPtr->exceptArrayPtr + savedExceptArrayNext,
1933 exceptionCount * sizeof(ExceptionRange));
1934 for (i = 0; i < exceptionCount; ++i) {
1935 curr_bb->foreignExceptions[i].nestingLevel -= envPtr->exceptDepth;
1937 envPtr->exceptArrayNext = savedExceptArrayNext;
1941 *-----------------------------------------------------------------------------
1943 * CreateMirrorJumpTable --
1945 * Makes a jump table with comparison values and assembly code labels.
1948 * Returns a standard Tcl status, with an error message in the
1949 * interpreter on error.
1952 * Initializes the jump table pointer in the current basic block to a
1953 * JumptableInfo. The keys in the JumptableInfo are the comparison
1954 * strings. The values, instead of being jump displacements, are
1955 * Tcl_Obj's with the code labels.
1959 CreateMirrorJumpTable(
1960 AssemblyEnv* assemEnvPtr, /* Assembly environment */
1961 Tcl_Obj* jumps) /* List of alternating keywords and labels */
1963 int objc; /* Number of elements in the 'jumps' list */
1964 Tcl_Obj** objv; /* Pointers to the elements in the list */
1965 CompileEnv* envPtr = assemEnvPtr->envPtr;
1966 /* Compilation environment */
1967 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
1968 /* Tcl interpreter */
1969 BasicBlock* bbPtr = assemEnvPtr->curr_bb;
1970 /* Current basic block */
1971 JumptableInfo* jtPtr;
1972 Tcl_HashTable* jtHashPtr; /* Hashtable in the JumptableInfo */
1973 Tcl_HashEntry* hashEntry; /* Entry for a key in the hashtable */
1974 int isNew; /* Flag==1 if the key is not yet in the
1978 if (Tcl_ListObjGetElements(interp, jumps, &objc, &objv) != TCL_OK) {
1981 if (objc % 2 != 0) {
1982 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
1983 Tcl_SetObjResult(interp, Tcl_NewStringObj(
1984 "jump table must have an even number of list elements",
1986 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADJUMPTABLE", NULL);
1992 * Allocate the jumptable.
1995 jtPtr = (JumptableInfo*)ckalloc(sizeof(JumptableInfo));
1996 jtHashPtr = &jtPtr->hashTable;
1997 Tcl_InitHashTable(jtHashPtr, TCL_STRING_KEYS);
2000 * Fill the keys and labels into the table.
2003 DEBUG_PRINT("jump table {\n");
2004 for (i = 0; i < objc; i+=2) {
2005 DEBUG_PRINT(" %s -> %s\n", Tcl_GetString(objv[i]),
2006 Tcl_GetString(objv[i+1]));
2007 hashEntry = Tcl_CreateHashEntry(jtHashPtr, Tcl_GetString(objv[i]),
2010 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
2011 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2012 "duplicate entry in jump table for \"%s\"",
2013 Tcl_GetString(objv[i])));
2014 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "DUPJUMPTABLEENTRY");
2015 DeleteMirrorJumpTable(jtPtr);
2019 Tcl_SetHashValue(hashEntry, objv[i+1]);
2020 Tcl_IncrRefCount(objv[i+1]);
2025 * Put the mirror jumptable in the basic block struct.
2028 bbPtr->jtPtr = jtPtr;
2033 *-----------------------------------------------------------------------------
2035 * DeleteMirrorJumpTable --
2037 * Cleans up a jump table when the basic block is deleted.
2039 *-----------------------------------------------------------------------------
2043 DeleteMirrorJumpTable(
2044 JumptableInfo* jtPtr)
2046 Tcl_HashTable* jtHashPtr = &jtPtr->hashTable;
2047 /* Hash table pointer */
2048 Tcl_HashSearch search; /* Hash search control */
2049 Tcl_HashEntry* entry; /* Hash table entry containing a jump label */
2050 Tcl_Obj* label; /* Jump label from the hash table */
2052 for (entry = Tcl_FirstHashEntry(jtHashPtr, &search);
2054 entry = Tcl_NextHashEntry(&search)) {
2055 label = (Tcl_Obj*)Tcl_GetHashValue(entry);
2056 Tcl_DecrRefCount(label);
2057 Tcl_SetHashValue(entry, NULL);
2059 Tcl_DeleteHashTable(jtHashPtr);
2064 *-----------------------------------------------------------------------------
2068 * Retrieves the next operand in sequence from an assembly instruction,
2069 * and makes sure that its value is known at compile time.
2072 * If successful, returns TCL_OK and leaves a Tcl_Obj with the operand
2073 * text in *operandObjPtr. In case of failure, returns TCL_ERROR and
2074 * leaves *operandObjPtr untouched.
2077 * Advances *tokenPtrPtr around the token just processed.
2079 *-----------------------------------------------------------------------------
2084 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2085 Tcl_Token** tokenPtrPtr, /* INPUT/OUTPUT: Pointer to the token holding
2087 Tcl_Obj** operandObjPtr) /* OUTPUT: Tcl object holding the operand text
2088 * with \-substitutions done. */
2090 Tcl_Interp* interp = (Tcl_Interp*) assemEnvPtr->envPtr->iPtr;
2091 Tcl_Obj* operandObj;
2093 TclNewObj(operandObj);
2094 if (!TclWordKnownAtCompileTime(*tokenPtrPtr, operandObj)) {
2095 Tcl_DecrRefCount(operandObj);
2096 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
2097 Tcl_SetObjResult(interp, Tcl_NewStringObj(
2098 "assembly code may not contain substitutions", -1));
2099 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "NOSUBST", NULL);
2103 *tokenPtrPtr = TokenAfter(*tokenPtrPtr);
2104 Tcl_IncrRefCount(operandObj);
2105 *operandObjPtr = operandObj;
2110 *-----------------------------------------------------------------------------
2112 * GetBooleanOperand --
2114 * Retrieves a Boolean operand from the input stream and advances
2115 * the token pointer.
2118 * Returns a standard Tcl result (with an error message in the
2119 * interpreter on failure).
2122 * Stores the Boolean value in (*result) and advances (*tokenPtrPtr)
2123 * to the next token.
2125 *-----------------------------------------------------------------------------
2130 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2131 Tcl_Token** tokenPtrPtr, /* Current token from the parser */
2132 int* result) /* OUTPUT: Integer extracted from the token */
2134 CompileEnv* envPtr = assemEnvPtr->envPtr;
2135 /* Compilation environment */
2136 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
2137 /* Tcl interpreter */
2138 Tcl_Token* tokenPtr = *tokenPtrPtr;
2139 /* INOUT: Pointer to the next token in the
2141 Tcl_Obj* intObj; /* Integer from the source code */
2142 int status; /* Tcl status return */
2145 * Extract the next token as a string.
2148 if (GetNextOperand(assemEnvPtr, tokenPtrPtr, &intObj) != TCL_OK) {
2153 * Convert to an integer, advance to the next token and return.
2156 status = Tcl_GetBooleanFromObj(interp, intObj, result);
2157 Tcl_DecrRefCount(intObj);
2158 *tokenPtrPtr = TokenAfter(tokenPtr);
2163 *-----------------------------------------------------------------------------
2165 * GetIntegerOperand --
2167 * Retrieves an integer operand from the input stream and advances the
2171 * Returns a standard Tcl result (with an error message in the
2172 * interpreter on failure).
2175 * Stores the integer value in (*result) and advances (*tokenPtrPtr) to
2178 *-----------------------------------------------------------------------------
2183 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2184 Tcl_Token** tokenPtrPtr, /* Current token from the parser */
2185 int* result) /* OUTPUT: Integer extracted from the token */
2187 CompileEnv* envPtr = assemEnvPtr->envPtr;
2188 /* Compilation environment */
2189 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
2190 /* Tcl interpreter */
2191 Tcl_Token* tokenPtr = *tokenPtrPtr;
2192 /* INOUT: Pointer to the next token in the
2194 Tcl_Obj* intObj; /* Integer from the source code */
2195 int status; /* Tcl status return */
2198 * Extract the next token as a string.
2201 if (GetNextOperand(assemEnvPtr, tokenPtrPtr, &intObj) != TCL_OK) {
2206 * Convert to an integer, advance to the next token and return.
2209 status = Tcl_GetIntFromObj(interp, intObj, result);
2210 Tcl_DecrRefCount(intObj);
2211 *tokenPtrPtr = TokenAfter(tokenPtr);
2216 *-----------------------------------------------------------------------------
2218 * GetListIndexOperand --
2220 * Gets the value of an operand intended to serve as a list index.
2223 * Returns a standard Tcl result: TCL_OK if the parse is successful and
2224 * TCL_ERROR (with an appropriate error message) if the parse fails.
2227 * Stores the list index at '*index'. Values between -1 and 0x7FFFFFFF
2228 * have their natural meaning; values between -2 and -0x80000000
2229 * represent 'end-2-N'.
2231 *-----------------------------------------------------------------------------
2235 GetListIndexOperand(
2236 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2237 Tcl_Token** tokenPtrPtr, /* Current token from the parser */
2238 int* result) /* OUTPUT: Integer extracted from the token */
2240 CompileEnv* envPtr = assemEnvPtr->envPtr;
2241 /* Compilation environment */
2242 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
2243 /* Tcl interpreter */
2244 Tcl_Token* tokenPtr = *tokenPtrPtr;
2245 /* INOUT: Pointer to the next token in the
2250 /* General operand validity check */
2251 if (GetNextOperand(assemEnvPtr, tokenPtrPtr, &value) != TCL_OK) {
2255 /* Convert to an integer, advance to the next token and return. */
2257 * NOTE: Indexing a list with an index before it yields the
2258 * same result as indexing after it, and might be more easily portable
2259 * when list size limits grow.
2261 status = TclIndexEncode(interp, value,
2262 TCL_INDEX_BEFORE,TCL_INDEX_BEFORE, result);
2264 Tcl_DecrRefCount(value);
2265 *tokenPtrPtr = TokenAfter(tokenPtr);
2270 *-----------------------------------------------------------------------------
2274 * Gets the name of a local variable from the input stream and advances
2275 * the token pointer.
2278 * Returns the LVT index of the local variable. Returns -1 if the
2279 * variable is non-local, not known at compile time, or cannot be
2280 * installed in the LVT (leaving an error message in the interpreter
2281 * result if necessary).
2284 * Advances the token pointer. May define a new LVT slot if the variable
2285 * has not yet been seen and the execution context allows for it.
2287 *-----------------------------------------------------------------------------
2292 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2293 Tcl_Token** tokenPtrPtr)
2295 CompileEnv* envPtr = assemEnvPtr->envPtr;
2296 /* Compilation environment */
2297 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
2298 /* Tcl interpreter */
2299 Tcl_Token* tokenPtr = *tokenPtrPtr;
2300 /* INOUT: Pointer to the next token in the
2302 Tcl_Obj* varNameObj; /* Name of the variable */
2303 const char* varNameStr;
2305 int localVar; /* Index of the variable in the LVT */
2307 if (GetNextOperand(assemEnvPtr, tokenPtrPtr, &varNameObj) != TCL_OK) {
2310 varNameStr = Tcl_GetStringFromObj(varNameObj, &varNameLen);
2311 if (CheckNamespaceQualifiers(interp, varNameStr, varNameLen)) {
2312 Tcl_DecrRefCount(varNameObj);
2315 localVar = TclFindCompiledLocal(varNameStr, varNameLen, 1, envPtr);
2316 Tcl_DecrRefCount(varNameObj);
2317 if (localVar == -1) {
2318 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
2319 Tcl_SetObjResult(interp, Tcl_NewStringObj(
2320 "cannot use this instruction to create a variable"
2321 " in a non-proc context", -1));
2322 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "LVT", NULL);
2326 *tokenPtrPtr = TokenAfter(tokenPtr);
2331 *-----------------------------------------------------------------------------
2333 * CheckNamespaceQualifiers --
2335 * Verify that a variable name has no namespace qualifiers before
2336 * attempting to install it in the LVT.
2339 * On success, returns TCL_OK. On failure, returns TCL_ERROR and stores
2340 * an error message in the interpreter result.
2342 *-----------------------------------------------------------------------------
2346 CheckNamespaceQualifiers(
2347 Tcl_Interp* interp, /* Tcl interpreter for error reporting */
2348 const char* name, /* Variable name to check */
2349 int nameLen) /* Length of the variable */
2353 for (p = name; p+2 < name+nameLen; p++) {
2354 if ((*p == ':') && (p[1] == ':')) {
2355 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2356 "variable \"%s\" is not local", name));
2357 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "NONLOCAL", name, NULL);
2365 *-----------------------------------------------------------------------------
2369 * Verify that a constant fits in a single byte in the instruction
2373 * On success, returns TCL_OK. On failure, returns TCL_ERROR and stores
2374 * an error message in the interpreter result.
2376 * This code is here primarily to verify that instructions like INCR_SCALAR1
2377 * are possible on a given local variable. The fact that there is no
2378 * INCR_SCALAR4 is puzzling.
2380 *-----------------------------------------------------------------------------
2385 Tcl_Interp* interp, /* Tcl interpreter for error reporting */
2386 int value) /* Value to check */
2388 Tcl_Obj* result; /* Error message */
2390 if (value < 0 || value > 0xFF) {
2391 result = Tcl_NewStringObj("operand does not fit in one byte", -1);
2392 Tcl_SetObjResult(interp, result);
2393 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "1BYTE", NULL);
2400 *-----------------------------------------------------------------------------
2402 * CheckSignedOneByte --
2404 * Verify that a constant fits in a single signed byte in the instruction
2408 * On success, returns TCL_OK. On failure, returns TCL_ERROR and stores
2409 * an error message in the interpreter result.
2411 * This code is here primarily to verify that instructions like INCR_SCALAR1
2412 * are possible on a given local variable. The fact that there is no
2413 * INCR_SCALAR4 is puzzling.
2415 *-----------------------------------------------------------------------------
2420 Tcl_Interp* interp, /* Tcl interpreter for error reporting */
2421 int value) /* Value to check */
2423 Tcl_Obj* result; /* Error message */
2425 if (value > 0x7F || value < -0x80) {
2426 result = Tcl_NewStringObj("operand does not fit in one byte", -1);
2427 Tcl_SetObjResult(interp, result);
2428 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "1BYTE", NULL);
2435 *-----------------------------------------------------------------------------
2437 * CheckNonNegative --
2439 * Verify that a constant is nonnegative
2442 * On success, returns TCL_OK. On failure, returns TCL_ERROR and stores
2443 * an error message in the interpreter result.
2445 * This code is here primarily to verify that instructions like INCR_INVOKE
2446 * are consuming a positive number of operands
2448 *-----------------------------------------------------------------------------
2453 Tcl_Interp* interp, /* Tcl interpreter for error reporting */
2454 int value) /* Value to check */
2456 Tcl_Obj* result; /* Error message */
2459 result = Tcl_NewStringObj("operand must be nonnegative", -1);
2460 Tcl_SetObjResult(interp, result);
2461 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "NONNEGATIVE", NULL);
2468 *-----------------------------------------------------------------------------
2470 * CheckStrictlyPositive --
2472 * Verify that a constant is positive
2475 * On success, returns TCL_OK. On failure, returns TCL_ERROR and
2476 * stores an error message in the interpreter result.
2478 * This code is here primarily to verify that instructions like INCR_INVOKE
2479 * are consuming a positive number of operands
2481 *-----------------------------------------------------------------------------
2485 CheckStrictlyPositive(
2486 Tcl_Interp* interp, /* Tcl interpreter for error reporting */
2487 int value) /* Value to check */
2489 Tcl_Obj* result; /* Error message */
2492 result = Tcl_NewStringObj("operand must be positive", -1);
2493 Tcl_SetObjResult(interp, result);
2494 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "POSITIVE", NULL);
2501 *-----------------------------------------------------------------------------
2505 * Defines a label appearing in the assembly sequence.
2508 * Returns a standard Tcl result. Returns TCL_OK and an empty result if
2509 * the definition succeeds; returns TCL_ERROR and an appropriate message
2510 * if a duplicate definition is found.
2512 *-----------------------------------------------------------------------------
2517 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2518 const char* labelName) /* Label being defined */
2520 CompileEnv* envPtr = assemEnvPtr->envPtr;
2521 /* Compilation environment */
2522 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
2523 /* Tcl interpreter */
2524 Tcl_HashEntry* entry; /* Label's entry in the symbol table */
2525 int isNew; /* Flag == 1 iff the label was previously
2528 /* TODO - This can now be simplified! */
2530 StartBasicBlock(assemEnvPtr, BB_FALLTHRU, NULL);
2533 * Look up the newly-defined label in the symbol table.
2536 entry = Tcl_CreateHashEntry(&assemEnvPtr->labelHash, labelName, &isNew);
2539 * This is a duplicate label.
2542 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
2543 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2544 "duplicate definition of label \"%s\"", labelName));
2545 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "DUPLABEL", labelName,
2552 * This is the first appearance of the label in the code.
2555 Tcl_SetHashValue(entry, assemEnvPtr->curr_bb);
2560 *-----------------------------------------------------------------------------
2562 * StartBasicBlock --
2564 * Starts a new basic block when a label or jump is encountered.
2567 * Returns a pointer to the BasicBlock structure of the new
2570 *-----------------------------------------------------------------------------
2575 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2576 int flags, /* Flags to apply to the basic block being
2577 * closed, if there is one. */
2578 Tcl_Obj* jumpLabel) /* Label of the location that the block jumps
2579 * to, or NULL if the block does not jump */
2581 CompileEnv* envPtr = assemEnvPtr->envPtr;
2582 /* Compilation environment */
2583 BasicBlock* newBB; /* BasicBlock structure for the new block */
2584 BasicBlock* currBB = assemEnvPtr->curr_bb;
2587 * Coalesce zero-length blocks.
2590 if (currBB->startOffset == envPtr->codeNext - envPtr->codeStart) {
2591 currBB->startLine = assemEnvPtr->cmdLine;
2596 * Make the new basic block.
2599 newBB = AllocBB(assemEnvPtr);
2602 * Record the jump target if there is one.
2605 currBB->jumpTarget = jumpLabel;
2606 if (jumpLabel != NULL) {
2607 Tcl_IncrRefCount(currBB->jumpTarget);
2611 * Record the fallthrough if there is one.
2614 currBB->flags |= flags;
2617 * Record the successor block.
2620 currBB->successor1 = newBB;
2621 assemEnvPtr->curr_bb = newBB;
2626 *-----------------------------------------------------------------------------
2630 * Allocates a new basic block
2633 * Returns a pointer to the newly allocated block, which is initialized
2634 * to contain no code and begin at the current instruction pointer.
2636 *-----------------------------------------------------------------------------
2641 AssemblyEnv* assemEnvPtr) /* Assembly environment */
2643 CompileEnv* envPtr = assemEnvPtr->envPtr;
2644 BasicBlock *bb = (BasicBlock*)ckalloc(sizeof(BasicBlock));
2646 bb->originalStartOffset =
2647 bb->startOffset = envPtr->codeNext - envPtr->codeStart;
2648 bb->startLine = assemEnvPtr->cmdLine + 1;
2649 bb->jumpOffset = -1;
2651 bb->prevPtr = assemEnvPtr->curr_bb;
2652 bb->predecessor = NULL;
2653 bb->successor1 = NULL;
2654 bb->jumpTarget = NULL;
2655 bb->initialStackDepth = 0;
2656 bb->minStackDepth = 0;
2657 bb->maxStackDepth = 0;
2658 bb->finalStackDepth = 0;
2660 bb->enclosingCatch = NULL;
2661 bb->foreignExceptionBase = -1;
2662 bb->foreignExceptionCount = 0;
2663 bb->foreignExceptions = NULL;
2671 *-----------------------------------------------------------------------------
2675 * Postprocessing after all bytecode has been generated for a block of
2679 * Returns a standard Tcl result, with an error message left in the
2680 * interpreter if appropriate.
2683 * The program is checked to see if any undefined labels remain. The
2684 * initial stack depth of all the basic blocks in the flow graph is
2685 * calculated and saved. The stack balance on exit is computed, checked
2688 *-----------------------------------------------------------------------------
2693 AssemblyEnv* assemEnvPtr) /* Assembly environment */
2695 int mustMove; /* Amount by which the code needs to be grown
2696 * because of expanding jumps */
2699 * Resolve the targets of all jumps and determine whether code needs to be
2703 if (CalculateJumpRelocations(assemEnvPtr, &mustMove)) {
2708 * Move the code if necessary.
2712 MoveCodeForJumps(assemEnvPtr, mustMove);
2716 * Resolve jump target labels to bytecode offsets.
2719 FillInJumpOffsets(assemEnvPtr);
2722 * Label each basic block with its catch context. Quit on inconsistency.
2725 if (ProcessCatches(assemEnvPtr) != TCL_OK) {
2730 * Make sure that no block accessible from a catch's error exit that hasn't
2731 * popped the exception stack can throw an exception.
2734 if (CheckForThrowInWrongContext(assemEnvPtr) != TCL_OK) {
2739 * Compute stack balance throughout the program.
2742 if (CheckStack(assemEnvPtr) != TCL_OK) {
2747 * TODO - Check for unreachable code. Or maybe not; unreachable code is
2755 *-----------------------------------------------------------------------------
2757 * CalculateJumpRelocations --
2759 * Calculate any movement that has to be done in the assembly code to
2760 * expand JUMP1 instructions to JUMP4 (because they jump more than a
2764 * Returns a standard Tcl result, with an appropriate error message if
2768 * Sets the 'startOffset' pointer in every basic block to the new origin
2769 * of the block, and turns off JUMP1 flags on instructions that must be
2770 * expanded (and adjusts them to the corresponding JUMP4's). Does *not*
2771 * store the jump offsets at this point.
2773 * Sets *mustMove to 1 if and only if at least one instruction changed
2774 * size so the code must be moved.
2776 * As a side effect, also checks for undefined labels and reports them.
2778 *-----------------------------------------------------------------------------
2782 CalculateJumpRelocations(
2783 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2784 int* mustMove) /* OUTPUT: Number of bytes that have been
2785 * added to the code */
2787 CompileEnv* envPtr = assemEnvPtr->envPtr;
2788 /* Compilation environment */
2789 BasicBlock* bbPtr; /* Pointer to a basic block being checked */
2790 Tcl_HashEntry* entry; /* Exit label's entry in the symbol table */
2791 BasicBlock* jumpTarget; /* Basic block where the jump goes */
2792 int motion; /* Amount by which the code has expanded */
2793 int offset; /* Offset in the bytecode from a jump
2794 * instruction to its target */
2795 unsigned opcode; /* Opcode in the bytecode being adjusted */
2798 * Iterate through basic blocks as long as a change results in code
2805 for (bbPtr = assemEnvPtr->head_bb;
2807 bbPtr = bbPtr->successor1) {
2809 * Advance the basic block start offset by however many bytes we
2810 * have inserted in the code up to this point
2813 bbPtr->startOffset += motion;
2816 * If the basic block references a label (and hence performs a
2817 * jump), find the location of the label. Report an error if the
2821 if (bbPtr->jumpTarget != NULL) {
2822 entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
2823 Tcl_GetString(bbPtr->jumpTarget));
2824 if (entry == NULL) {
2825 ReportUndefinedLabel(assemEnvPtr, bbPtr,
2831 * If the instruction is a JUMP1, turn it into a JUMP4 if its
2832 * target is out of range.
2835 jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
2836 if (bbPtr->flags & BB_JUMP1) {
2837 offset = jumpTarget->startOffset
2838 - (bbPtr->jumpOffset + motion);
2839 if (offset < -0x80 || offset > 0x7F) {
2840 opcode = TclGetUInt1AtPtr(envPtr->codeStart
2841 + bbPtr->jumpOffset);
2843 TclStoreInt1AtPtr(opcode,
2844 envPtr->codeStart + bbPtr->jumpOffset);
2846 bbPtr->flags &= ~BB_JUMP1;
2852 * If the basic block references a jump table, that doesn't affect
2853 * the code locations, but resolve the labels now, and store basic
2854 * block pointers in the jumptable hash.
2857 if (bbPtr->flags & BB_JUMPTABLE) {
2858 if (CheckJumpTableLabels(assemEnvPtr, bbPtr) != TCL_OK) {
2863 *mustMove += motion;
2864 } while (motion != 0);
2870 *-----------------------------------------------------------------------------
2872 * CheckJumpTableLabels --
2874 * Make sure that all the labels in a jump table are defined.
2877 * Returns TCL_OK if they are, TCL_ERROR if they aren't.
2879 *-----------------------------------------------------------------------------
2883 CheckJumpTableLabels(
2884 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2885 BasicBlock* bbPtr) /* Basic block that ends in a jump table */
2887 Tcl_HashTable* symHash = &bbPtr->jtPtr->hashTable;
2888 /* Hash table with the symbols */
2889 Tcl_HashSearch search; /* Hash table iterator */
2890 Tcl_HashEntry* symEntryPtr; /* Hash entry for the symbols */
2891 Tcl_Obj* symbolObj; /* Jump target */
2892 Tcl_HashEntry* valEntryPtr; /* Hash entry for the resolutions */
2895 * Look up every jump target in the jump hash.
2898 DEBUG_PRINT("check jump table labels %p {\n", bbPtr);
2899 for (symEntryPtr = Tcl_FirstHashEntry(symHash, &search);
2900 symEntryPtr != NULL;
2901 symEntryPtr = Tcl_NextHashEntry(&search)) {
2902 symbolObj = (Tcl_Obj*)Tcl_GetHashValue(symEntryPtr);
2903 valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
2904 Tcl_GetString(symbolObj));
2905 DEBUG_PRINT(" %s -> %s (%d)\n",
2906 (char*) Tcl_GetHashKey(symHash, symEntryPtr),
2907 Tcl_GetString(symbolObj), (valEntryPtr != NULL));
2908 if (valEntryPtr == NULL) {
2909 ReportUndefinedLabel(assemEnvPtr, bbPtr, symbolObj);
2918 *-----------------------------------------------------------------------------
2920 * ReportUndefinedLabel --
2922 * Report that a basic block refers to an undefined jump label
2925 * Stores an error message, error code, and line number information in
2926 * the assembler's Tcl interpreter.
2928 *-----------------------------------------------------------------------------
2932 ReportUndefinedLabel(
2933 AssemblyEnv* assemEnvPtr, /* Assembly environment */
2934 BasicBlock* bbPtr, /* Basic block that contains the undefined
2936 Tcl_Obj* jumpTarget) /* Label of a jump target */
2938 CompileEnv* envPtr = assemEnvPtr->envPtr;
2939 /* Compilation environment */
2940 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
2941 /* Tcl interpreter */
2943 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
2944 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
2945 "undefined label \"%s\"", Tcl_GetString(jumpTarget)));
2946 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "NOLABEL",
2947 Tcl_GetString(jumpTarget), NULL);
2948 Tcl_SetErrorLine(interp, bbPtr->jumpLine);
2953 *-----------------------------------------------------------------------------
2955 * MoveCodeForJumps --
2957 * Move bytecodes in memory to accommodate JUMP1 instructions that have
2958 * expanded to become JUMP4's.
2960 *-----------------------------------------------------------------------------
2965 AssemblyEnv* assemEnvPtr, /* Assembler environment */
2966 int mustMove) /* Number of bytes of added code */
2968 CompileEnv* envPtr = assemEnvPtr->envPtr;
2969 /* Compilation environment */
2970 BasicBlock* bbPtr; /* Pointer to a basic block being checked */
2971 int topOffset; /* Bytecode offset of the following basic
2972 * block before code motion */
2975 * Make sure that there is enough space in the bytecode array to
2976 * accommodate the expanded code.
2979 while (envPtr->codeEnd < envPtr->codeNext + mustMove) {
2980 TclExpandCodeArray(envPtr);
2984 * Iterate through the bytecodes in reverse order, and move them upward to
2988 topOffset = envPtr->codeNext - envPtr->codeStart;
2989 for (bbPtr = assemEnvPtr->curr_bb; bbPtr != NULL; bbPtr = bbPtr->prevPtr) {
2990 DEBUG_PRINT("move code from %d to %d\n",
2991 bbPtr->originalStartOffset, bbPtr->startOffset);
2992 memmove(envPtr->codeStart + bbPtr->startOffset,
2993 envPtr->codeStart + bbPtr->originalStartOffset,
2994 topOffset - bbPtr->originalStartOffset);
2995 topOffset = bbPtr->originalStartOffset;
2996 bbPtr->jumpOffset += (bbPtr->startOffset - bbPtr->originalStartOffset);
2998 envPtr->codeNext += mustMove;
3002 *-----------------------------------------------------------------------------
3004 * FillInJumpOffsets --
3006 * Fill in the final offsets of all jump instructions once bytecode
3007 * locations have been completely determined.
3009 *-----------------------------------------------------------------------------
3014 AssemblyEnv* assemEnvPtr) /* Assembly environment */
3016 CompileEnv* envPtr = assemEnvPtr->envPtr;
3017 /* Compilation environment */
3018 BasicBlock* bbPtr; /* Pointer to a basic block being checked */
3019 Tcl_HashEntry* entry; /* Hashtable entry for a jump target label */
3020 BasicBlock* jumpTarget; /* Basic block where a jump goes */
3021 int fromOffset; /* Bytecode location of a jump instruction */
3022 int targetOffset; /* Bytecode location of a jump instruction's
3025 for (bbPtr = assemEnvPtr->head_bb;
3027 bbPtr = bbPtr->successor1) {
3028 if (bbPtr->jumpTarget != NULL) {
3029 entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
3030 Tcl_GetString(bbPtr->jumpTarget));
3031 jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
3032 fromOffset = bbPtr->jumpOffset;
3033 targetOffset = jumpTarget->startOffset;
3034 if (bbPtr->flags & BB_JUMP1) {
3035 TclStoreInt1AtPtr(targetOffset - fromOffset,
3036 envPtr->codeStart + fromOffset + 1);
3038 TclStoreInt4AtPtr(targetOffset - fromOffset,
3039 envPtr->codeStart + fromOffset + 1);
3042 if (bbPtr->flags & BB_JUMPTABLE) {
3043 ResolveJumpTableTargets(assemEnvPtr, bbPtr);
3049 *-----------------------------------------------------------------------------
3051 * ResolveJumpTableTargets --
3053 * Puts bytecode addresses for the targets of a jumptable into the
3057 * Returns TCL_OK if they are, TCL_ERROR if they aren't.
3059 *-----------------------------------------------------------------------------
3063 ResolveJumpTableTargets(
3064 AssemblyEnv* assemEnvPtr, /* Assembly environment */
3065 BasicBlock* bbPtr) /* Basic block that ends in a jump table */
3067 CompileEnv* envPtr = assemEnvPtr->envPtr;
3068 /* Compilation environment */
3069 Tcl_HashTable* symHash = &bbPtr->jtPtr->hashTable;
3070 /* Hash table with the symbols */
3071 Tcl_HashSearch search; /* Hash table iterator */
3072 Tcl_HashEntry* symEntryPtr; /* Hash entry for the symbols */
3073 Tcl_Obj* symbolObj; /* Jump target */
3074 Tcl_HashEntry* valEntryPtr; /* Hash entry for the resolutions */
3075 int auxDataIndex; /* Index of the auxdata */
3076 JumptableInfo* realJumpTablePtr;
3077 /* Jump table in the actual code */
3078 Tcl_HashTable* realJumpHashPtr;
3079 /* Jump table hash in the actual code */
3080 Tcl_HashEntry* realJumpEntryPtr;
3081 /* Entry in the jump table hash in
3082 * the actual code */
3083 BasicBlock* jumpTargetBBPtr;
3084 /* Basic block that the jump proceeds to */
3087 auxDataIndex = TclGetInt4AtPtr(envPtr->codeStart + bbPtr->jumpOffset + 1);
3088 DEBUG_PRINT("bbPtr = %p jumpOffset = %d auxDataIndex = %d\n",
3089 bbPtr, bbPtr->jumpOffset, auxDataIndex);
3090 realJumpTablePtr = (JumptableInfo*)TclFetchAuxData(envPtr, auxDataIndex);
3091 realJumpHashPtr = &realJumpTablePtr->hashTable;
3094 * Look up every jump target in the jump hash.
3097 DEBUG_PRINT("resolve jump table {\n");
3098 for (symEntryPtr = Tcl_FirstHashEntry(symHash, &search);
3099 symEntryPtr != NULL;
3100 symEntryPtr = Tcl_NextHashEntry(&search)) {
3101 symbolObj = (Tcl_Obj*)Tcl_GetHashValue(symEntryPtr);
3102 DEBUG_PRINT(" symbol %s\n", Tcl_GetString(symbolObj));
3104 valEntryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
3105 Tcl_GetString(symbolObj));
3106 jumpTargetBBPtr = (BasicBlock*)Tcl_GetHashValue(valEntryPtr);
3108 realJumpEntryPtr = Tcl_CreateHashEntry(realJumpHashPtr,
3109 Tcl_GetHashKey(symHash, symEntryPtr), &junk);
3110 DEBUG_PRINT(" %s -> %s -> bb %p (pc %d) hash entry %p\n",
3111 (char*) Tcl_GetHashKey(symHash, symEntryPtr),
3112 Tcl_GetString(symbolObj), jumpTargetBBPtr,
3113 jumpTargetBBPtr->startOffset, realJumpEntryPtr);
3115 Tcl_SetHashValue(realJumpEntryPtr,
3116 INT2PTR(jumpTargetBBPtr->startOffset - bbPtr->jumpOffset));
3122 *-----------------------------------------------------------------------------
3124 * CheckForThrowInWrongContext --
3126 * Verify that no beginCatch/endCatch sequence can throw an exception
3127 * after an original exception is caught and before its exception context
3128 * is removed from the stack.
3131 * Returns a standard Tcl result.
3134 * Stores an appropriate error message in the interpreter as needed.
3136 *-----------------------------------------------------------------------------
3140 CheckForThrowInWrongContext(
3141 AssemblyEnv* assemEnvPtr) /* Assembly environment */
3143 BasicBlock* blockPtr; /* Current basic block */
3146 * Walk through the basic blocks in turn, checking all the ones that have
3147 * caught an exception and not disposed of it properly.
3150 for (blockPtr = assemEnvPtr->head_bb;
3152 blockPtr = blockPtr->successor1) {
3153 if (blockPtr->catchState == BBCS_CAUGHT) {
3155 * Walk through the instructions in the basic block.
3158 if (CheckNonThrowingBlock(assemEnvPtr, blockPtr) != TCL_OK) {
3167 *-----------------------------------------------------------------------------
3169 * CheckNonThrowingBlock --
3171 * Check that a basic block cannot throw an exception.
3174 * Returns TCL_ERROR if the block cannot be proven to be nonthrowing.
3177 * Stashes an error message in the interpreter result.
3179 *-----------------------------------------------------------------------------
3183 CheckNonThrowingBlock(
3184 AssemblyEnv* assemEnvPtr, /* Assembly environment */
3185 BasicBlock* blockPtr) /* Basic block where exceptions are not
3188 CompileEnv* envPtr = assemEnvPtr->envPtr;
3189 /* Compilation environment */
3190 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
3191 /* Tcl interpreter */
3192 BasicBlock* nextPtr; /* Pointer to the succeeding basic block */
3193 int offset; /* Bytecode offset of the current
3195 int bound; /* Bytecode offset following the last
3196 * instruction of the block. */
3197 unsigned char opcode; /* Current bytecode instruction */
3200 * Determine where in the code array the basic block ends.
3203 nextPtr = blockPtr->successor1;
3204 if (nextPtr == NULL) {
3205 bound = envPtr->codeNext - envPtr->codeStart;
3207 bound = nextPtr->startOffset;
3211 * Walk through the instructions of the block.
3214 offset = blockPtr->startOffset;
3215 while (offset < bound) {
3217 * Determine whether an instruction is nonthrowing.
3220 opcode = (envPtr->codeStart)[offset];
3221 if (BytecodeMightThrow(opcode)) {
3223 * Report an error for a throw in the wrong context.
3226 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
3227 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
3228 "\"%s\" instruction may not appear in "
3229 "a context where an exception has been "
3230 "caught and not disposed of.",
3231 tclInstructionTable[opcode].name));
3232 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADTHROW", NULL);
3233 AddBasicBlockRangeToErrorInfo(assemEnvPtr, blockPtr);
3237 offset += tclInstructionTable[opcode].numBytes;
3243 *-----------------------------------------------------------------------------
3245 * BytecodeMightThrow --
3247 * Tests if a given bytecode instruction might throw an exception.
3250 * Returns 1 if the bytecode might throw an exception, 0 if the
3251 * instruction is known never to throw.
3253 *-----------------------------------------------------------------------------
3258 unsigned char opcode)
3261 * Binary search on the non-throwing bytecode list.
3265 int max = sizeof(NonThrowingByteCodes) - 1;
3269 while (max >= min) {
3270 mid = (min + max) / 2;
3271 c = NonThrowingByteCodes[mid];
3274 } else if (opcode > c) {
3278 * Opcode is nonthrowing.
3289 *-----------------------------------------------------------------------------
3293 * Audit stack usage in a block of assembly code.
3296 * Returns a standard Tcl result.
3299 * Updates stack depth on entry for all basic blocks in the flowgraph.
3300 * Calculates the max stack depth used in the program, and updates the
3301 * compilation environment to reflect it.
3303 *-----------------------------------------------------------------------------
3308 AssemblyEnv* assemEnvPtr) /* Assembly environment */
3310 CompileEnv* envPtr = assemEnvPtr->envPtr;
3311 /* Compilation environment */
3312 int maxDepth; /* Maximum stack depth overall */
3315 * Checking the head block will check all the other blocks recursively.
3318 assemEnvPtr->maxDepth = 0;
3319 if (StackCheckBasicBlock(assemEnvPtr, assemEnvPtr->head_bb, NULL,
3325 * Post the max stack depth back to the compilation environment.
3328 maxDepth = assemEnvPtr->maxDepth + envPtr->currStackDepth;
3329 if (maxDepth > envPtr->maxStackDepth) {
3330 envPtr->maxStackDepth = maxDepth;
3334 * If the exit is reachable, make sure that the program exits with 1
3335 * operand on the stack.
3338 if (StackCheckExit(assemEnvPtr) != TCL_OK) {
3343 * Reset the visited state on all basic blocks.
3346 ResetVisitedBasicBlocks(assemEnvPtr);
3351 *-----------------------------------------------------------------------------
3353 * StackCheckBasicBlock --
3355 * Checks stack consumption for a basic block (and recursively for its
3359 * Returns a standard Tcl result.
3362 * Updates initial stack depth for the basic block and its successors.
3363 * (Final and maximum stack depth are relative to initial, and are not
3366 * This procedure eventually checks, for the entire flow graph, whether stack
3367 * balance is consistent. It is an error for a given basic block to be
3368 * reachable along multiple flow paths with different stack depths.
3370 *-----------------------------------------------------------------------------
3374 StackCheckBasicBlock(
3375 AssemblyEnv* assemEnvPtr, /* Assembly environment */
3376 BasicBlock* blockPtr, /* Pointer to the basic block being checked */
3377 BasicBlock* predecessor, /* Pointer to the block that passed control to
3379 int initialStackDepth) /* Stack depth on entry to the block */
3381 CompileEnv* envPtr = assemEnvPtr->envPtr;
3382 /* Compilation environment */
3383 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
3384 /* Tcl interpreter */
3385 BasicBlock* jumpTarget; /* Basic block where a jump goes */
3386 int stackDepth; /* Current stack depth */
3387 int maxDepth; /* Maximum stack depth so far */
3388 int result; /* Tcl status return */
3389 Tcl_HashSearch jtSearch; /* Search structure for the jump table */
3390 Tcl_HashEntry* jtEntry; /* Hash entry in the jump table */
3391 Tcl_Obj* targetLabel; /* Target label from the jump table */
3392 Tcl_HashEntry* entry; /* Hash entry in the label table */
3394 if (blockPtr->flags & BB_VISITED) {
3396 * If the block is already visited, check stack depth for consistency
3397 * among the paths that reach it.
3400 if (blockPtr->initialStackDepth == initialStackDepth) {
3403 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
3404 Tcl_SetObjResult(interp, Tcl_NewStringObj(
3405 "inconsistent stack depths on two execution paths", -1));
3408 * TODO - add execution trace of both paths
3411 Tcl_SetErrorLine(interp, blockPtr->startLine);
3412 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADSTACK", NULL);
3418 * If the block is not already visited, set the 'predecessor' link to
3419 * indicate how control got to it. Set the initial stack depth to the
3420 * current stack depth in the flow of control.
3423 blockPtr->flags |= BB_VISITED;
3424 blockPtr->predecessor = predecessor;
3425 blockPtr->initialStackDepth = initialStackDepth;
3428 * Calculate minimum stack depth, and flag an error if the block
3429 * underflows the stack.
3432 if (initialStackDepth + blockPtr->minStackDepth < 0) {
3433 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
3434 Tcl_SetObjResult(interp, Tcl_NewStringObj("stack underflow", -1));
3435 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADSTACK", NULL);
3436 AddBasicBlockRangeToErrorInfo(assemEnvPtr, blockPtr);
3437 Tcl_SetErrorLine(interp, blockPtr->startLine);
3443 * Make sure that the block doesn't try to pop below the stack level of an
3447 if (blockPtr->enclosingCatch != 0 &&
3448 initialStackDepth + blockPtr->minStackDepth
3449 < (blockPtr->enclosingCatch->initialStackDepth
3450 + blockPtr->enclosingCatch->finalStackDepth)) {
3451 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
3452 Tcl_SetObjResult(interp, Tcl_NewStringObj(
3453 "code pops stack below level of enclosing catch", -1));
3454 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADSTACKINCATCH", -1);
3455 AddBasicBlockRangeToErrorInfo(assemEnvPtr, blockPtr);
3456 Tcl_SetErrorLine(interp, blockPtr->startLine);
3462 * Update maximum stgack depth.
3465 maxDepth = initialStackDepth + blockPtr->maxStackDepth;
3466 if (maxDepth > assemEnvPtr->maxDepth) {
3467 assemEnvPtr->maxDepth = maxDepth;
3471 * Calculate stack depth on exit from the block, and invoke this procedure
3472 * recursively to check successor blocks.
3475 stackDepth = initialStackDepth + blockPtr->finalStackDepth;
3477 if (blockPtr->flags & BB_FALLTHRU) {
3478 result = StackCheckBasicBlock(assemEnvPtr, blockPtr->successor1,
3479 blockPtr, stackDepth);
3482 if (result == TCL_OK && blockPtr->jumpTarget != NULL) {
3483 entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
3484 Tcl_GetString(blockPtr->jumpTarget));
3485 jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
3486 result = StackCheckBasicBlock(assemEnvPtr, jumpTarget, blockPtr,
3491 * All blocks referenced in a jump table are successors.
3494 if (blockPtr->flags & BB_JUMPTABLE) {
3495 for (jtEntry = Tcl_FirstHashEntry(&blockPtr->jtPtr->hashTable,
3497 result == TCL_OK && jtEntry != NULL;
3498 jtEntry = Tcl_NextHashEntry(&jtSearch)) {
3499 targetLabel = (Tcl_Obj*)Tcl_GetHashValue(jtEntry);
3500 entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
3501 Tcl_GetString(targetLabel));
3502 jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
3503 result = StackCheckBasicBlock(assemEnvPtr, jumpTarget,
3504 blockPtr, stackDepth);
3512 *-----------------------------------------------------------------------------
3516 * Makes sure that the net stack effect of an entire assembly language
3517 * script is to push 1 result.
3520 * Returns a standard Tcl result, with an error message in the
3521 * interpreter result if the stack is wrong.
3524 * If the assembly code had a net stack effect of zero, emits code to the
3525 * concluding block to push a null result. In any case, updates the stack
3526 * depth in the compile environment to reflect the net effect of the
3529 *-----------------------------------------------------------------------------
3534 AssemblyEnv* assemEnvPtr) /* Assembly environment */
3536 CompileEnv* envPtr = assemEnvPtr->envPtr;
3537 /* Compilation environment */
3538 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
3539 /* Tcl interpreter */
3540 int depth; /* Net stack effect */
3541 int litIndex; /* Index in the literal pool of the empty
3543 BasicBlock* curr_bb = assemEnvPtr->curr_bb;
3544 /* Final basic block in the assembly */
3547 * Don't perform these checks if execution doesn't reach the exit (either
3548 * because of an infinite loop or because the only return is from the
3552 if (curr_bb->flags & BB_VISITED) {
3554 * Exit with no operands; push an empty one.
3557 depth = curr_bb->finalStackDepth + curr_bb->initialStackDepth;
3560 * Emit a 'push' of the empty literal.
3563 litIndex = TclRegisterNewLiteral(envPtr, "", 0);
3566 * Assumes that 'push' is at slot 0 in TalInstructionTable.
3569 BBEmitInst1or4(assemEnvPtr, 0, litIndex, 0);
3574 * Exit with unbalanced stack.
3578 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
3579 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
3580 "stack is unbalanced on exit from the code (depth=%d)",
3582 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADSTACK", NULL);
3588 * Record stack usage.
3591 envPtr->currStackDepth += depth;
3598 *-----------------------------------------------------------------------------
3602 * First pass of 'catch' processing.
3605 * Returns a standard Tcl result, with an appropriate error message if
3606 * the result is TCL_ERROR.
3609 * Labels all basic blocks with their enclosing catches.
3611 *-----------------------------------------------------------------------------
3616 AssemblyEnv* assemEnvPtr) /* Assembly environment */
3618 BasicBlock* blockPtr; /* Pointer to a basic block */
3621 * Clear the catch state of all basic blocks.
3624 for (blockPtr = assemEnvPtr->head_bb;
3626 blockPtr = blockPtr->successor1) {
3627 blockPtr->catchState = BBCS_UNKNOWN;
3628 blockPtr->enclosingCatch = NULL;
3632 * Start the check recursively from the first basic block, which is
3633 * outside any exception context
3636 if (ProcessCatchesInBasicBlock(assemEnvPtr, assemEnvPtr->head_bb,
3637 NULL, BBCS_NONE, 0) != TCL_OK) {
3642 * Check for unclosed catch on exit.
3645 if (CheckForUnclosedCatches(assemEnvPtr) != TCL_OK) {
3650 * Now there's enough information to build the exception ranges.
3653 if (BuildExceptionRanges(assemEnvPtr) != TCL_OK) {
3658 * Finally, restore any exception ranges from embedded scripts.
3661 RestoreEmbeddedExceptionRanges(assemEnvPtr);
3666 *-----------------------------------------------------------------------------
3668 * ProcessCatchesInBasicBlock --
3670 * First-pass catch processing for one basic block.
3673 * Returns a standard Tcl result, with error message in the interpreter
3674 * result if an error occurs.
3676 * This procedure checks consistency of the exception context through the
3677 * assembler program, and records the enclosing 'catch' for every basic block.
3679 *-----------------------------------------------------------------------------
3683 ProcessCatchesInBasicBlock(
3684 AssemblyEnv* assemEnvPtr, /* Assembly environment */
3685 BasicBlock* bbPtr, /* Basic block being processed */
3686 BasicBlock* enclosing, /* Start basic block of the enclosing catch */
3687 enum BasicBlockCatchState state,
3688 /* BBCS_NONE, BBCS_INCATCH, or BBCS_CAUGHT */
3689 int catchDepth) /* Depth of nesting of catches */
3691 CompileEnv* envPtr = assemEnvPtr->envPtr;
3692 /* Compilation environment */
3693 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
3694 /* Tcl interpreter */
3695 int result; /* Return value from this procedure */
3696 BasicBlock* fallThruEnclosing;
3697 /* Enclosing catch if execution falls thru */
3698 enum BasicBlockCatchState fallThruState;
3699 /* Catch state of the successor block */
3700 BasicBlock* jumpEnclosing; /* Enclosing catch if execution goes to jump
3702 enum BasicBlockCatchState jumpState;
3703 /* Catch state of the jump target */
3704 int changed = 0; /* Flag == 1 iff successor blocks need to be
3705 * checked because the state of this block has
3707 BasicBlock* jumpTarget; /* Basic block where a jump goes */
3708 Tcl_HashSearch jtSearch; /* Hash search control for a jumptable */
3709 Tcl_HashEntry* jtEntry; /* Entry in a jumptable */
3710 Tcl_Obj* targetLabel; /* Target label from a jumptable */
3711 Tcl_HashEntry* entry; /* Entry from the label table */
3714 * Update the state of the current block, checking for consistency. Set
3715 * 'changed' to 1 if the state changes and successor blocks need to be
3719 if (bbPtr->catchState == BBCS_UNKNOWN) {
3720 bbPtr->enclosingCatch = enclosing;
3721 } else if (bbPtr->enclosingCatch != enclosing) {
3722 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
3723 Tcl_SetObjResult(interp, Tcl_NewStringObj(
3724 "execution reaches an instruction in inconsistent "
3725 "exception contexts", -1));
3726 Tcl_SetErrorLine(interp, bbPtr->startLine);
3727 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADCATCH", NULL);
3731 if (state > bbPtr->catchState) {
3732 bbPtr->catchState = state;
3737 * If this block has been visited before, and its state hasn't changed,
3738 * we're done with it for now.
3744 bbPtr->catchDepth = catchDepth;
3747 * Determine enclosing catch and 'caught' state for the fallthrough and
3748 * the jump target. Default for both is the state of the current block.
3751 fallThruEnclosing = enclosing;
3752 fallThruState = state;
3753 jumpEnclosing = enclosing;
3757 * TODO: Make sure that the test cases include validating that a natural
3758 * loop can't include 'beginCatch' or 'endCatch'
3761 if (bbPtr->flags & BB_BEGINCATCH) {
3763 * If the block begins a catch, the state for the successor is 'in
3764 * catch'. The jump target is the exception exit, and the state of the
3765 * jump target is 'caught.'
3768 fallThruEnclosing = bbPtr;
3769 fallThruState = BBCS_INCATCH;
3770 jumpEnclosing = bbPtr;
3771 jumpState = BBCS_CAUGHT;
3775 if (bbPtr->flags & BB_ENDCATCH) {
3777 * If the block ends a catch, the state for the successor is whatever
3778 * the state was on entry to the catch.
3781 if (enclosing == NULL) {
3782 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
3783 Tcl_SetObjResult(interp, Tcl_NewStringObj(
3784 "endCatch without a corresponding beginCatch", -1));
3785 Tcl_SetErrorLine(interp, bbPtr->startLine);
3786 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "BADENDCATCH", NULL);
3790 fallThruEnclosing = enclosing->enclosingCatch;
3791 fallThruState = enclosing->catchState;
3796 * Visit any successor blocks with the appropriate exception context
3800 if (bbPtr->flags & BB_FALLTHRU) {
3801 result = ProcessCatchesInBasicBlock(assemEnvPtr, bbPtr->successor1,
3802 fallThruEnclosing, fallThruState, catchDepth);
3804 if (result == TCL_OK && bbPtr->jumpTarget != NULL) {
3805 entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
3806 Tcl_GetString(bbPtr->jumpTarget));
3807 jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
3808 result = ProcessCatchesInBasicBlock(assemEnvPtr, jumpTarget,
3809 jumpEnclosing, jumpState, catchDepth);
3813 * All blocks referenced in a jump table are successors.
3816 if (bbPtr->flags & BB_JUMPTABLE) {
3817 for (jtEntry = Tcl_FirstHashEntry(&bbPtr->jtPtr->hashTable,&jtSearch);
3818 result == TCL_OK && jtEntry != NULL;
3819 jtEntry = Tcl_NextHashEntry(&jtSearch)) {
3820 targetLabel = (Tcl_Obj*)Tcl_GetHashValue(jtEntry);
3821 entry = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
3822 Tcl_GetString(targetLabel));
3823 jumpTarget = (BasicBlock*)Tcl_GetHashValue(entry);
3824 result = ProcessCatchesInBasicBlock(assemEnvPtr, jumpTarget,
3825 jumpEnclosing, jumpState, catchDepth);
3833 *-----------------------------------------------------------------------------
3835 * CheckForUnclosedCatches --
3837 * Checks that a sequence of assembly code has no unclosed catches on
3841 * Returns a standard Tcl result, with an error message for unclosed
3844 *-----------------------------------------------------------------------------
3848 CheckForUnclosedCatches(
3849 AssemblyEnv* assemEnvPtr) /* Assembly environment */
3851 CompileEnv* envPtr = assemEnvPtr->envPtr;
3852 /* Compilation environment */
3853 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
3854 /* Tcl interpreter */
3856 if (assemEnvPtr->curr_bb->catchState >= BBCS_INCATCH) {
3857 if (assemEnvPtr->flags & TCL_EVAL_DIRECT) {
3858 Tcl_SetObjResult(interp, Tcl_NewStringObj(
3859 "catch still active on exit from assembly code", -1));
3860 Tcl_SetErrorLine(interp,
3861 assemEnvPtr->curr_bb->enclosingCatch->startLine);
3862 Tcl_SetErrorCode(interp, "TCL", "ASSEM", "UNCLOSEDCATCH", NULL);
3870 *-----------------------------------------------------------------------------
3872 * BuildExceptionRanges --
3874 * Walks through the assembly code and builds exception ranges for the
3875 * catches embedded therein.
3878 * Returns a standard Tcl result with an error message in the interpreter
3879 * if anything is unsuccessful.
3882 * Each contiguous block of code with a given catch exit is assigned an
3883 * exception range at the appropriate level.
3884 * Exception ranges in embedded blocks have their levels corrected and
3885 * collated into the table.
3886 * Blocks that end with 'beginCatch' are associated with the innermost
3887 * exception range of the following block.
3889 *-----------------------------------------------------------------------------
3893 BuildExceptionRanges(
3894 AssemblyEnv* assemEnvPtr) /* Assembly environment */
3896 CompileEnv* envPtr = assemEnvPtr->envPtr;
3897 /* Compilation environment */
3898 BasicBlock* bbPtr; /* Current basic block */
3899 BasicBlock* prevPtr = NULL; /* Previous basic block */
3900 int catchDepth = 0; /* Current catch depth */
3901 int maxCatchDepth = 0; /* Maximum catch depth in the program */
3902 BasicBlock** catches; /* Stack of catches in progress */
3903 int* catchIndices; /* Indices of the exception ranges of catches
3908 * Determine the max catch depth for the entire assembly script
3909 * (excluding embedded eval's and expr's, which will be handled later).
3912 for (bbPtr=assemEnvPtr->head_bb; bbPtr != NULL; bbPtr=bbPtr->successor1) {
3913 if (bbPtr->catchDepth > maxCatchDepth) {
3914 maxCatchDepth = bbPtr->catchDepth;
3919 * Allocate memory for a stack of active catches.
3922 catches = (BasicBlock**)ckalloc(maxCatchDepth * sizeof(BasicBlock*));
3923 catchIndices = (int *)ckalloc(maxCatchDepth * sizeof(int));
3924 for (i = 0; i < maxCatchDepth; ++i) {
3926 catchIndices[i] = -1;
3930 * Walk through the basic blocks and manage exception ranges.
3933 for (bbPtr=assemEnvPtr->head_bb; bbPtr != NULL; bbPtr=bbPtr->successor1) {
3934 UnstackExpiredCatches(envPtr, bbPtr, catchDepth, catches,
3936 LookForFreshCatches(bbPtr, catches);
3937 StackFreshCatches(assemEnvPtr, bbPtr, catchDepth, catches,
3941 * If the last block was a 'begin catch', fill in the exception range.
3944 catchDepth = bbPtr->catchDepth;
3945 if (prevPtr != NULL && (prevPtr->flags & BB_BEGINCATCH)) {
3946 TclStoreInt4AtPtr(catchIndices[catchDepth-1],
3947 envPtr->codeStart + bbPtr->startOffset - 4);
3953 /* Make sure that all catches are closed */
3955 if (catchDepth != 0) {
3956 Tcl_Panic("unclosed catch at end of code in "
3957 "tclAssembly.c:BuildExceptionRanges, can't happen");
3960 /* Free temp storage */
3962 ckfree(catchIndices);
3969 *-----------------------------------------------------------------------------
3971 * UnstackExpiredCatches --
3973 * Unstacks and closes the exception ranges for any catch contexts that
3974 * were active in the previous basic block but are inactive in the
3977 *-----------------------------------------------------------------------------
3981 UnstackExpiredCatches(
3982 CompileEnv* envPtr, /* Compilation environment */
3983 BasicBlock* bbPtr, /* Basic block being processed */
3984 int catchDepth, /* Depth of nesting of catches prior to entry
3986 BasicBlock** catches, /* Array of catch contexts */
3987 int* catchIndices) /* Indices of the exception ranges
3988 * corresponding to the catch contexts */
3990 ExceptionRange* range; /* Exception range for a specific catch */
3991 BasicBlock* block; /* Catch block being examined */
3992 BasicBlockCatchState catchState;
3993 /* State of the code relative to the catch
3994 * block being examined ("in catch" or
3998 * Unstack any catches that are deeper than the nesting level of the basic
3999 * block being entered.
4002 while (catchDepth > bbPtr->catchDepth) {
4004 if (catches[catchDepth] != NULL) {
4005 range = envPtr->exceptArrayPtr + catchIndices[catchDepth];
4006 range->numCodeBytes = bbPtr->startOffset - range->codeOffset;
4007 catches[catchDepth] = NULL;
4008 catchIndices[catchDepth] = -1;
4013 * Unstack any catches that don't match the basic block being entered,
4014 * either because they are no longer part of the context, or because the
4015 * context has changed from INCATCH to CAUGHT.
4018 catchState = bbPtr->catchState;
4019 block = bbPtr->enclosingCatch;
4020 while (catchDepth > 0) {
4022 if (catches[catchDepth] != NULL) {
4023 if (catches[catchDepth] != block || catchState >= BBCS_CAUGHT) {
4024 range = envPtr->exceptArrayPtr + catchIndices[catchDepth];
4025 range->numCodeBytes = bbPtr->startOffset - range->codeOffset;
4026 catches[catchDepth] = NULL;
4027 catchIndices[catchDepth] = -1;
4029 catchState = block->catchState;
4030 block = block->enclosingCatch;
4036 *-----------------------------------------------------------------------------
4038 * LookForFreshCatches --
4040 * Determines whether a basic block being entered needs any exception
4041 * ranges that are not already stacked.
4043 * Does not create the ranges: this procedure iterates from the innermost
4044 * catch outward, but exception ranges must be created from the outermost
4047 *-----------------------------------------------------------------------------
4051 LookForFreshCatches(
4052 BasicBlock* bbPtr, /* Basic block being entered */
4053 BasicBlock** catches) /* Array of catch contexts that are already
4056 BasicBlockCatchState catchState;
4057 /* State ("in catch" or "caught") of the
4059 BasicBlock* block; /* Current enclosing catch */
4060 int catchDepth; /* Nesting depth of the current catch */
4062 catchState = bbPtr->catchState;
4063 block = bbPtr->enclosingCatch;
4064 catchDepth = bbPtr->catchDepth;
4065 while (catchDepth > 0) {
4067 if (catches[catchDepth] != block && catchState < BBCS_CAUGHT) {
4068 catches[catchDepth] = block;
4070 catchState = block->catchState;
4071 block = block->enclosingCatch;
4076 *-----------------------------------------------------------------------------
4078 * StackFreshCatches --
4080 * Make ExceptionRange records for any catches that are in the basic
4081 * block being entered and were not in the previous basic block.
4083 *-----------------------------------------------------------------------------
4088 AssemblyEnv* assemEnvPtr, /* Assembly environment */
4089 BasicBlock* bbPtr, /* Basic block being processed */
4090 int catchDepth, /* Depth of nesting of catches prior to entry
4092 BasicBlock** catches, /* Array of catch contexts */
4093 int* catchIndices) /* Indices of the exception ranges
4094 * corresponding to the catch contexts */
4096 CompileEnv* envPtr = assemEnvPtr->envPtr;
4097 /* Compilation environment */
4098 ExceptionRange* range; /* Exception range for a specific catch */
4099 BasicBlock* block; /* Catch block being examined */
4100 BasicBlock* errorExit; /* Error exit from the catch block */
4101 Tcl_HashEntry* entryPtr;
4106 * Iterate through the enclosing catch blocks from the outside in,
4107 * looking for ones that don't have exception ranges (and are uncaught)
4110 for (catchDepth = 0; catchDepth < bbPtr->catchDepth; ++catchDepth) {
4111 if (catchIndices[catchDepth] == -1 && catches[catchDepth] != NULL) {
4113 * Create an exception range for a block that needs one.
4116 block = catches[catchDepth];
4117 catchIndices[catchDepth] =
4118 TclCreateExceptRange(CATCH_EXCEPTION_RANGE, envPtr);
4119 range = envPtr->exceptArrayPtr + catchIndices[catchDepth];
4120 range->nestingLevel = envPtr->exceptDepth + catchDepth;
4121 envPtr->maxExceptDepth =
4122 TclMax(range->nestingLevel + 1, envPtr->maxExceptDepth);
4123 range->codeOffset = bbPtr->startOffset;
4125 entryPtr = Tcl_FindHashEntry(&assemEnvPtr->labelHash,
4126 Tcl_GetString(block->jumpTarget));
4127 if (entryPtr == NULL) {
4128 Tcl_Panic("undefined label in tclAssembly.c:"
4129 "BuildExceptionRanges, can't happen");
4132 errorExit = (BasicBlock*)Tcl_GetHashValue(entryPtr);
4133 range->catchOffset = errorExit->startOffset;
4139 *-----------------------------------------------------------------------------
4141 * RestoreEmbeddedExceptionRanges --
4143 * Processes an assembly script, replacing any exception ranges that
4144 * were present in embedded code.
4146 *-----------------------------------------------------------------------------
4150 RestoreEmbeddedExceptionRanges(
4151 AssemblyEnv* assemEnvPtr) /* Assembly environment */
4153 CompileEnv* envPtr = assemEnvPtr->envPtr;
4154 /* Compilation environment */
4155 BasicBlock* bbPtr; /* Current basic block */
4156 int rangeBase; /* Base of the foreign exception ranges when
4157 * they are reinstalled */
4158 int rangeIndex; /* Index of the current foreign exception
4159 * range as reinstalled */
4160 ExceptionRange* range; /* Current foreign exception range */
4161 unsigned char opcode; /* Current instruction's opcode */
4162 int catchIndex; /* Index of the exception range to which the
4163 * current instruction refers */
4167 * Walk the basic blocks looking for exceptions in embedded scripts.
4170 for (bbPtr = assemEnvPtr->head_bb;
4172 bbPtr = bbPtr->successor1) {
4173 if (bbPtr->foreignExceptionCount != 0) {
4175 * Reinstall the embedded exceptions and track their nesting level
4178 rangeBase = envPtr->exceptArrayNext;
4179 for (i = 0; i < bbPtr->foreignExceptionCount; ++i) {
4180 range = bbPtr->foreignExceptions + i;
4181 rangeIndex = TclCreateExceptRange(range->type, envPtr);
4182 range->nestingLevel += envPtr->exceptDepth + bbPtr->catchDepth;
4183 memcpy(envPtr->exceptArrayPtr + rangeIndex, range,
4184 sizeof(ExceptionRange));
4185 if (range->nestingLevel >= envPtr->maxExceptDepth) {
4186 envPtr->maxExceptDepth = range->nestingLevel + 1;
4191 * Walk through the bytecode of the basic block, and relocate
4192 * INST_BEGIN_CATCH4 instructions to the new locations
4195 i = bbPtr->startOffset;
4196 while (i < bbPtr->successor1->startOffset) {
4197 opcode = envPtr->codeStart[i];
4198 if (opcode == INST_BEGIN_CATCH4) {
4199 catchIndex = TclGetUInt4AtPtr(envPtr->codeStart + i + 1);
4200 if (catchIndex >= bbPtr->foreignExceptionBase
4201 && catchIndex < (bbPtr->foreignExceptionBase +
4202 bbPtr->foreignExceptionCount)) {
4203 catchIndex -= bbPtr->foreignExceptionBase;
4204 catchIndex += rangeBase;
4205 TclStoreInt4AtPtr(catchIndex, envPtr->codeStart+i+1);
4208 i += tclInstructionTable[opcode].numBytes;
4215 *-----------------------------------------------------------------------------
4217 * ResetVisitedBasicBlocks --
4219 * Turns off the 'visited' flag in all basic blocks at the conclusion
4222 *-----------------------------------------------------------------------------
4226 ResetVisitedBasicBlocks(
4227 AssemblyEnv* assemEnvPtr) /* Assembly environment */
4231 for (block = assemEnvPtr->head_bb; block != NULL;
4232 block = block->successor1) {
4233 block->flags &= ~BB_VISITED;
4238 *-----------------------------------------------------------------------------
4240 * AddBasicBlockRangeToErrorInfo --
4242 * Updates the error info of the Tcl interpreter to show a given basic
4243 * block in the code.
4245 * This procedure is used to label the callstack with source location
4246 * information when reporting an error in stack checking.
4248 *-----------------------------------------------------------------------------
4252 AddBasicBlockRangeToErrorInfo(
4253 AssemblyEnv* assemEnvPtr, /* Assembly environment */
4254 BasicBlock* bbPtr) /* Basic block in which the error is found */
4256 CompileEnv* envPtr = assemEnvPtr->envPtr;
4257 /* Compilation environment */
4258 Tcl_Interp* interp = (Tcl_Interp*) envPtr->iPtr;
4259 /* Tcl interpreter */
4260 Tcl_Obj* lineNo; /* Line number in the source */
4262 Tcl_AddErrorInfo(interp, "\n in assembly code between lines ");
4263 TclNewIntObj(lineNo, bbPtr->startLine);
4264 Tcl_IncrRefCount(lineNo);
4265 Tcl_AppendObjToErrorInfo(interp, lineNo);
4266 Tcl_AddErrorInfo(interp, " and ");
4267 if (bbPtr->successor1 != NULL) {
4268 Tcl_SetIntObj(lineNo, bbPtr->successor1->startLine);
4269 Tcl_AppendObjToErrorInfo(interp, lineNo);
4271 Tcl_AddErrorInfo(interp, "end of assembly code");
4273 Tcl_DecrRefCount(lineNo);
4277 *-----------------------------------------------------------------------------
4279 * DupAssembleCodeInternalRep --
4281 * Part of the Tcl object type implementation for Tcl assembly language
4282 * bytecode. We do not copy the bytecode internalrep. Instead, we return
4283 * without setting copyPtr->typePtr, so the copy is a plain string copy
4284 * of the assembly source, and if it is to be used as a compiled
4285 * expression, it will need to be reprocessed.
4287 * This makes sense, because with Tcl's copy-on-write practices, the
4288 * usual (only?) time Tcl_DuplicateObj() will be called is when the copy
4289 * is about to be modified, which would invalidate any copied bytecode
4290 * anyway. The only reason it might make sense to copy the bytecode is if
4291 * we had some modifying routines that operated directly on the internalrep,
4292 * as we do for lists and dicts.
4300 *-----------------------------------------------------------------------------
4304 DupAssembleCodeInternalRep(
4315 *-----------------------------------------------------------------------------
4317 * FreeAssembleCodeInternalRep --
4319 * Part of the Tcl object type implementation for Tcl expression
4320 * bytecode. Frees the storage allocated to hold the internal rep, unless
4321 * ref counts indicate bytecode execution is still in progress.
4327 * May free allocated memory. Leaves objPtr untyped.
4329 *-----------------------------------------------------------------------------
4333 FreeAssembleCodeInternalRep(
4336 ByteCode *codePtr = (ByteCode *)objPtr->internalRep.twoPtrValue.ptr1;
4338 if (codePtr->refCount-- <= 1) {
4339 TclCleanupByteCode(codePtr);
4341 objPtr->typePtr = NULL;