disassemble

Bytecode disassembly command, introduced in Tcl 8.5.

tcl::unsupported::disassemble proc procName
tcl::unsupported::disassemble lambda lambdaTerm
tcl::unsupported::disassemble script script
tcl::unsupported::disassemble method class method
tcl::unsupported::disassemble objmethod object method

Usage

Disassembles the bytecode of a procedure, lambda/apply term, general script, etc. and returns the disassembly. The format is formally undefined, but is really an exposure of the disassembler built in since 8.0 when compiling with the correct options and can probably be read by anyone who has ever read any assembly code without much difficulty. Understanding the bytecodes requires reading tclCompile.c and (possibly) tclExecute.c. Will not disassemble anything created by tbcload.

Notes

It's unfortunately not at all compatible with the assemble command; the formats are quite different (though the opcodes themselves are the same).

Not present in safe interpreters. This command was developed during the Fourteenth Annual Tcl/Tk Conference (2007).

See also dis2asm for a converter from disassemble output to assemble input.

Example Output/Use

% proc foo {x} {
   for {set y 0} {$y < $x} {incr y} {
      puts $x,$y
   }
}
% tcl::unsupported::disassemble proc foo
ByteCode 0x0x9a2ec58, refCt 1, epoch 1, interp 0x0x9a377d8 (epoch 1)
  Source "\n   for {set y 0} {$y < $x} {incr y} {\n      puts $x,$y"
  Cmds 4, src 61, inst 43, litObjs 4, aux 0, stkDepth 4, code/src 0.00
  Proc 0x0x9a48f70, refCt 1, args 1, compiled locals 2
      slot 0, scalar, arg, "x"
      slot 1, scalar, "y"
  Exception ranges 2, depth 1:
      0: level 0, loop, pc 7-18, continue 20, break 40
      1: level 0, loop, pc 20-31, continue -1, break 40
  Commands 4:
      1: pc 0-41, src 4-59         2: pc 0-3, src 9-15
      3: pc 7-18, src 45-54        4: pc 20-31, src 29-34
  Command 1: "for {set y 0} {$y < $x} {incr y} {\n      puts $x,$y\n   "
  Command 2: "set y 0"
    (0) push1 0         # "0"
    (2) storeScalar1 %v1        # var "y"
    (4) pop
    (5) jump1 +28       # pc 33
  Command 3: "puts $x,$y"
    (7) push1 1         # "puts"
    (9) loadScalar1 %v0         # var "x"
    (11) push1 2        # ","
    (13) loadScalar1 %v1        # var "y"
    (15) concat1 3
    (17) invokeStk1 2
    (19) pop
  Command 4: "incr y"
    (20) startCommand +12 1     # next cmd at pc 32
    (29) incrScalar1Imm %v1 +1  # var "y"
    (32) pop
    (33) loadScalar1 %v1        # var "y"
    (35) loadScalar1 %v0        # var "x"
    (37) lt
    (38) jumpTrue1 -31  # pc 7
    (40) push1 3        # ""
    (42) done

DKF: I'm working (currently it's experimental) on a disassembler that produces output suitable for being consumed by scripts. I'm preliminarily calling it getbytecode, though I'm not very happy with that name! With it, I can do this:

# inspired by http://blog.fairchild.dk/2013/06/automagical-asm-control-flow-arrow-annotation/

