[Richard Suchenwirth] 2013-11-30 - Another chapter in the [dis2asm] saga: a converter from the language produced by the tcl::unsupported::'''disassemble''' command to the language (briefly "dis") expected by the tcl::unsupported::'''assemble''' command, also known as [TAL] (Tcl Assembly Language). Both are varieties of [assembler] for Tcl's [bytecode] engine, but there are also a number of variations between them, which [dis2asm] tries to fix. One remaining issue on the [dis2asm] page was that a "done" instruction (with a semantics like [return], but not available in TAL, so so far I just skipped it) produced bad code if not at its end. Example (the "done" in question is at position (9)): ====== % aproc f x {if {$x > 0} {return 1} else {return 0}} -x proc f x {asm { load x ;# (0) loadScalar1 %v0 # var "x" push 0 ;# (2) push1 0 # "0" gt ;# (4) gt jumpFalse L12 ;# (5) jumpFalse1 +7 # pc 12 push 1 ;# (7) push1 1 # "1" ;# (9) done ;# (10) nop ;# (11) nop label L12; push 0 ;# (12) push1 0 # "0" ;# (14) done ;# (15) done }} % f 2 inconsistent stack depths on two execution paths ====== I had already proposed a fix for this on [dis2asm]: convert "done" to "jump Done" when not at or near the end of the input, and add a "label Done" at the very end of the output. The TAL output now runs well, and looks like ====== % aproc f x {if {$x > 0} {return 1} else {return 0}} -x proc f x {asm { load x ;# (0) loadScalar1 %v0 # var "x" push 0 ;# (2) push1 0 # "0" gt ;# (4) gt jumpFalse L12 ;# (5) jumpFalse1 +7 # pc 12 push 1 ;# (7) push1 1 # "1" jump Done ;# (9) done ;# (10) nop ;# (11) nop label L12; push 0 ;# (12) push1 0 # "0" ;# (14) done ;# (15) done label Done; }} % f 42 1 % f -42 0 ====== So here is the latest version of [dis2asm] that gets things done: ====== proc dis2asm body { set fstart " push -1; store @p; pop " set fstep " incrImm @p +1;load @l;load @p listIndex;store @i;pop load @l;listLength;lt " set res "" set wait "" set jumptargets {} set lines [split $body \n] foreach line $lines { ;#-- pass 1: collect jump targets if [regexp {\# pc (\d+)} $line -> pc] {lappend jumptargets $pc} } set lineno 0 foreach line $lines { ;#-- pass 2: do the rest incr lineno set line [string trim $line] if {$line eq ""} continue set code "" if {[regexp {slot (\d+), (.+)} $line -> number descr]} { set slot($number) $descr } elseif {[regexp {data=.+loop=%v(\d+)} $line -> ptr]} { #got ptr, carry on } elseif {[regexp {it%v(\d+).+\[%v(\d+)\]} $line -> copy number]} { set loopvar [lindex $slot($number) end] if {$wait ne ""} { set map [list @p $ptr @i $loopvar @l $copy] set code [string map $map $fstart] append res "\n $code ;# $wait" set wait "" } } elseif {[regexp {\((\d+)\) (.+)} $line -> pc instr]} { if {$pc in $jumptargets} {append res "\n label L$pc;"} if {[regexp {(.+)#(.+)} $instr -> instr comment]} { set arg [list [lindex $comment end]] if [string match jump* $instr] {set arg L$arg} } else {set arg ""} set instr0 [normalize [lindex $instr 0]] switch -- $instr0 { concat - invokeStk {set arg [lindex $instr end]} incrImm {set arg [list $arg [lindex $instr end]]} } set code "$instr0 $arg" switch -- $instr0 { done { if {$lineno < [llength $lines]-2} { set code "jump Done" } else {set code ""} } startCommand {set code ""} foreach_start {set wait $line; continue} foreach_step {set code [string map $map $fstep]} } append res "\n [format %-24s $code] ;# $line" } } append res "\n label Done;\n" return $res } ====== <>Example