ADDED quadcode/builder.tcl Index: quadcode/builder.tcl ================================================================== --- /dev/null +++ quadcode/builder.tcl @@ -0,0 +1,147 @@ +# builder.tcl -- +# +# Class that allows for building new quadcode inside a +# quadcode::transformer. +# +# Copyright (c) 2018 by Kevin B. Kenny +# +# See the file "license.terms" for information on usage and redistribution +# of this file, and for a DISCLAIMER OF ALL WARRANTIES. +# +# Some passes that modify quadcode do fairly extensive code rewriting, and +# it's convenient have procedures to track these and allow access at a level +# closer to an 'assembly language'. This class contains convenience methods +# to allow automatic tracking of variable uses and defs, named temporaries, +# and named basic blocks. +# +#------------------------------------------------------------------------------ + +# quadcode::builder -- +# +# Class with support methods for building quadcode. +# +oo::class create quadcode::builder { + + variable xfmr; # quadcode::transformer object containing + ; # the quadcode of interest. + + variable b; # Basic block number under construction + + variable bb; # Content of the basic block under construction + + variable bbindex; # Dictionary whose keys are the names of + ; # basic blocks and whose values are the + ; # basic block numbers. + + variable varindex; # Dictionary whose keys are named variables + ; # and whose values are the SSA names of the + ; # variables. +} + +# quadcode::builder constructor -- +# +# Makes a quadcode::builder to allow for assembling quadcode. +# +# Parameters: +# xfmr - quadcode::transformer object containing the subprogram +# b - Basic block number under construction +# bb - Basic block content of the block under construction + +oo::define quadcode::builder constructor {xfmr_ b_ bb_} { + set xfmr $xfmr_ + set b $b_ + set bb $bb_ + set bbindex {} + set varindex {} +} + +# quadcode::builder maketemp -- +# +# Makes a temporary variable. +# +# Parameters: +# name - Base name for the temporary. +# +# Results: +# Returns the name of the constructed temporary. +# +# Side effects: +# Stores the name as the most recent instance of the temporary. + +oo::define quadcode::builder method maketemp {name} { + set ssaname [$xfmr newVarInstance [list temp @$name]] + dict set varindex $name $ssaname + return $ssaname +} + +# quadcode::builder method emit -- +# +# Emits an instruction into the basic block under construction. +# +# Parameters: +# q - Quadcode instruction to emit +# +# Results: +# None. +# +# Side effects: +# Appends the instruction to the basic bnlock under construction. +# If the instruction is a jump, adds the basic block predecessor +# relationship. If the instruction is an assignment, updates the +# ud-chain of the assigned variable. Updates the du-chains of all +# variables used oin the instruction. + +oo::define quadcode::builder method emit {q} { + + # Split the instruction + lassign $q opcode res argl + + # Handle the result + switch -exact -- [lindex $res 0] { + "bb" { + + # Instruction is a jump, link the basic block to the jump target + $xfmr bblink $b [lindex $res 1] + } + "temp" - "var" { + + # Instrtuction is an assignment, update the ud-chain. + dict set udchain $res $b + } + } + + # Handle the arguments + foreach arg [lrange $q 2 end] { + switch -exact -- [lindex $arg 0] { + "temp" - "var" { + + # Argument is an SSA value, update the du-chain. + $xfmr addUse $arg $b + } + } + } + + # Add the instruction to the block + lappend bb $q + + return +} + +# quadcode::builder method bb -- +# +# Returns the content of the basic block under construction. +# +# Results: +# Returns the instructions. + +oo::define quadcode::builder method bb {} { + return $bb +} + +# Local Variables: +# mode: tcl +# fill-column: 78 +# auto-fill-function: nil +# buffer-file-coding-system: utf-8-unix +# indent-tabs-mode: nil +# End: Index: quadcode/duchain.tcl ================================================================== --- quadcode/duchain.tcl +++ quadcode/duchain.tcl @@ -426,11 +426,11 @@ } } set bb [lindex $bbcontent $b] lset bbcontent $b {} - lset bbcontent $b [linsert $bb[unset -nocomplain bb] $pc $q] + lset bbcontent $b [linsert $bb[set bb {}] $pc $q] return } # quadcode::transformer method audit-duchain -- # Index: quadcode/invoke.tcl ================================================================== --- quadcode/invoke.tcl +++ quadcode/invoke.tcl @@ -29,10 +29,13 @@ # pc0 - Program counter of the start of the invocation sequence. $pc0 <= $pc # q - The 'invoke' instruction itself # cmd - The command being invoked # argl - The arglist from the 'invoke' instruction # cfin - The callframe that flows into the invocation sequence, or Nothing + # cfin_invoke - The callframe that is input to the 'invoke' or + # 'invokeExpanded' instruction, or Nothing. + # res_invoke - The result of 'invoke' or 'invokeExpanded' # cfout - The callframe that flows out of the invocation sequence, or # {} # invars - Dictionary whose keys are literal variable names and whose # values are the sources of variables that need to be copied # to the callframe prior to invocation @@ -41,11 +44,11 @@ # whose values are the quadcode values that need to be # assigned from the callframe after the invocation # errexit - Basic block number to jump to on error exit # normexit - Basic block number to jump to on normal exit - variable xfmr b pc q cmd argl \ + variable xfmr b pc q cmd res_invoke cfin_invoke argl \ pc0 cfin invars retval cfout outvars errexit normexit constructor {} { # Defer construction to an initialization method to avoid throwing # constructor errors. @@ -77,24 +80,24 @@ set bb [$xfmr getBasicBlock $b] set q [lindex $bb $pc] # Take apart the invocation - set argl [lassign $q op cfo_invoke cfi_invoke cmd] + set argl [lassign $q op res_invoke cfin_invoke cmd] if {$op ni {"invoke" "invokeExpanded"}} { error "cannot analyze: not an invocation." } # Find the input callframe and relevant input variables set pc0 $pc set cfin Nothing set invars {} - if {$cfi_invoke ne "Nothing"} { + if {$cfin_invoke ne "Nothing"} { set qb [lindex $bb [expr {$pc-1}]] if {[lindex $qb 0] eq "moveToCallFrame"} { - if {[lindex $qb 1] ne $cfi_invoke} { + if {[lindex $qb 1] ne $cfin_invoke} { error "cannot analyze: moveToCallFrame mislinked" } set varl [lassign $qb - - cfin] foreach {namelit source} $varl { if {[lindex $namelit 0] ne "literal"} { @@ -106,28 +109,28 @@ } } # Find the result value - set retval $cfo_invoke + set retval $res_invoke if {[lindex $bb [incr pc] 0] eq "retrieveResult"} { set q2 [lindex $bb $pc] lassign $q2 - retval cf2 - if {$cf2 ne $cfo_invoke} { + if {$cf2 ne $res_invoke} { error "cannot analyze: retrieveResult mislinked" } } else { incr pc -1 } # Find the output callframe - set cfout $cfo_invoke + set cfout $res_invoke if {[lindex $bb [incr pc] 0] eq "extractCallFrame"} { set q2 [lindex $bb $pc] lassign $q2 - cfout cf2 - if {$cf2 ne $cfo_invoke} { + if {$cf2 ne $res_invoke} { error "cannot analyze: extractCallFrame mislinked" } } else { incr pc -1 } @@ -171,25 +174,86 @@ } return } +# quadcode::invocationSequence method arginfo -- +# +# Queries [info args] for the invoked command. +# +# Results: +# Returns an ordered pair consisting of {1 result} if the args +# are known, or {0 {}} otherwise. The value of 'result' in the +# ordered pair is the result of [info args] for the given command. + +oo::define quadcode::invocationSequence method arginfo {} { + lassign [my cmd] status cmdName + if {!$status + || [catch {info args $cmdName} arginfo]} { + return {0 {}} + } + return [list 1 $arginfo] +} + +# quadcode::invocationSequence method b -- +# +# Returns the basic block number of an invocation sequence + +oo::define quadcode::invocationSequence method b {} { + return $b +} + # quadcode::invocationSequence method cfin -- # # Returns the starting callframe for an invocation sequence oo::define quadcode::invocationSequence method cfin {} { return $cfin } + +# quadcode::invocationSequence method cfin_invoke -- +# +# Returns the callframe from the 'invoke' instruction in an +# invocation sequence + +oo::define quadcode::invocationSequence method cfin_invoke {} { + return $cfin_invoke +} # quadcode::invocationSequence method cfout -- # # Returns the ending callframe for an invocation sequence oo::define quadcode::invocationSequence method cfout {} { return $cfout } + +# quadcode::invocationSequence method cmd -- +# +# Queries the name of the invoked command. +# +# Results: +# +# Returns an ordered pair that is {1 commandName} if the sequence +# invokes a known command, and {0 {}} if the sequence does not +# invoke a known command. + +oo::define quadcode::invocationSequence method cmd {} { + if {[lindex $cmd 0] eq "literal"} { + return [list 1 [lindex $cmd 1]] + } else { + return {0 {}} + } +} + +# quadcode::invocation sequence method argl -- +# +# Returns the argument list of the invoked command. + +oo::define quadcode::invocationSequence method argl {} { + return $argl +} # quadcode::invocationSequence method errexit -- # # Returns the error exit block number for an invocation sequence @@ -226,14 +290,24 @@ # Returns the starting program counter for an invocation sequence oo::define quadcode::invocationSequence method pc0 {} { return $pc0 } + +# quadcode::invocationSequence method res_invoke -- +# +# Returns the result of the 'invoke' or 'invokeExpanded' in +# an invocation sequence + +oo::define quadcode::invocationSequence method res_invoke {} { + return $res_invoke +} # quadcode::invocationSequence method retval -- # # Returns the return value for an invocation sequence oo::define quadcode::invocationSequence method retval {} { return $retval } + Index: quadcode/narrow.tcl ================================================================== --- quadcode/narrow.tcl +++ quadcode/narrow.tcl @@ -18,30 +18,25 @@ # Results: # None. # # Preconditions: # -# The program must be in SSA form, and the DJ graph (bbidom, bbkids, -# bbnlevels, bblevel) must be accurate. ud- and du-chains must be -# present. +# The program must be deconstructed from SSA form. ud- and du-chains +# must reflect the state of the program before deconstruction. One +# round of copy propagation must have been done. # # Side effects: # # Wherever a conditional branch tests the data type or existence of # a value, narrowing instructions are inserted in the quadcode to # mark that the value is of the required type. # -# The ud- and du-chains are updated as the narrowing instructions -# are inserted, and phi instructions for the controlled variables -# are inserted on the iterated dominance frontier. The phi's may -# turn out to be useless, in which case a subsequent 'uselessphis' -# pass will clean them up. -# # All of the operations introduced in this pass consist of introducing # new quads of the form -# v2 := some_narrowing_operation(v1) -# where v2 is a new variable instance, followed by fixup to the SSA diagram. +# v := some_narrowing_operation(v) +# where v is the value being tested (possibly indirectly) by the conditional +# jump. oo::define quadcode::transformer method narrow {} { upvar #0 quadcode::dataType::IMPURE IMPURE my debug-narrow { @@ -75,15 +70,22 @@ set falseBranch [lindex $q 1 1] set trueBranch [lindex $bb end 1 1] } # These operations may narrow if the defining instruction - # is 'exists' or 'instanceOf' + # is 'arrayExists', 'exists' or 'instanceOf' # The assignment appears at 'dpc' within basic block 'dbb' # and consists of the quadcode statement 'dquad'. - lassign [my findDef [lindex $q 2]] dbb dpc dquad + if {[catch { + + # Finding the definition will throw an error at a phi. + # The error can be ignored, because phi is not 'arrayExists' + # 'exists' or 'instanceOf'. + my findDef [lindex $q 2] + } result]} continue + lassign $result dbb dpc dquad set dop [lindex $dquad 0 0] switch -exact -- $dop { arrayExists { @@ -91,20 +93,30 @@ if {[lindex $dvar 0] ni {var temp}} continue my insertQuad $trueBranch 0 \ [list extractArray $dvar $dvar] my insertQuad $falseBranch 0 \ [list extractScalar $dvar $dvar] - my narrow_repairSSA $dvar $trueBranch $falseBranch + my debug-narrow { + puts "$trueBranch:0:\ + [lindex $bbcontent $trueBranch 0]" + puts "$falseBranch:0:\ + [lindex $bbcontent $falseBranch 0]" + } } exists { set dvar [lindex $dquad 2] if {[lindex $dvar 0] ni {var temp}} continue my insertQuad $trueBranch 0 \ [list extractExists $dvar $dvar] my insertQuad $falseBranch 0 \ [list unset $dvar] - my narrow_repairSSA $dvar $trueBranch $falseBranch + my debug-narrow { + puts "$trueBranch:0:\ + [lindex $bbcontent $trueBranch 0]" + puts "$falseBranch:0:\ + [lindex $bbcontent $falseBranch 0]" + } } instanceOf { set typecode [lindex $dquad 0 1] set typename [lindex $dquad 0 2] @@ -118,11 +130,16 @@ [list [list narrowToType $typecode $typename] \ $dvar $dvar] my insertQuad $falseBranch 0 \ [list [list narrowToType $nottype $notname] \ $dvar $dvar] - my narrow_repairSSA $dvar $trueBranch $falseBranch + my debug-narrow { + puts "$trueBranch:0:\ + [lindex $bbcontent $trueBranch 0]" + puts "$falseBranch:0:\ + [lindex $bbcontent $falseBranch 0]" + } } } } jumpMaybe { @@ -135,45 +152,19 @@ puts " $okBranch:0: [list copy $var $var]" puts " $failBranch:0: [list extractFail $var $var]" } my insertQuad $okBranch 0 [list copy $var $var] my insertQuad $failBranch 0 [list extractFail $var $var] - my narrow_repairSSA $var $okBranch $failBranch } } } my debug-narrow { puts "after inserting narrowing operations:" my dump-bb } } - -# quadcode::transformer method narrow_repairSSA -- -# -# Repairs the SSA property after introducing a narrowing operation -# on a variable. -# -# Parameters: -# v - Variable that has been narrowed and now has duplicate assignments -# b1 - First block containing a new assignment to v -# b2 - Second block containing a new assignment to v -# -# Results: -# None. -# -# Side effects: -# The SSA property is restored by giving new names to the assignments to -# $v, and updating the uses, possibly introducing new phi operations. - -oo::define quadcode::transformer method narrow_repairSSA {v b1 b2} { - set d [dict create [dict get $udchain $v] 1] - dict incr d $b1 - dict incr d $b2 - - my repairSSAVariable $v $d -} # quadcode::transformer method cleanupNarrow -- # # Removes narrowing instructions that are no longer relevant # Index: quadcode/transformer.tcl ================================================================== --- quadcode/transformer.tcl +++ quadcode/transformer.tcl @@ -326,12 +326,19 @@ varargs deadbb bbidom bblevel rewriteParamChecks + deconstructSSA narrow + ssa + ud_du_chain + copyprop } { + my debug-transform { + puts "Run: $pass" + } lappend timings $pass [lindex [time [list my $pass]] 0] my debug-audit { my audit-duchain $pass my audit-phis $pass } @@ -766,10 +773,11 @@ source [file join $quadcode::libdir types.tcl] source [file join $quadcode::libdir abbreviate.tcl] source [file join $quadcode::libdir aliases.tcl] source [file join $quadcode::libdir bb.tcl] +source [file join $quadcode::libdir builder.tcl] source [file join $quadcode::libdir bytecode.tcl] source [file join $quadcode::libdir callframe.tcl] source [file join $quadcode::libdir constfold.tcl] source [file join $quadcode::libdir constjump.tcl] source [file join $quadcode::libdir copyprop.tcl] Index: quadcode/varargs.tcl ================================================================== --- quadcode/varargs.tcl +++ quadcode/varargs.tcl @@ -61,20 +61,17 @@ incr pc # At this point in optimization, all invokes are part of a # sequence that is followed within a few instructions by a # jumpMaybe, so there can never be more than one in a basic - # block. Since rewriting invocations can peform major surgery - # on the program, simply call out to the appropriate handling - # routine and 'break' to the next basic block. - + # block. switch -exact [lindex $q 0] { "invoke" - "invokeExpanded" { my debug-varargs { puts "varargs: examine $b:$pc: $q" } - my varargsRewriteInvoke $b $pc $q + my va_RewriteInvoke $b $pc $q break } } } } @@ -84,11 +81,11 @@ my dump-bb } } -# quadcode::transformer method varargsRewriteInvoke -- +# quadcode::transformer method va_RewriteInvoke -- # # Rewrites 'invoke' and 'invokeExpanded' instructions to accommodate # compiled procs that accept variable numbers of arguments without going # through a call thunk or losing data type information. # @@ -101,199 +98,123 @@ # None. # # Side effects: # Rewrites the instruction and 'expand' instructions that it # uses. Updates ud- and du-chains. -# -# We actually have to work on a whole code burst here. What we need to -# consider is a sequence like -# -# (may be omitted) moveToCallFrame cf1 cf0 name0 var0 name1 var1 ... -# invokeExpanded res0 cf1 command args... -# (may be omitted) retrieveResult res1 res0 -# (may be omitted) extractCallframe cf2 res0 -# (zero or more) moveFromCallFrame var cf2 name -# jumpMaybe catchHandler res1 -# jump normalReturn -# -# The reason is that there is a considerable amount of logic in -# optimization and code generation that isn't prepared to have -# a moveFromCallFrame's callframe argument be the result of a phi. -# (There are places where the optimizer needs to track moveFromCallFrame -# back to a unique 'invoke' or other callframe-altering operation). -# -# What we want the result to look like: -# -# (... code to unpack arguments. Normal exit is bbNormal. Wrong -# number of args exit is bbWrong.) -# -# bbNormal: -# moveToCallFrame cf3 cf0 name0 var0 name1 var1 ... -# invoke res2 cf3 command args ... -# retrieveResult res3 res2 -# extractCallframe cf4 res3 -# moveFromCallFrame var' cf4 name (zero or more) -# jumpMaybe bb0 res3 -# jump bb1 -# -# bbWrong: -# (no need for moveToCallFrame) -# invokeExpanded res4 Nothing command originalArgs... -# (no need for callframe manipulation) -# jumpMaybe bb0 res4 -# jump bb1 (this jump is never taken, the 'invokeExpanded' always errors -# out in this case). -# -# bb0: -# cf2* = phi(cf4 [bbNormal], cf0 [bbWrong]) -# res1* = phi(res3 [bbNormal], res4 [bbWrong]) -# zero or more: -# var* = phi(var' [bbnormal], var' reaching def [bbWrong]) -# jump catchHandler -# -# bb1: -# cf2** = phi(cf4 [bbNormal], cf0 [bbWrong]) -# res1** = phi(res3 [bbNormal], res4 [bbWrong]) -# zero or more: -# var** = phi(var' [bbnormal], var' reaching def [bbWrong]) -# jump normalReturn -# -# Then, cf2*/cf2**, res1*/res1** and all instances of var*/var** become and -# are treated as duplicate definitions for repairSSAVariable. -# -# Note that the reaching definition of each variable in 'moveFromCallFrame' -# is easily obtained, because it has to be in the 'moveToCallFrame' that -# precedes the 'invokeExpanded'. - -oo::define quadcode::transformer method varargsRewriteInvoke {b pc q} { - - set newqds {} - - # Take apart the quad - set argv [lassign $q opcode cfout cfin calleeLit] - - # We care only about 'invokeExpanded' operations where the procedure - # name is known a priori, the expected args are known, and - # the target procedure is compiled. - - # TODO: We also care about {*} expansion passed to non-variadic - # Core commands. That will give us information about - # their stack effects. - - if {[lindex $calleeLit 0] ne "literal" - || [catch { - set callee [lindex $calleeLit 1] - info args $callee - } arginfo] - || ![$specializer compiling $callee]} { - - return - } - + +oo::define quadcode::transformer method va_RewriteInvoke {b pc q} { + + # Analyze the invocation sequence. This codeburst will run from the + # 'moveToCallFrame' preceding the invocation out to the end of the + # basic block. We will be rewriting it. + set call [::quadcode::invocationSequence new] + $call analyze [self] $b $pc + + # We can process only those sequences where the procedure name is known + # a priori, the expected arguments are known, and the target procedure + # is compiled. BUG - We know the arguments to a great many Core commands + # and need to work with them as well. + lassign [$call arginfo] status arginfo + if {!$status} return my debug-varargs { puts "[my full-name]: $b:$pc: $q" - } - - # Analyze the codeburst that carries out the 'invokeExpanded'. - # This codeburst will run from the 'moveToCallFrame' preceding - # the invocation out to the end of the basic block. - # We will be rewriting it. - - set call [::quadcode::invocationSequence new] - trace add variable call unset [list $call destroy] - $call analyze [self] $b $pc + puts " arginfo = $arginfo" + } # We are going to be doing major surgery on the basic block. # Remove the 'invokeExpanded' and all following instructions # from the block. Unlink the block from its successors, and # remove ud- and du-chaining for the removed instructions. - - set bb [my varargsUnlinkTail $b [$call pc0]] - - # Create the basic blocks for the actual invocation sequences. We make - # them in advance to avoid backpatching. - # Blocks 'err0b', 'norm0b', 'err1b' and 'norm1b' will be empty and are - # present in order to split critical edges. - - set norm0b [llength $bbcontent] - lappend bbcontent {}; lappend bbpred {} - set err0b [llength $bbcontent] - lappend bbcontent {}; lappend bbpred {} - set notokb [llength $bbcontent] - lappend bbcontent {}; lappend bbpred {} - set norm1b [llength $bbcontent] - lappend bbcontent {}; lappend bbpred {} - set err1b [llength $bbcontent] - lappend bbcontent {}; lappend bbpred {} - set normb [llength $bbcontent] - lappend bbcontent {}; lappend bbpred {} - set normphis {} - set errorb [llength $bbcontent] - lappend bbcontent {}; lappend bbpred {} - set errorphis {} - - # Create the first part of the 'invoke' instruction - set invokeres [my newVarInstance $cfin] - set newq [list invoke $invokeres $cfin $calleeLit] - - # Generate code for the 'wrong # args' case - set notokbb {} - set invexpres [my newVarInstance [$call retval]] - foreach qq [my varargsEmitWrongArgs $invexpres {} Nothing $calleeLit] { - my varargsEmitAndTrack $notokb notokbb $qq - } - dict set normphis [$call retval] [list bb $norm1b] $invexpres - dict set errorphis [$call retval] [list bb $err1b] $invexpres - dict set normphis [$call cfout] [list bb $norm1b] [$call cfin] - dict set errorphis [$call cfout] [list bb $err1b] [$call cfin] - my varargsEmitAndTrack $notokb notokbb \ - [list jumpMaybe [list bb $err1b] $invexpres] - my varargsEmitAndTrack $notokb notokbb [list jump [list bb $norm1b]] - lset bbcontent $notokb $notokbb - - # Split the critical edges - foreach {edge target} [list $norm0b $normb $err0b $errorb \ - $norm1b $normb $err1b $errorb] { - set splitbb {} - my varargsEmitAndTrack $edge splitbb [list jump [list bb $target]] - lset bbcontent $edge $splitbb - } - - # Now start the parameter checking logic - + set bb [my va_UnlinkTail $b [$call pc0]] + set B [quadcode::builder new [self] $b $bb] + + # Prepare parameters for the 'invoke' (or 'invokeExpanded') call, and + # add the call to the instruction sequence under construction. + my va_PrepareArgs $B $call + + puts "NOT FINISHED." + exit + $B destroy + $call destroy + return +} + +# quadcode::transformer method va_PrepareArgs -- +# +# Emits code to prepare the arguments for an 'invoke' or +# 'invokeExpanded' command, up to the point where the actual +# 'invoke' is issued. +# +# Parameters: +# B - quadcode::builder where the new invocation sequence is being built. +# call - Object describing the invocation sequence. +# +# Results: +# None. + +oo::define quadcode::transformer method va_PrepareArgs {B call} { + + # Create the first part of the 'invoke' instruction. + + lassign [$call cmd] status callee + if {!$status} { + error "can't find callee -- can't happen" + } + set newq [list invoke \ + [$call res_invoke] [$call cfin_invoke] \ + [list literal $callee]] + + # Find out how many plain parameters (that is, not 'args') the + # called command has. + lassign [$call arginfo] status arginfo + if {!$status} { + error "can't find arginfo - can't happen" + } set nPlainParams [llength $arginfo] set haveargs 0 if {[lindex $arginfo end] eq "args"} { set haveargs 1 incr nPlainParams -1 } - # Start by matching off non-expanded args with parameters in the callee - + # Any leading plain arguments that do not have {*} can simply be retained + # in the parameter list of [invoke]. + # $pos will be the position in the parameter list of the first + # parameter that needs special handling. + set argl [$call argl] set pos 0 while {$pos < $nPlainParams} { - if {[my varargsNonExpandedArgument newq $arginfo $pos $q]} break + if {[my va_NonExpandedArgument newq $arginfo $pos $argl]} break incr pos } - # Concatenate the remaining args into a list. 'listLoc' will - # be the name of the object that holds the list. my debug-varargs { - puts "varargs: $b:$pc: $q:\n\ - \ Matched leading non-expanded args.\ - $pos of $nPlainParams plain params" + puts "varargs: [$call b]:[$call pc0]: matched $pos out of $nPlainParams\ + leading non-expanded arg(s)." } + + # Generate code to make the rest of the args into a list + my va_MakeArgList $B $argl $pos + + puts "NOT DONE - varargs matched non-expanded args." + exit 1 + +} + + +if 0 { + set tempIndex -1 - set listLoc [my varargsExpandFixed bb tempIndex pos $b $q] + set listLoc [my va_MakeArgList bb tempIndex pos $b $q] # We are going to need the length of the list, so # extract that now. (If it turns out somehow that we # don't use it, 'deadvars' will get rid of this, anyway.) set lenLoc1 [my newVarInstance [list temp [incr tempIndex]]] set lenLoc [my newVarInstance [list temp $tempIndex]] - my varargsEmitAndTrack $b bb [list listLength $lenLoc1 $listLoc] - my varargsEmitAndTrack $b bb [list extractMaybe $lenLoc $lenLoc1] + my va_EmitAndTrack $b bb [list listLength $lenLoc1 $listLoc] + my va_EmitAndTrack $b bb [list extractMaybe $lenLoc $lenLoc1] # Count the mandatory args set firstMandatory $pos while {$pos < $nPlainParams} { @@ -310,16 +231,16 @@ if {$firstOptional > $firstMandatory} { # Make code to check length of arg list, starting a # new basic block set nMandatory [expr {$firstOptional - $firstMandatory}] - set b [my varargsCheckEnough $b $bb $lenLoc $compTemp \ + set b [my va_CheckEnough $b $bb $lenLoc $compTemp \ $nMandatory $notokb] set bb {} # Make code to transfer mandatory args - my varargsUnpackMandatory tempIndex bb newq $b $listLoc $nMandatory + my va_UnpackMandatory tempIndex bb newq $b $listLoc $nMandatory } # Now we have the parameters that have default values. set j $nMandatory @@ -332,30 +253,30 @@ lappend bbcontent {} lappend bbpred {} set i $firstOptional while {$i < $nPlainParams} { info default $callee [lindex $arginfo $i] defaultVal - lassign [my varargsUnpackOptional tempIndex b bb \ + lassign [my va_UnpackOptional tempIndex b bb \ $finishB $compTemp $listLoc $lenLoc $j] \ fromBlock argLoc lappend optInfo [list $fromBlock $defaultVal $argLoc] incr i incr j } # Close out the last basic block, switch to the 'finish' block # and emit 'phi' instructions to get the correct parameter set - my varargsFinishOptional b bb newq $finishB $optInfo + my va_FinishOptional b bb newq $finishB $optInfo } # If the procedure has 'args', then fill it in with the remainder of the # arg list. if {$haveargs} { - my varargsDoArgs tempIndex $b bb newq $listLoc $j + my va_DoArgs tempIndex $b bb newq $listLoc $j } else { - my varargsCheckTooMany b bb $lenLoc $compTemp $j $notokb + my va_CheckTooMany b bb $lenLoc $compTemp $j $notokb } # Create the normal invocation sequence. # 1. Create moveToCallFrame @@ -365,45 +286,45 @@ set cf2 [my newVarInstance $cfin] set q2 [list moveToCallFrame $cf2 $cfin] dict for {vname val} $invars { lappend q2 [list literal $vname] $val } - my varargsEmitAndTrack $b bb $q2 + my va_EmitAndTrack $b bb $q2 set cfin $cf2 lset newq 2 $cfin } # 2. Emit the call as rewritten - my varargsEmitAndTrack $b bb $newq + my va_EmitAndTrack $b bb $newq # 3. Make the 'retrieveResult' set okresult [my newVarInstance [$call retval]] - my varargsEmitAndTrack $b bb [list retrieveResult $okresult $invokeres] + my va_EmitAndTrack $b bb [list retrieveResult $okresult $invokeres] dict set normphis [$call retval] [list bb $norm0b] $okresult dict set errorphis [$call retval] [list bb $err0b] $okresult # 4. Make the 'extractCallFrame' set okcf [my newVarInstance [$call cfout]] - my varargsEmitAndTrack $b bb [list extractCallFrame $okcf $invokeres] + my va_EmitAndTrack $b bb [list extractCallFrame $okcf $invokeres] dict set normphis [$call cfout] [list bb $norm0b] $okcf dict set errorphis [$call cfout] [list bb $err0b] $okcf # 5. Make 'moveFromCallFrame' for all output values dict for {vname outval} [$call outvars] { set okval [my newVarInstance $outval] - my varargsEmitAndTrack $b bb \ + my va_EmitAndTrack $b bb \ [list moveFromCallFrame $okval $okcf [list literal $vname]] dict set normphis $outval [list bb $norm0b] $okval dict set errorphis $outval [list bb $err0b] $okval set notokval [dict get [$call invars] $vname] dict set normphis $outval [list bb $norm1b] $notokval dict set errorphis $outval [list bb $err1b] $notokval } # 6. Make the terminal jumps - my varargsEmitAndTrack $b bb [list jumpMaybe [list bb $err0b] $okresult] - my varargsEmitAndTrack $b bb [list jump [list bb $norm0b]] + my va_EmitAndTrack $b bb [list jumpMaybe [list bb $err0b] $okresult] + my va_EmitAndTrack $b bb [list jump [list bb $norm0b]] # Emit the final basic block rewrite lset bbcontent $b $bb @@ -418,13 +339,13 @@ if {[dict exists $toRepair $v $normb]} { set val [dict get $toRepair $v $normb] } incr val dict set toRepair $v $normb $val - my varargsEmitAndTrack $normb normbb [list phi $v {*}$sources] + my va_EmitAndTrack $normb normbb [list phi $v {*}$sources] } - my varargsEmitAndTrack $normb normbb [list jump [list bb [$call normexit]]] + my va_EmitAndTrack $normb normbb [list jump [list bb [$call normexit]]] lset bbcontent $normb $normbb # Make the block for the error exit set errorbb {} foreach {v sources} $errorphis { @@ -432,13 +353,13 @@ if {[dict exists $toRepair $v $errorb]} { set val [dict get $toRepair $v $errorb] } incr val dict set toRepair $v $errorb $val - my varargsEmitAndTrack $errorb errorbb [list phi $v {*}$sources] + my va_EmitAndTrack $errorb errorbb [list phi $v {*}$sources] } - my varargsEmitAndTrack $errorb errorbb [list jump [list bb [$call errexit]]] + my va_EmitAndTrack $errorb errorbb [list jump [list bb [$call errexit]]] lset bbcontent $errorb $errorbb # Restore dominance relationships my bbidom; my bblevel @@ -461,33 +382,36 @@ my debug-varargs { puts "After repairing SSA relationships:" my dump-bb } + $call destroy + return } -# quadcode::transformer method varargsUnlinkTail -- +# quadcode::transformer method va_UnlinkTail -- # -# Takes the last few instructions of a basic block and removes -# them temporarily, unlinking the block from its successors and -# the instructions from their ud- and du-chains. +# Removes the invocation sequence from a basic block in preparation +# for rewriting it. # # Parameters: # b - Number of the basic block # pc - Program counter of the first instruction being deleted # # Results: -# Returns the partial basic block that remains +# Returns the partial basic block that remains. # # Side effects: -# Linkages are destroyed. +# Variable defs and uses in the invocation sequence are removed +# from ud- and du-chains. The basic block is unlinked from its +# successors. -oo::define quadcode::transformer method varargsUnlinkTail {b pc} { +oo::define quadcode::transformer method va_UnlinkTail {b pc} { set bb [lindex $bbcontent $b] - set head [lrange $bb 0 [expr {$pc-1}]] set tail [lrange $bb $pc end] + set bb [lreplace $bb[set bb {}] $pc end] foreach q $tail { if {[lindex $q 1 0] in {"temp" "var"}} { dict unset udchain [lindex $q 1] } foreach arg [lrange $q 2 end] { @@ -497,141 +421,156 @@ } } foreach b2 [my bbsucc $b] { my removePred $b2 $b } - - lset bbcontent $b $head - - return $head + return $bb } -# quadcode::transformer method varargsNonExpandedArgument -- +# quadcode::transformer method va_NonExpandedArgument -- # # Transfer a leading non-expanded argument into a quad # under construction when rewriting 'invokeExpanded' # # Parameters: # newqVar - Name of a variable in caller's scope storing the # plain 'invoke' operation under construction # arginfo - Result of [info args] against the invoked proc # pos - Position of the argument (0 = first) in the argument list -# q - Quadruple under construction +# argl - Argument list of the 'invoke' or 'invokeExpanded' instruction # # Results: # Returns 0 if the parameter was transferred, 1 if we are at the # end of the possible static transfers. -oo::define quadcode::transformer method \ - varargsNonExpandedArgument {newqVar arginfo pos q} { - - upvar 1 $newqVar newq - - set param [lindex $arginfo $pos] - set arg [lindex $q [expr {4 + $pos}]] - switch -exact -- [lindex $arg 0] { - "literal" { - } - "temp" - "var" { - lassign [my findDef $arg] defb defpc defstmt - if {[lindex $defstmt 0] eq "expand"} { - return 1 - } - } - default { - return 1 - } - } - lappend newq $arg - return 0 - } - -# quadcode::transformer method varargsExpandFixed -- +oo::define quadcode::transformer method va_NonExpandedArgument {newqVar + arginfo + pos argl} { + + upvar 1 $newqVar newq + + set param [lindex $arginfo $pos] + set arg [lindex $argl $pos] + switch -exact -- [lindex $arg 0] { + "literal" { + } + "temp" - "var" { + lassign [my findDef $arg] defb defpc defstmt + if {[lindex $defstmt 0] eq "expand"} { + return 1 + } + } + default { + return 1 + } + } + lappend newq $arg + return 0 +} + +# quadcode::transformer method va_MakeArgList -- # # Takes the non-fixed-position arguments of 'invokeExpanded' # and emits code to make them into a list. # # Parameters: -# bbVar - Variable in caller holding the basic block under construction -# tempIdxVar - Variable in caller holding the number of the last -# temporary allocated. -# posVar - Position in the parameter list where the list construction -# should begin. -# b - Basic block number of the block under construction -# q - 'invokeExpanded' instruction being deconstructed +# B - quadcode::builder that is rewriting the invocation sequence. +# argl - Argument list being analyzed +# pos - Position in the argument list # # Results: # # Returns the name of a variable, temporary or literal that holds the # expanded list. -oo::define quadcode::transformer method \ - varargsExpandFixed {bbVar tempIdxVar posVar b q} { - - upvar 1 $bbVar bb $tempIdxVar tempIndex $posVar pos - - set listTemp [list temp [incr tempIndex]] - - # Handle the first arg. Since 'invokeExpanded' always - # has at least one expanded arg, there has to be a first - # arg. - if {4 + $pos >= [llength $q]} { - set listLoc "literal {}" - } else { - set arg [lindex $q [expr {4 + $pos}]] - switch -exact -- [lindex $arg 0] { - "literal" { - set listLoc [my newVarInstance $listTemp] - my varargsEmitAndTrack $b bb [list list $listLoc $arg] - } - "temp" - "var" { - lassign [my findDef $arg] defb defpc defstmt - if {[lindex $defstmt 0] eq "expand"} { - set listLoc [lindex $defstmt 2] - } else { - set listLoc [my newVarInstance $listTemp] - my varargsEmitAndTrack $b bb [list list $listLoc $arg] - } - } - } - } - - # listLoc now is holding the location of the list under - # construction. Concatenate the remaining params onto it. - - foreach arg [lrange $q [expr {5 + $pos}] end] { +oo::define quadcode::transformer method va_MakeArgList {B argl pos} { + + # Handle the first arg. 'listloc' will be the variable holding the + # expanded arglist. 'mightThrow' will be 1 if 'listloc' + # might be a non-list and 0 otherwise. + if {$pos >= [llength $argl]} { + set listLoc "literal {}" + } else { + set arg [lindex $argl $pos] + switch -exact -- [lindex $arg 0] { + "literal" { + set listloc [$B maketemp arglist] + $B emit [list list $listloc $arg] + set mightThrow 0 + } + "temp" - "var" { + lassign [my findDef $arg] defb defpc defstmt + if {[lindex $defstmt 0] eq "expand"} { + set listLoc [lindex $defstmt 2] + set mightThrow 1 + } else { + set listLoc [$B maketemp arglist] + $B emit [list list $listLoc $arg] + set mightThrow 0 + } + } + } + } + puts "did first arg, arglist is $listLoc, and b so far is\n[join [$B bb] \n]" + puts "lhsMightThrow = $lhsMightThrow" + + if {$lhsMightThrow} { + + exit 1 + + # listLoc now holds the location of the list under + # construction. Concatenate the remaining params onto it. + + foreach arg [lrange $argl [expr {1 + $pos}] end] { # Do we need to expand this arg? switch -exact -- [lindex $arg 0] { "literal" { - set op listAppend + set op "listAppend" } "temp" - "var" { lassign [my findDef $arg] defb defpc defstmt if {[lindex $defstmt 0] eq "expand"} { - set op listConcat + set op "listConcat" } else { - set op listAppend + set op "listAppend" } } } # Make variable to hold Maybe result from the concatenation, # and emit the concatenation. - # This can't fail, $listTemp is known to be a list - set nloc [my newVarInstance $listTemp] - my varargsEmitAndTrack $b bb [list $op $nloc $listLoc $arg] + set nloc [$B maketemp arglist] + $B emit [list $op $nloc $listLoc $arg] + + if {$lhsMightThrow || $op == "listConcat"} { + my makeErrorBlock $B + set intb [$B makeblock] + set nextb [$B makeblock] + $B emit [list jumpMaybe [list bb $intb] $nloc] + set lhsMightThrow 0 + $B emit [list jump [list bb $nextb]] + $B buildin $intb + set error [$B maketemp nloc] + $B emit [list extractFail $error $nloc] + $B emit [list jump [list bb $errorb]] + $B phi $errorb error $error + $B emit [list jump [list bb $errorb]] + $B buildin $nextb + + # KBK is here - need to get my context back!!! + } # extract the result from the Maybe - set listLoc [my newVarInstance $listTemp] - my varargsEmitAndTrack $b bb [list extractMaybe $listLoc $nloc] + set listLoc [$B maketemp arglist] + $B emit [list extractMaybe $listLoc $nloc] } return $listLoc } -# quadcode::transformer method varargsCheckEnough -- +# quadcode::transformer method va_CheckEnough -- # # Emits code to check for too few args passed to invokeExpanded # # Parameters: # b - Basic block number under construction @@ -642,15 +581,15 @@ # errorB - Basic block to jump to if too few args # # Results: # Returns the new basic block number; this method ends the block. -oo::define quadcode::transformer method varargsCheckEnough {b bb lenLoc compTemp +oo::define quadcode::transformer method va_CheckEnough {b bb lenLoc compTemp nMandatory errorB} { # Emit {$nMandatory > $lenLoc} set compLoc [my newVarInstance $compTemp] - my varargsEmitAndTrack $b bb \ + my va_EmitAndTrack $b bb \ [list gt $compLoc [list literal $nMandatory] $lenLoc] # Emit jumpTrue to the error block. This has to go through an # intermediate block because it will be a critical edge otherwise. # Emit jump to the following block @@ -659,25 +598,25 @@ lappend bbpred {} set newb [llength $bbcontent] lappend bbcontent {} lappend bbpred {} - my varargsEmitAndTrack $b bb [list jumpTrue [list bb $intb] $compLoc] - my varargsEmitAndTrack $b bb [list jump [list bb $newb]] + my va_EmitAndTrack $b bb [list jumpTrue [list bb $intb] $compLoc] + my va_EmitAndTrack $b bb [list jump [list bb $newb]] lset bbcontent $b $bb set bb {} # Emit the intermediate jump - my varargsEmitAndTrack $intb bb [list jump [list bb $errorB]] + my va_EmitAndTrack $intb bb [list jump [list bb $errorB]] lset bbcontent $intb $bb set bb {} return $newb } -# quadcode::transformer method varargsUnpackMandatory -- +# quadcode::transformer method va_UnpackMandatory -- # # Unpacks the mandatory args to a proc from the list created # by argument expansion # # Parameters; @@ -694,11 +633,11 @@ # None. # # Side effects: # Emits code to unpack the mandatory parameters -oo::define quadcode::transformer method varargsUnpackMandatory {tempIdxVar +oo::define quadcode::transformer method va_UnpackMandatory {tempIdxVar bbVar newqVar b listLoc nMandatory} { upvar 1 $tempIdxVar tempIdx $bbVar bb $newqVar newq @@ -707,24 +646,24 @@ # Emit the 'listIndex' instruction for one arg. It can't fail # because we know we have a list set argTemp [list temp [incr tempIdx]] set argLoc [my newVarInstance $argTemp] - my varargsEmitAndTrack $b bb \ + my va_EmitAndTrack $b bb \ [list listIndex $argLoc $listLoc [list literal $i]] # Emit the 'extractMaybe' to get the arg from the Maybe # result of 'listIndex' set argLoc2 [my newVarInstance $argTemp] - my varargsEmitAndTrack $b bb [list extractMaybe $argLoc2 $argLoc] + my va_EmitAndTrack $b bb [list extractMaybe $argLoc2 $argLoc] # Put the extracted arg on the 'invoke' instruction lappend newq $argLoc2 } } -# quadcode::transformer method varargsUnpackOptional -- +# quadcode::transformer method va_UnpackOptional -- # # Emits code to unpack one optional parameter in an invokeExpanded # # Parameters: # tempIdxVar - Variable holding the index of the last used temporary @@ -744,11 +683,11 @@ # # Side effects: # Emits code to unpack one value, or jump to the finish block if # there is nothing to unpack. -oo::define quadcode::transformer method varargsUnpackOptional {tempIdxVar bVar +oo::define quadcode::transformer method va_UnpackOptional {tempIdxVar bVar bbVar finishB compTemp listLoc lenLoc j} { upvar 1 $tempIdxVar tempIndex $bVar b $bbVar bb @@ -757,49 +696,49 @@ set argTemp [list temp [incr tempIndex]] set argLoc1 [my newVarInstance $argTemp] set argLoc2 [my newVarInstance $argTemp] # Emit the list length comparison - my varargsEmitAndTrack $b bb [list ge $compLoc $pos $lenLoc] + my va_EmitAndTrack $b bb [list ge $compLoc $pos $lenLoc] # Emit the jump to the finish block We need to make an intermediate block # because otherwise the flowgraph edge would be critical set intb [llength $bbcontent] lappend bbcontent {} lappend bbpred {} - my varargsEmitAndTrack $b bb [list jumpTrue [list bb $intb] $compLoc] + my va_EmitAndTrack $b bb [list jumpTrue [list bb $intb] $compLoc] # Create the next block and jump to it set newb [llength $bbcontent] lappend bbcontent {} lappend bbpred {} - my varargsEmitAndTrack $b bb [list jump [list bb $newb]] + my va_EmitAndTrack $b bb [list jump [list bb $newb]] lset bbcontent $b $bb # Make the intermediate block set b $intb set bb {} - my varargsEmitAndTrack $b bb [list jump [list bb $finishB]] + my va_EmitAndTrack $b bb [list jump [list bb $finishB]] lset bbcontent $b $bb # Advance to the new block set b $newb set bb {} # Emit the 'listIndex' to unpack the arg - my varargsEmitAndTrack $b bb [list listIndex $argLoc1 $listLoc $pos] + my va_EmitAndTrack $b bb [list listIndex $argLoc1 $listLoc $pos] # Emit the 'extractMaybe' on the 'listIndex' result - my varargsEmitAndTrack $b bb [list extractMaybe $argLoc2 $argLoc1] + my va_EmitAndTrack $b bb [list extractMaybe $argLoc2 $argLoc1] # Return the place where we stored the arg return [list $intb $argLoc2] } -# quadcode::transformer method varargsFinishOptional -- +# quadcode::transformer method va_FinishOptional -- # # Finish transmitting the args that have default values when # compiling {*} # # Parameters: @@ -818,19 +757,19 @@ # Closes out the current basic block, opens the finish block, # and emits phi instructions into the finish block. Adds the # outputs of the phi instructions to the 'invoke' instruction # under construction. -oo::define quadcode::transformer method varargsFinishOptional {bVar bbVar +oo::define quadcode::transformer method va_FinishOptional {bVar bbVar newqVar finishB optInfo} { upvar 1 $bVar b $bbVar bb $newqVar newq # Finish the current block and start building into 'finishB' - my varargsEmitAndTrack $b bb [list jump [list bb $finishB]] + my va_EmitAndTrack $b bb [list jump [list bb $finishB]] lset bbcontent $b $bb set bb {} set fromb $b set b $finishB @@ -853,16 +792,16 @@ } else { lappend q $defaultLit } } lappend q [list bb $fromb] $tempLoc - my varargsEmitAndTrack $b bb $q + my va_EmitAndTrack $b bb $q lappend newq $newTemp } } -# quadcode::transformer method varargsDoArgs -- +# quadcode::transformer method va_DoArgs -- # # Emits code to extract the parameter sequence needed to fill '$args' # from the parameter list. # # Parameters: @@ -879,29 +818,29 @@ # # Side effects: # Emits any code necessary to fill in 'args', and adds the resulting # variable onto the end of the new instruction. -oo::define quadcode::transformer method varargsDoArgs {tempIdxVar b bbVar +oo::define quadcode::transformer method va_DoArgs {tempIdxVar b bbVar newqVar listLoc i} { upvar 1 $tempIdxVar tempIndex $bbVar bb $newqVar newq if {$i == 0} { lappend newq $listLoc } else { set argsTemp [list temp [incr tempIndex]] set argsLoc1 [my newVarInstance $argsTemp] - my varargsEmitAndTrack $b bb [list listRange $argsLoc1 $listLoc \ + my va_EmitAndTrack $b bb [list listRange $argsLoc1 $listLoc \ [list literal $i] [list literal end]] set argsLoc2 [my newVarInstance $argsTemp] - my varargsEmitAndTrack $b bb [list extractMaybe $argsLoc2 $argsLoc1] + my va_EmitAndTrack $b bb [list extractMaybe $argsLoc2 $argsLoc1] lappend newq $argsLoc2 } } -# quadcode::transformer method varargsCheckTooMany -- +# quadcode::transformer method va_CheckTooMany -- # # Emits a codeburst to check whether an 'invokeExpanded' has # too many args # # Parameters: @@ -916,42 +855,42 @@ # None # # Side effects: # Emits code and closes the basic block -oo::define quadcode::transformer method varargsCheckTooMany {bVar bbVar lenLoc +oo::define quadcode::transformer method va_CheckTooMany {bVar bbVar lenLoc compTemp i errorB} { upvar 1 $bVar b $bbVar bb set compLoc [my newVarInstance $compTemp] - my varargsEmitAndTrack $b bb [list gt $compLoc $lenLoc [list literal $i]] + my va_EmitAndTrack $b bb [list gt $compLoc $lenLoc [list literal $i]] set intb [llength $bbcontent] lappend bbcontent {} lappend bbpred {} - my varargsEmitAndTrack $b bb [list jumpTrue [list bb $intb] $compLoc] + my va_EmitAndTrack $b bb [list jumpTrue [list bb $intb] $compLoc] set newb [llength $bbcontent] lappend bbcontent {} lappend bbpred {} - my varargsEmitAndTrack $b bb [list jump [list bb $newb]] + my va_EmitAndTrack $b bb [list jump [list bb $newb]] lset bbcontent $b $bb set b $intb set bb {} - my varargsEmitAndTrack $b bb [list jump [list bb $errorB]] + my va_EmitAndTrack $b bb [list jump [list bb $errorB]] lset bbcontent $b $bb set b $newb set bb {} } -# quadcode::transformer method varargsEmitWrongArgs -- +# quadcode::transformer method va_EmitWrongArgs -- # # Generates code to throw the 'wrong # args' error when needed # # Parameters: # result - Quadcode value that will hold the command result @@ -965,11 +904,11 @@ # None. # # Side effects: # Returns a codeburst that throws the exception -oo::define quadcode::transformer method varargsEmitWrongArgs {result cfout +oo::define quadcode::transformer method va_EmitWrongArgs {result cfout cfin cmd} { set burst {} if {$cfin ne "Nothing"} { lappend burst [list copy $cfout $cfin] @@ -1000,11 +939,11 @@ lappend burst $q return $burst } -# quadcode::transformer method varargsEmitAndTrack -- +# quadcode::transformer method va_EmitAndTrack -- # # Emits a quadcode instruction and tracks its effects # # Parameters: # b - Basic block number @@ -1016,11 +955,11 @@ # # Side effects: # Instruction is added to the basic block, and linked in ud- and du-chains # Basic block is linked in control flow if needed. -oo::define quadcode::transformer method varargsEmitAndTrack {b bbVar q} { +oo::define quadcode::transformer method va_EmitAndTrack {b bbVar q} { upvar 1 $bbVar bb set res [lindex $q 1] switch -exact -- [lindex $res 0] {