proc ::tcl::unsupported::controlflow {type args} {
    # Pass the arguments through to the underlying bytecode retrieval system
    set dis [uplevel 1 [list tcl::unsupported::getbytecode $type {*}$args]]

    set charmap {
        {} " "
        {d u} "\u2502"
        {l r} "\u2500"
        {r u} "\u2514"
        {d r} "\u250c"
        {d l r u} "\u253c"
        {l r x} "\u25ba"
        {l r u} "\u2534"
        {d l r} "\u252c"
    }

    # Build the collection of jumps
    set jumps {}
    set prefix {0 {} 1 {}}
    dict for {from inst} [dict get $dis instructions] {
        foreach argument [lrange $inst 1 end] {
            if {[regexp {^pc (\d+) \(.*\)$} $argument -> to]} {
                lappend jumps [list $from $to [llength $jumps]]
                dict set prefix [dict size $prefix] {}
            } elseif {[regexp {^\?(\d+)$} $argument -> auxIdx]} {
                set aux [lindex [dict get $dis auxiliary] $auxIdx]
                if {[dict get $aux name] eq "JumptableInfo"} {
                    # Doesn't scale very well...
                    dict for {str offset} [dict get $aux mapping] {
                        lappend jumps [list $from [expr {$from+$offset}] \
                                [llength $jumps]]
                        dict set prefix [dict size $prefix] {}
                    }
                }
            }
        }
    }
    set alen [string length $from]

    # Optimize the apparent depths of non-overlapping jumps/loops
    for {set pi -1;set i 0} {[set j $i] < [llength $jumps]} {incr i} {
        lassign [lindex $jumps $i] from to idx
        if {$idx <= $pi} continue {set pi $idx}
        set max [expr {max($from, $to)}]
        while {[incr j] < [llength $jumps]} {
            lassign [lindex $jumps $j] f2 t2 i2
            if {$i2 > $idx && $max < min($f2, $t2)} {
                lset jumps $j 2 $idx
                lassign [lindex $jumps $j] from to idx
                set max [expr {max($from, $to)}]
            }
        }
    }
    set indices [lsort -integer -unique [lmap j $jumps {lindex $j 2}]]
    for {set i [llength $jumps]} {[incr i -1] >= 0} {} {
        if {$i in $indices} continue
        for {set j 0} {$j < [llength $jumps]} {incr j} {
            if {[lindex $jumps $j 2] > $i} {
                lset jumps $j 2 [expr {[lindex $jumps $j 2] - 1}]
            }
        }
        dict unset prefix [expr {[dict size $prefix]-1}]
    }

    # Print the output
    dict for {addr inst} [dict get $dis instructions] {
        array set outl $prefix
        set len [dict size $prefix]
        foreach jump $jumps {
            lassign $jump from to i
            # Reverse the order; looks better
            set i [expr {$len - $i - 2}]
            if {$from == $addr} {
                lappend outl($i) [lindex "u d" [expr {$from<$to}]] "r"
                while {[incr i] < $len} {
                    lappend outl($i) "l" "r"
                }
            } elseif {$to == $addr} {
                lappend outl($i) [lindex "u d" [expr {$from>$to}]] "r"
                while {[incr i] < $len} {
                    lappend outl($i) "l" "r"
                }
                lappend outl([incr i -1]) "x"
            } elseif {$from<$addr && $to>$addr || $from>$addr && $to<$addr} {
                lappend outl($i) "u" "d"
            }
        }
        puts [format "%s %*d %s" [join [lmap n [dict keys $prefix] {
            dict get $charmap [lsort -unique $outl($n)]
        }] ""] $alen $addr $inst]
    }
}

Applied to itself, I get this output:

 Output
                0 push1 @0
                2 push1 @1
                4 push1 @2
                6 loadScalar1 %0
                8 list 2
               13 loadScalar1 %1
               15 listConcat
               16 invokeStk1 3
               18 storeScalar1 %2
               20 pop
               21 push1 @3
               23 storeScalar1 %3
               25 pop
               26 push1 @4
               28 storeScalar1 %4
               30 pop
               31 push1 @5
               33 storeScalar1 %5
               35 pop
               36 loadScalar1 %2
               38 push1 @6
               40 dictGet 1
               45 beginCatch4 0
               50 dictFirst %46
          ┌─   55 jumpTrue4 {pc 332 (277)}
  ┌───────┼►   60 storeScalar1 %6
  │       │    62 pop
  │       │    63 storeScalar1 %7
  │       │    65 pop
  │       │    66 loadScalar1 %7
  │       │    68 listRangeImm .1 .end
  │       │    77 storeScalar1 %47
  │       │    79 pop
  │       │    80 foreach_start4 ?0
  │┌──────┼►   85 foreach_step4 ?0
  ││     ┌┼─   90 jumpFalse4 {pc 310 (220)}
  ││     ││    95 push1 @7
  ││     ││    97 push1 @8
  ││     ││    99 loadScalar1 %11
  ││     ││   101 push1 @9
  ││     ││   103 push1 @10
  ││     ││   105 invokeStk1 5
  ││     ││   107 nop
  ││    ┌┼┼─  108 jumpFalse1 {pc 147 (39)}
  ││    │││   110 loadScalar1 %6
  ││    │││   112 loadScalar1 %12
  ││    │││   114 loadScalar1 %4
  ││    │││   116 listLength
  ││    │││   117 list 3
  ││    │││   122 lappendScalar1 %4
  ││    │││   124 pop
  ││    │││   125 push1 @11
  ││    │││   127 loadScalar1 %5
  ││    │││   129 invokeStk1 2
  ││    │││   131 push1 @4
  ││    │││   133 dictSet 1 %5
  ││   ┌┼┼┼─  142 jump4 {pc 301 (159)}
  ││   │└┼┼►  147 push1 @7
  ││   │ ││   149 push1 @12
  ││   │ ││   151 loadScalar1 %11
  ││   │ ││   153 push1 @9
  ││   │ ││   155 push1 @13
  ││   │ ││   157 invokeStk1 5
  ││   │ ││   159 nop
  ││   │┌┼┼─  160 jumpFalse4 {pc 299 (139)}
  ││   ││││   165 loadScalar1 %2
  ││   ││││   167 push1 @14
  ││   ││││   169 dictGet 1
  ││   ││││   174 loadScalar1 %14
  ││   ││││   176 listIndex
  ││   ││││   177 storeScalar1 %13
  ││   ││││   179 pop
  ││   ││││   180 loadScalar1 %13
  ││   ││││   182 push1 @15
  ││   ││││   184 dictGet 1
  ││   ││││   189 push1 @16
  ││   ││││   191 streq
  ││  ┌┼┼┼┼─  192 jumpFalse1 {pc 295 (103)}
  ││  │││││   194 loadScalar1 %13
  ││  │││││   196 push1 @17
  ││  │││││   198 dictGet 1
  ││  │││││   203 beginCatch4 3
  ││  │││││   208 dictFirst %49
  ││ ┌┼┼┼┼┼─  213 jumpTrue4 {pc 282 (69)}
  ││┌┼┼┼┼┼┼►  218 storeScalar1 %15
  │││││││││   220 pop
  │││││││││   221 storeScalar1 %16
  │││││││││   223 pop
  │││││││││   224 loadScalar1 %6
  │││││││││   226 loadScalar1 %6
  │││││││││   228 loadScalar1 %16
  │││││││││   230 add
  │││││││││   231 loadScalar1 %4
  │││││││││   233 listLength
  │││││││││   234 list 3
  │││││││││   239 lappendScalar1 %4
  │││││││││   241 pop
  │││││││││   242 push1 @11
  │││││││││   244 loadScalar1 %5
  │││││││││   246 invokeStk1 2
  │││││││││   248 push1 @4
  │││││││││   250 dictSet 1 %5
  │││││││││   259 pop
  │││││││││   260 dictNext %49
  ││└┼┼┼┼┼┼─  265 jumpFalse4 {pc 218 (-47)}
  ││┌┼┼┼┼┼┼─  270 jump1 {pc 282 (12)}
  │││││││││   272 pushReturnOpts
  │││││││││   273 pushResult
  │││││││││   274 endCatch
  │││││││││   275 unsetScalar 0 %49
  │││││││││   281 returnStk
  ││└┴┼┼┼┼┼►  282 pop
  ││  │││││   283 pop
  ││  │││││   284 endCatch
  ││  │││││   285 unsetScalar 0 %49
  ││  │││││   291 push1 @4
  ││ ┌┼┼┼┼┼─  293 jump1 {pc 301 (8)}
  ││ │└┼┼┼┼►  295 push1 @4
  ││ │┌┼┼┼┼─  297 jump1 {pc 301 (4)}
  ││ │││└┼┼►  299 push1 @4
  ││ └┴┴─┼┼►  301 pop
  │└─────┼┼─  302 jump4 {pc 85 (-217)}
  │      ││   307 nop
  │      ││   308 nop
  │      ││   309 nop
  │      └┼►  310 dictNext %46
  └───────┼─  315 jumpFalse4 {pc 60 (-255)}
         ┌┼─  320 jump1 {pc 332 (12)}
         ││   322 pushReturnOpts
         ││   323 pushResult
         ││   324 endCatch
         ││   325 unsetScalar 0 %46
         ││   331 returnStk
         └┴►  332 pop
              333 pop
              334 endCatch
              335 unsetScalar 0 %46
              341 nop
              342 nop
              343 nop
              344 loadScalar1 %6
              346 strlen
              347 storeScalar1 %18
              349 pop
              350 push1 @18
              352 storeScalar1 %19
              354 pop
              355 push1 @19
              357 storeScalar1 %20
              359 pop
          ┌─  360 jump4 {pc 606 (246)}
   ┌──────┼►  365 loadScalar1 %4
   │      │   367 loadScalar1 %20
   │      │   369 listIndex
   │      │   370 dup
   │      │   371 listIndexImm .0
   │      │   376 storeScalar1 %6
   │      │   378 pop
   │      │   379 dup
   │      │   380 listIndexImm .1
   │      │   385 storeScalar1 %12
   │      │   387 pop
   │      │   388 dup
   │      │   389 listIndexImm .2
   │      │   394 storeScalar1 %21
   │      │   396 pop
   │      │   397 listRangeImm .3 .end
   │      │   406 pop
   │      │   407 loadScalar1 %21
   │      │   409 loadScalar1 %19
   │      │   411 le
   │     ┌┼─  412 jumpFalse1 {pc 421 (9)}
   │    ┌┼┼─  414 jump4 {pc 602 (188)}
   │   ┌┼┼┼─  419 jump1 {pc 425 (6)}
   │   ││└┼►  421 loadScalar1 %21
   │   ││ │   423 storeScalar1 %19
   │   └┼─┼►  425 pop
   │    │ │   426 push1 @20
   │    │ │   428 loadScalar1 %6
   │    │ │   430 loadScalar1 %12
   │    │ │   432 invokeStk1 3
   │    │ │   434 tryCvtToNumeric
   │    │ │   435 storeScalar1 %22
   │    │ │   437 pop
   │    │┌┼─  438 jump4 {pc 587 (149)}
   │┌───┼┼┼►  443 loadScalar1 %4
   ││   │││   445 loadScalar1 %23
   ││   │││   447 listIndex
   ││   │││   448 dup
   ││   │││   449 listIndexImm .0
   ││   │││   454 storeScalar1 %24
   ││   │││   456 pop
   ││   │││   457 dup
   ││   │││   458 listIndexImm .1
   ││   │││   463 storeScalar1 %25
   ││   │││   465 pop
   ││   │││   466 dup
   ││   │││   467 listIndexImm .2
   ││   │││   472 storeScalar1 %26
   ││   │││   474 pop
   ││   │││   475 listRangeImm .3 .end
   ││   │││   484 pop
   ││   │││   485 loadScalar1 %26
   ││   │││   487 loadScalar1 %21
   ││   │││   489 gt
   ││  ┌┼┼┼─  490 jumpFalse1 {pc 509 (19)}
   ││  ││││   492 loadScalar1 %22
   ││  ││││   494 push1 @21
   ││  ││││   496 loadScalar1 %24
   ││  ││││   498 loadScalar1 %25
   ││  ││││   500 invokeStk1 3
   ││  ││││   502 lt
   ││ ┌┼┼┼┼─  503 jumpFalse1 {pc 509 (6)}
   ││ │││││   505 push1 @1
   ││┌┼┼┼┼┼─  507 jump1 {pc 511 (4)}
   │││└┴┼┼┼►  509 push1 @19
   ││└─┬┼┼┼►  511 jumpFalse1 {pc 584 (73)}
   ││  ││││   513 loadScalar1 %23
   ││  ││││   515 push1 @22
   ││  ││││   517 loadScalar1 %21
   ││  ││││   519 loadScalar1 %4
   ││  ││││   521 lsetFlat 4
   ││  ││││   526 storeScalar1 %4
   ││  ││││   528 pop
   ││  ││││   529 loadScalar1 %4
   ││  ││││   531 loadScalar1 %23
   ││  ││││   533 listIndex
   ││  ││││   534 dup
   ││  ││││   535 listIndexImm .0
   ││  ││││   540 storeScalar1 %6
   ││  ││││   542 pop
   ││  ││││   543 dup
   ││  ││││   544 listIndexImm .1
   ││  ││││   549 storeScalar1 %12
   ││  ││││   551 pop
   ││  ││││   552 dup
   ││  ││││   553 listIndexImm .2
   ││  ││││   558 storeScalar1 %21
   ││  ││││   560 pop
   ││  ││││   561 listRangeImm .3 .end
   ││  ││││   570 pop
   ││  ││││   571 push1 @20
   ││  ││││   573 loadScalar1 %6
   ││  ││││   575 loadScalar1 %12
   ││  ││││   577 invokeStk1 3
   ││  ││││   579 tryCvtToNumeric
   ││  ││││   580 storeScalar1 %22
   ││ ┌┼┼┼┼─  582 jump1 {pc 586 (4)}
   ││ │└┼┼┼►  584 push1 @4
   ││ └─┼┼┼►  586 pop
   ││   │└┼►  587 incrScalar1Imm %23 1
   ││   │ │   590 loadScalar1 %4
   ││   │ │   592 listLength
   ││   │ │   593 lt
   │└───┼─┼─  594 jumpTrue4 {pc 443 (-151)}
   │    │ │   599 nop
   │    │ │   600 nop
   │    │ │   601 nop
   │    └─┼►  602 incrScalar1Imm %20 1
   │      │   605 pop
   │      └►  606 loadScalar1 %20
   │          608 storeScalar1 %23
   │          610 loadScalar1 %4
   │          612 listLength
   │          613 lt
   └────────  614 jumpTrue4 {pc 365 (-249)}
              619 nop
              620 nop
              621 nop
              622 push1 @23
              624 push1 @24
              626 push1 @25
              628 loadScalar1 %4
              630 storeScalar1 %51
              632 pop
              633 push1 @4
              635 storeScalar1 %50
              637 pop
              638 foreach_start4 ?1
         ┌─►  643 foreach_step4 ?1
         │┌─  648 jumpFalse1 {pc 662 (14)}
         ││   650 loadScalar1 %23
         ││   652 listIndexImm .2
         ││   657 lappendScalar1 %50
         ││   659 pop
         └┼─  660 jump1 {pc 643 (-17)}
          └►  662 loadScalar1 %50
              664 unsetScalar 0 %50
              670 invokeStk1 4
              672 storeScalar1 %27
              674 pop
              675 loadScalar1 %4
              677 listLength
              678 storeScalar1 %20
              680 pop
          ┌─  681 jump1 {pc 792 (111)}
    ┌─────┼►  683 loadScalar1 %20
    │     │   685 loadScalar1 %27
    │     │   687 listIn
    │    ┌┼─  688 jumpFalse1 {pc 697 (9)}
    │   ┌┼┼─  690 jump4 {pc 792 (102)}
    │  ┌┼┼┼─  695 jump1 {pc 699 (4)}
    │  ││└┼►  697 push1 @4
    │  └┼─┼►  699 pop
    │   │ │   700 push1 @19
    │   │ │   702 storeScalar1 %23
    │   │ │   704 pop
    │   │┌┼─  705 jump1 {pc 759 (54)}
    │┌──┼┼┼►  707 loadScalar1 %4
    ││  │││   709 loadScalar1 %23
    ││  │││   711 push1 @22
    ││  │││   713 lindexMulti 3
    ││  │││   718 loadScalar1 %20
    ││  │││   720 gt
    ││ ┌┼┼┼─  721 jumpFalse1 {pc 752 (31)}
    ││ ││││   723 loadScalar1 %23
    ││ ││││   725 push1 @22
    ││ ││││   727 loadScalar1 %4
    ││ ││││   729 loadScalar1 %23
    ││ ││││   731 push1 @22
    ││ ││││   733 lindexMulti 3
    ││ ││││   738 push1 @1
    ││ ││││   740 sub
    ││ ││││   741 loadScalar1 %4
    ││ ││││   743 lsetFlat 4
    ││ ││││   748 storeScalar1 %4
    ││┌┼┼┼┼─  750 jump1 {pc 754 (4)}
    │││└┼┼┼►  752 push1 @4
    ││└─┼┼┼►  754 pop
    ││  │││   755 incrScalar1Imm %23 1
    ││  │││   758 pop
    ││  │└┼►  759 loadScalar1 %23
    ││  │ │   761 loadScalar1 %4
    ││  │ │   763 listLength
    ││  │ │   764 lt
    │└──┼─┼─  765 jumpTrue1 {pc 707 (-58)}
    │   │ │   767 nop
    │   │ │   768 nop
    │   │ │   769 nop
    │   │ │   770 push1 @11
    │   │ │   772 loadScalar1 %5
    │   │ │   774 invokeStk1 2
    │   │ │   776 push1 @1
    │   │ │   778 sub
    │   │ │   779 dictUnset 1 %5
    │   │ │   788 pop
    │   │ │   789 nop
    │   │ │   790 nop
    │   │ │   791 nop
    │   └─┴►  792 incrScalar1Imm %20 -1
    │         795 push1 @19
    │         797 ge
    └───────  798 jumpTrue1 {pc 683 (-115)}
              800 nop
              801 nop
              802 nop
              803 loadScalar1 %2
              805 push1 @6
              807 dictGet 1
              812 beginCatch4 13
              817 dictFirst %53
          ┌─  822 jumpTrue4 {pc 1294 (472)}
 ┌────────┼►  827 storeScalar1 %31
 │        │   829 pop
 │        │   830 storeScalar1 %7
 │        │   832 pop
 │        │   833 loadScalar1 %5
 │        │   835 dup
 │        │   836 listLength
 │        │   837 push1 @1
 │        │   839 bitand
 │       ┌┼─  840 jumpFalse1 {pc 855 (15)}
 │       ││   842 push1 @26
 │       ││   844 push1 @27
 │       ││   846 returnImm 1 0
 │       └┼►  855 storeScalar1 %54
 │        │   857 pop
 │        │   858 arrayExistsImm 33
 │       ┌┼─  863 jumpTrue1 {pc 870 (7)}
 │       ││   865 arrayMakeImm 33
 │       └┼►  870 foreach_start4 ?2
 │      ┌─┼►  875 foreach_step4 ?2
 │      │┌┼─  880 jumpFalse1 {pc 891 (11)}
 │      │││   882 loadScalar1 %56
 │      │││   884 loadScalar1 %57
 │      │││   886 storeArray1 %33
 │      │││   888 pop
 │      └┼┼─  889 jump1 {pc 875 (-14)}
 │       └┼►  891 unsetScalar 0 %54
 │        │   897 nop
 │        │   898 nop
 │        │   899 nop
 │        │   900 push1 @11
 │        │   902 loadScalar1 %5
 │        │   904 invokeStk1 2
 │        │   906 storeScalar1 %38
 │        │   908 pop
 │        │   909 loadScalar1 %4
 │        │   911 storeScalar1 %58
 │        │   913 pop
 │        │   914 foreach_start4 ?3
 │┌───────┼►  919 foreach_step4 ?3
 ││      ┌┼─  924 jumpFalse4 {pc 1193 (269)}
 ││      ││   929 loadScalar1 %41
 ││      ││   931 dup
 ││      ││   932 listIndexImm .0
 ││      ││   937 storeScalar1 %6
 ││      ││   939 pop
 ││      ││   940 dup
 ││      ││   941 listIndexImm .1
 ││      ││   946 storeScalar1 %12
 ││      ││   948 pop
 ││      ││   949 dup
 ││      ││   950 listIndexImm .2
 ││      ││   955 storeScalar1 %20
 ││      ││   957 pop
 ││      ││   958 listRangeImm .3 .end
 ││      ││   967 pop
 ││      ││   968 loadScalar1 %38
 ││      ││   970 loadScalar1 %20
 ││      ││   972 sub
 ││      ││   973 push1 @22
 ││      ││   975 sub
 ││      ││   976 storeScalar1 %20
 ││      ││   978 pop
 ││      ││   979 loadScalar1 %6
 ││      ││   981 loadScalar1 %31
 ││      ││   983 eq
 ││     ┌┼┼─  984 jumpFalse1 {pc 1043 (59)}
 ││     │││   986 push1 @28
 ││     │││   988 push1 @29
 ││     │││   990 loadScalar1 %20
 ││     │││   992 push1 @30
 ││     │││   994 concat1 3
 ││     │││   996 push1 @31
 ││     │││   998 loadScalar1 %6
 ││     │││  1000 loadScalar1 %12
 ││     │││  1002 lt
 ││     │││  1003 listIndex
 ││     │││  1004 push1 @32
 ││     │││  1006 invokeStk1 4
 ││     │││  1008 pop
 ││    ┌┼┼┼─ 1009 jump1 {pc 1028 (19)}
 ││   ┌┼┼┼┼► 1011 push1 @28
 ││   │││││  1013 push1 @29
 ││   │││││  1015 loadScalar1 %20
 ││   │││││  1017 push1 @30
 ││   │││││  1019 concat1 3
 ││   │││││  1021 push1 @33
 ││   │││││  1023 push1 @32
 ││   │││││  1025 invokeStk1 4
 ││   │││││  1027 pop
 ││   │└┼┼┼► 1028 incrScalar1Imm %20 1
 ││   │ │││  1031 loadScalar1 %38
 ││   │ │││  1033 lt
 ││   └─┼┼┼─ 1034 jumpTrue1 {pc 1011 (-23)}
 ││     │││  1036 push1 @4
 ││    ┌┼┼┼─ 1038 jump4 {pc 1184 (146)}
 ││    │└┼┼► 1043 loadScalar1 %12
 ││    │ ││  1045 loadScalar1 %31
 ││    │ ││  1047 eq
 ││    │┌┼┼─ 1048 jumpFalse1 {pc 1112 (64)}
 ││    ││││  1050 push1 @28
 ││    ││││  1052 push1 @29
 ││    ││││  1054 loadScalar1 %20
 ││    ││││  1056 push1 @30
 ││    ││││  1058 concat1 3
 ││    ││││  1060 push1 @31
 ││    ││││  1062 loadScalar1 %6
 ││    ││││  1064 loadScalar1 %12
 ││    ││││  1066 gt
 ││    ││││  1067 listIndex
 ││    ││││  1068 push1 @32
 ││    ││││  1070 invokeStk1 4
 ││    ││││  1072 pop
 ││   ┌┼┼┼┼─ 1073 jump1 {pc 1092 (19)}
 ││  ┌┼┼┼┼┼► 1075 push1 @28
 ││  ││││││  1077 push1 @29
 ││  ││││││  1079 loadScalar1 %20
 ││  ││││││  1081 push1 @30
 ││  ││││││  1083 concat1 3
 ││  ││││││  1085 push1 @33
 ││  ││││││  1087 push1 @32
 ││  ││││││  1089 invokeStk1 4
 ││  ││││││  1091 pop
 ││  │└┼┼┼┼► 1092 incrScalar1Imm %20 1
 ││  │ ││││  1095 loadScalar1 %38
 ││  │ ││││  1097 lt
 ││  └─┼┼┼┼─ 1098 jumpTrue1 {pc 1075 (-23)}
 ││    ││││  1100 nop
 ││    ││││  1101 nop
 ││    ││││  1102 nop
 ││    ││││  1103 incrScalar1Imm %20 -1
 ││    ││││  1106 push1 @34
 ││    ││││  1108 lappendArray1 %33
 ││   ┌┼┼┼┼─ 1110 jump1 {pc 1184 (74)}
 ││   ││└┼┼► 1112 loadScalar1 %6
 ││   ││ ││  1114 loadScalar1 %31
 ││   ││ ││  1116 lt
 ││   ││┌┼┼─ 1117 jumpFalse1 {pc 1130 (13)}
 ││   │││││  1119 loadScalar1 %12
 ││   │││││  1121 loadScalar1 %31
 ││   │││││  1123 gt
 ││  ┌┼┼┼┼┼─ 1124 jumpFalse1 {pc 1130 (6)}
 ││  ││││││  1126 push1 @1
 ││ ┌┼┼┼┼┼┼─ 1128 jump1 {pc 1132 (4)}
 ││ │└┼┼┴┼┼► 1130 push1 @19
 ││ └─┼┼┬┼┼► 1132 jumpTrue1 {pc 1160 (28)}
 ││   │││││  1134 loadScalar1 %6
 ││   │││││  1136 loadScalar1 %31
 ││   │││││  1138 gt
 ││  ┌┼┼┼┼┼─ 1139 jumpFalse1 {pc 1152 (13)}
 ││  ││││││  1141 loadScalar1 %12
 ││  ││││││  1143 loadScalar1 %31
 ││  ││││││  1145 lt
 ││ ┌┼┼┼┼┼┼─ 1146 jumpFalse1 {pc 1152 (6)}
 ││ │││││││  1148 push1 @1
 ││┌┼┼┼┼┼┼┼─ 1150 jump1 {pc 1154 (4)}
 │││└┴┼┼┼┼┼► 1152 push1 @19
 ││└─┬┼┼┼┼┼► 1154 jumpTrue1 {pc 1160 (6)}
 ││  ││││││  1156 push1 @19
 ││ ┌┼┼┼┼┼┼─ 1158 jump1 {pc 1162 (4)}
 ││ │└┼┼┴┼┼► 1160 push1 @1
 ││ └─┼┼┬┼┼► 1162 jumpFalse1 {pc 1182 (20)}
 ││   │││││  1164 push1 @28
 ││   │││││  1166 push1 @29
 ││   │││││  1168 loadScalar1 %20
 ││   │││││  1170 push1 @30
 ││   │││││  1172 concat1 3
 ││   │││││  1174 push1 @35
 ││   │││││  1176 push1 @36
 ││   │││││  1178 invokeStk1 4
 ││  ┌┼┼┼┼┼─ 1180 jump1 {pc 1184 (4)}
 ││  │││└┼┼► 1182 push1 @4
 ││  └┴┴─┼┼► 1184 pop
 │└──────┼┼─ 1185 jump4 {pc 919 (-266)}
 │       ││  1190 nop
 │       ││  1191 nop
 │       ││  1192 nop
 │       └┼► 1193 push1 @37
 │        │  1195 push1 @38
 │        │  1197 push1 @39
 │        │  1199 push1 @40
 │        │  1201 push1 @41
 │        │  1203 loadScalar1 %5
 │        │  1205 invokeStk1 2
 │        │  1207 storeScalar1 %61
 │        │  1209 pop
 │        │  1210 push1 @4
 │        │  1212 storeScalar1 %60
 │        │  1214 pop
 │        │  1215 foreach_start4 ?4
 │      ┌─┼► 1220 foreach_step4 ?4
 │      │┌┼─ 1225 jumpFalse1 {pc 1249 (24)}
 │      │││  1227 loadScalar1 %3
 │      │││  1229 push1 @23
 │      │││  1231 push1 @25
 │      │││  1233 loadScalar1 %45
 │      │││  1235 loadArray1 %33
 │      │││  123-7 invokeStk1 3
 │      │││  1239 dictGet 1
 │      │││  1244 lappendScalar1 %60
 │      │││  1246 pop
 │      └┼┼─ 1247 jump1 {pc 1220 (-27)}
 │       └┼► 1249 loadScalar1 %60
 │        │  1251 unsetScalar 0 %60
 │        │  1257 push1 @4
 │        │  1259 invokeStk1 3
 │        │  1261 loadScalar1 %18
 │        │  1263 loadScalar1 %31
 │        │  1265 loadScalar1 %7
 │        │  1267 invokeStk1 6
 │        │  1269 invokeStk1 2
 │        │  1271 pop
 │        │  1272 dictNext %53
 └────────┼─ 1277 jumpFalse4 {pc 827 (-450)}
         ┌┼─ 1282 jump1 {pc 1294 (12)}
         ││  1284 pushReturnOpts
         ││  1285 pushResult
         ││  1286 endCatch
         ││  1287 unsetScalar 0 %53
         ││  1293 returnStk
         └┴► 1294 pop
             1295 pop
             1296 endCatch
             1297 unsetScalar 0 %53
             1303 push1 @4
             1305 done

RS 2013-12-03 - Could the following be a bug? (Note the space between unsetScalar and 1):

suchenwi@suchenwi-NC10:~$ tclkit
% proc f {} {set x 1; unset x}
% tcl::unsupported::disassemble proc f
ByteCode 0x0x93fc5b8, refCt 1, epoch 15, interp 0x0x936c7a0 (epoch 15)
  Source "set x 1; unset x"
  Cmds 2, src 16, inst 14, litObjs 2, aux 0, stkDepth 1, code/src 0.00
  Proc 0x0x9401ec0, refCt 1, args 0, compiled locals 1
      slot 0, scalar, "x"
  Commands 2:
      1: pc 0-4, src 0-6        2: pc 5-12, src 9-15
  Command 1: "set x 1"
    (0) push1 0         # "1"
    (2) storeScalar1 %v0         # var "x"
    (4) pop 
  Command 2: "unset x"
    (5) unsetScalar 1 %v0         # var "x"               <---------------------- here
    (11) push1 1         # ""
    (13) done 

% info pa
8.6.1

AMG: What's going on with PC 47 until 61? What are those jumps and pops for?

proc ftw_9 {{dirs .}} {
    while {[llength $dirs]} {
        set dirs [concat [lassign $dirs name] [glob -nocomplain -directory $name -type d *]]
    }
}
tcl::unsupported::disassemble proc ftw_9
ByteCode 0x0x9b81f0, refCt 1, epoch 15, interp 0x0x94dac0 (epoch 15)
  Source "\n    while {[llength $dirs]} {\n        set dirs [conc"...
  Cmds 6, src 130, inst 86, litObjs 7, aux 0, stkDepth 8, code/src 0.00
  Proc 0x0xa0e060, refCt 1, args 1, compiled locals 2
      slot 0, scalar, arg, "dirs"
      slot 1, scalar, "name"
  Exception ranges 2, depth 2:
      0: level 0, loop, pc 2-67, continue 69, break 83
      1: level 1, loop, pc 45-46, continue 55, break 49
  Commands 6:
      1: pc 0-84, src 5-128        2: pc 2-67, src 39-122
      3: pc 11-65, src 49-121        4: pc 11-30, src 57-74
      5: pc 31-60, src 78-120        6: pc 69-80, src 13-25
  Command 1: "while {[llength $dirs]} {\n        set dirs [concat [la"...
    (0) jump1 +69       # pc 69
  Command 2: "set dirs [concat [lassign $dirs name] [glob -nocomplain"...
    (2) startCommand +66 3      # next cmd at pc 68, 3 cmds start here
  Command 3: "concat [lassign $dirs name] [glob -nocomplain -director"...
  Command 4: "lassign $dirs name"...
    (11) loadScalar1 %v0        # var "dirs"
    (13) dup
    (14) listIndexImm 0
    (19) storeScalar1 %v1       # var "name"
    (21) pop
    (22) listRangeImm 1 end
  Command 5: "glob -nocomplain -directory $name -type d *"...
    (31) push1 0        # "glob"
    (33) push1 1        # "-nocomplain"
    (35) push1 2        # "-directory"
    (37) loadScalar1 %v1        # var "name"
    (39) push1 3        # "-type"
    (41) push1 4        # "d"
    (43) push1 5        # "*"
    (45) invokeStk1 7
    (47) jump1 +14      # pc 61
    (49) pop
    (50) jump4 +33      # pc 83
    (55) pop
    (56) jump4 +13      # pc 69
    (61) concatStk 2
    (66) storeScalar1 %v0       # var "dirs"
    (68) pop
  Command 6: "llength $dirs"...
    (69) startCommand +12 1     # next cmd at pc 81
    (78) loadScalar1 %v0        # var "dirs"
    (80) listLength
    (81) jumpTrue1 -79  # pc 2
    (83) push1 6        # ""
    (85) done

DKF: Those are exception handlers that convert exceptions thrown from the invokeStk1 (remember, it doesn't know that it won't) into jumps to the correct way to handle the break (start at (49)) or the continue (start at (55)). This is defined by exception range #1.