Check-in [dbd9571178]
Bounty program for improvements to Tcl and certain Tcl packages.
Tcl 2019 Conference, Houston/TX, US, Nov 4-8
Send your abstracts to tclconference@googlegroups.com
or submit via the online form by Sep 9.

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Move callframe moves to the actual points where callframe values are moved. They can then be optimized by avaiability and liveness analysis.
Timelines: family | ancestors | descendants | both | notworking | kbk-refactor-callframe
Files: files | file ages | folders
SHA3-256:dbd9571178f097cefc9361707c89a48ea8565504e7e3f21813611404cc541a39
User & Date: kbk 2019-01-01 21:57:35
Context
2019-01-01
23:06
Update quadcode generation and documentation so that 'direct' operations refer to the callframe (which is necessary in the event that they are emitted to deal with double-dereference). CODE ISSUER IS NOT YET UPDATED TO COPE WITH THE CHANGE. check-in: 104debc734 user: kbk tags: notworking, kbk-refactor-callframe
21:57
Move callframe moves to the actual points where callframe values are moved. They can then be optimized by avaiability and liveness analysis. check-in: dbd9571178 user: kbk tags: notworking, kbk-refactor-callframe
2018-12-29
21:00
Keep dictionary iterations out of the callframe - they can never be addressed by upvar check-in: ea7b788fb4 user: kbk tags: kbk-refactor-callframe
Changes

Changes to quadcode/callframe.tcl.

29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
    foreach bb $bbcontent {
	if {[lsearch -exact -index 0 $bb upvar] >= 0} {
	    return 1
	}
    }
    return 0
}
 
# quadcode::transformer method callframeMotion --
#
#	Adds callframe data motion for variables that may be links
#	by virtue of appearing in 'nsupvar', 'upvar' or 'variable'
#	opcodes, or that may be read or written by 'invoke'
#
# Results:
#	None.
#
# Side effects:
#	Adds the necessary instructions to keep the callframe in step
#	with linked variables and with variables that may be used or
#	set in invoked commands.
#
# This pass runs before SSA, so that it doesn't need to worry about
# variable renaming. It inserts a great many redundant data motions.
# 'cleanupCallFrameMotion' will take out some of them (at least any
# 'moveFromCallFrame' whose result is unused), but proper optmization
# will depend on global alias analysis, which we don't yet have.

oo::define quadcode::transformer method callFrameMotion {} {

    my debug-callframe {
	puts "Before callframeMotion:"
	my dump-bb
	puts "Links: $links"
    }

    set catches {};		# Dictionary enumerating the places where
    ;				# errorInfo and errorCode must be spoilt

    # Walk through the basic blocks and insert any needed instructions
    # before and after the blocks

    set b -1
    foreach bb $bbcontent {
	incr b
	set newbb {}
	set pc -1
	foreach q $bb {
	    incr pc
	    my callFrameMovesBefore $b $pc newbb $q
	    lappend newbb $q
	    my callFrameMovesAfter $b $pc newbb $q
	    if {[lindex $q 0] eq "jumpMaybe"} {
		dict set catches [lindex $q 1 1] {}
		my debug-callframe {
		    puts "   [lindex $q 1] appears to be a catch block"
		}
	    }
	}
	lset bbcontent $b $newbb
    }

    # Insert instructions to spoil ::errorCode and ::errorInfo after each
    # catch.

    my debug-callframe {
	puts "Clean up catch blocks:"
    }
    dict for {b -} $catches {
	set newbb [list {startCatch {temp @callframe} {temp @callframe}}]
	my debug-callframe {
	    puts "$b:0: [lindex $newbb 0]"
	}
	dict for {var -} $links {
	    set vname [lindex $var 1]
	    set newq [list moveFromCallFrame \
			  $var {temp @callframe} \
			  [list literal $vname]]
	    my debug-callframe {
		puts "$b:[llength $newbb]: $newq"
	    }
	    lappend newbb $newq
	}
	set bb [lindex $bbcontent $b]
	lset bbcontent $b {}
	set bb [linsert $bb[set bb {}] 0 {*}$newbb]
	lset bbcontent $b $bb
    }

    my debug-callframe {
	puts "After callframeMotion:"
	my dump-bb
    }
}
 
# quadcode::transformer method callFrameMovesBefore --
#
#	Inserts any data motion to and from the callframe required before
#	a given quadcode instruction.
#
# Parameters:
#	b - Basic block number
#	pc - Program counter within the block
#	newbbv - Name of a variable in caller's scope accumulating the new
#	         instruction list for the block
#	q - Instruction being analyzed.
#
# Results:
#	None.
#
# Side effects:
#	The code that is needed before $q is inserted.
#
# If an instruction is an 'invoke', then 'moveToCallFrame' is needed
# before it, with all purely local variables (those that do not appear
# in 'nsupvar', 'upvar', 'variable') listed.
#
# 'load' can be treated the same way as 'invoke'. In the worst
# case, we do not know what variables they read and write, and simply have
# to deal with whatever they do to the callframe. Once we've done copy
# propagation, we can deal with the common case where 'loadStk' and
# 'storeStk' are actually reading and writing namespace-qualified
# variables.
#
# Even though 'store' does not read variables, but only writes them, it
# still needs all the 'moveToCallFrame' operations as well. The reason
# is that if it is discovered that 'store' is storing to a predictable
# place, 'moveFromCallFrame' instructions need to be eliminated.
# In that case, we need the corresponding 'moveToCallFrame' operations
# so as to look up the reaching definition of the variable.
#
# The 'cleanupCallFrameMotion' pass will optimize away the variables
# that the 'invoke' or 'load' does not read, or that are not aliased
# to the variable that the 'store' might alter.  It can also remove
# data motion involving (some of) the variables that do not need to be
# moved to the callframe because they are already there. This
# optimization may have the effect of killing 'moveFromCallFrame'
# instructions, which will be removed by the cleanup optimizations.

oo::define quadcode::transformer method callFrameMovesBefore {b pc newbbv q} {
    if {[lindex $q 0] in {
	"directAppend" "directArrayAppend" "directArrayExists" "directArrayGet"
	"directArrayLappend" "directArrayLappendList" "directArraySet"
	"directArrayUnset" "directExists" "directGet" "directLappend"
	"directLappendList" "directSet" "directUnset" "invoke" "invokeExpanded"
    }} {

	# All variables are forced into the callframe before 'invoke',
	# 'load' and 'store'. Variables that cannot be accessed are
	# optimized away later.

	upvar 1 $newbbv newbb
	set newq {moveToCallFrame {temp @callframe} {temp @callframe}}
	foreach v $vars {
	    lappend newq [list literal [lindex $v 1]] $v
	    # TODO - Store-store optimization is needed, to detect that
	    #	     $v is already in the callframe
	}
	my debug-callframe {
	    puts "    $newq"
	    puts "inserted before"
	    puts "$b:$pc: $q"
	}
	lappend newbb $newq
    }
}
 
# quadcode::transformer method callFrameMovesAfter --
#
#	Inserts any data motion to and from the callframe required after
#	a given quadcode instruction.
#
# Parameters:
#	b - Basic block number
#	pc - Program counter within the block
#	newbbv - Name of a variable in caller's scope accumulating the new
#	         instruction list for the block
#	q - Instruction being analyzed.
#
# Results:
#	None.
#
# Side effects:
#	The code that is needed before $q is inserted.
#
# For an 'invoke', we need to insert moves from the callframe for all
# variables, both links and strictly local variables. (Links are included
# because we don't yet know what variables the invoked command may have
# written. and if any of them potentially have aliases, we need to move
# the aliases as well as the linked variables.)
#
# 'load' requires no postaction, since it cannot alter variables.
# 'store' may be treated similarly to 'invoke'.  We can optimize later
# the common case where the name of the variable being loaded or
# stored is constant, once we've done copy propagation and know what
# the name is.
#
# For 'nsupvar', 'upvar' and 'variable', we need to insert a single
# move from the callframe to retrieve the initial value of the variable
# after creating the link.
#
# An instruction that assigns to a variable that may be a link must move
# the result to the callframe, and then move all linked variables from the
# callframe. (Alias analysis may be able to down-select what is moved here.)
#
# Many of these moves will be dead, and we depend on cleanup optimizations
# to get rid of them.

oo::define quadcode::transformer method callFrameMovesAfter {b pc newbbv q} {

    upvar 1 $newbbv newbb

    switch -exact -- [lindex $q 0] {

	"invoke" - "invokeExpanded" - "nsupvar" - "upvar" - "variable" {

	    # 'invoke', 'nsupvar', 'upvar', 'variable' are followed by
	    # 'extractCallFrame' and will be dealt with when the
	    # 'extractCallFrame' is encountered.

	}

	"extractCallFrame" {

	    # Find the instruction that altered the callframe

	    set sourceCF [lindex $q 2]

	    set pc2 $pc
	    while {$pc2 > 0} {
		incr pc2 -1
		set q2 [lindex $bbcontent $b $pc2]
		if {[lindex $q2 1] eq $sourceCF} break
	    }
	    if {$pc2 < 0} {
		error "cannot find source of callframe in $b:$pc: $q"
	    }

	    switch -exact [lindex $q2 0] {

		"invoke" - "invokeExpanded" {

		    # After 'invoke' or 'store', all variables are
		    # retrieved from the callframe. Variables that are
		    # not changed (either because an invoked proc
		    # doesn't reference them, or because they cannot
		    # alias the target of the 'store') are removed
		    # later.

		    my debug-callframe {
			puts "insert after"
			puts "$b:$pc: $q"
			puts "   (origin: $b:$pc2: $q2)"
		    }
		    foreach v $vars {
			set newq [list moveFromCallFrame $v [lindex $q 1] \
				      [list literal [lindex $v 1]]]
			my debug-callframe {
			    puts "    $newq"
			}
			lappend newbb $newq
		    }
		}

		"nsupvar" - "upvar" - "variable" {

		    # After creating a new alias as a local variable, the
		    # value of the variable has to be retrieved from the
		    # callframe.
		    upvar 1 $newbbv newbb
		    my debug-callframe {
			puts "insert after"
			puts "$b:$pc: $q"
			puts "   (origin: $b:$pc2: $q2)"
		    }
		    set litname [lindex $q2 3]
		    set name [lindex $litname 1]
		    set newq [list moveFromCallFrame \
				  [list var $name] [lindex $q 1] $litname]
		    my debug-callframe {
			puts "    $newq"
		    }
		    lappend newbb $newq
		}
	    }
	}

	"dictAppend" - "dictGet" - "dictIncr" - "dictIterStart" -
	"dictLappend" - "dictSet" - "dictSize" - "dictUnset" -
	"dictSetOrUnset" -
	"div" - "expon" - "foreachStart" -
	"initException" -
	"listAppend" - "listConcat" -
	"listIn" - "listIndex" - "listLength" - "listRange" - "listSet" -
	"mod" - "not" - "originCmd" - "regexp" -
	"strindex" - "strrange" - "strreplace" {
	    # These operations all return FAIL, and a subsequent
	    # 'extractMaybe' will be needed. The intermediate copy
	    # of the variable will not be moved to the frame. We wait
	    # for the 'extractMaybe' to move the actual value.
	}

	default {

	    # On any assignment, we move the result to the callframe,
	    # then move anything that the result might alias back from
	    # the callframe. We put a 'no op' in between so that code that
	    # tracks the callframe content can find the correct values.

	    # On a direct assignment, we also need to recover anything
	    # that might alias the direct variable.

	    set tgt [lindex $q 1]
	    set needMovesFrom 0
	    if {[lindex $tgt 0] eq "var"
		    && [dict exists $links $tgt]} {
		upvar 1 $newbbv newbb
		my debug-callframe {
		    puts "insert after"
		    puts "$b:$pc: $q"
		}
		set vname [lindex $tgt 1]
		set newq [list moveToCallFrame \
			      {temp @callframe} {temp @callframe} \
			      [list literal $vname] $tgt]
		lappend newbb $newq
		my debug-callframe {
		    puts "    $newq"
		}
		set needMovesFrom 1
	    }
	    if {[lindex $q 0] in {
		"directAppend" "directArrayAppend" "directArrayLappend"
		"directArrayLappendList" "directArraySet" "directArrayUnset"
		"directLappend" "directLappendList" "directSet" "directUnset"
	    }} {
		unset -nocomplain vname
		set tgt Nothing
		set needMovesFrom 1
	    }

	    if {$needMovesFrom} {
		if {[info exists vname]} {
		    set nopArg [list literal $vname]
		} else {
		    set nopArg Nothing
		}
		set newq2 [list callFrameNop \
			       {temp @callframe} {temp @callframe} $nopArg]
		my debug-callframe {
		    puts "    $newq2"
		}
		lappend newbb $newq2
		dict for {var -} $links {
		    if {$tgt ne $var} {
			set vname [lindex $var 1]
			set newq [list moveFromCallFrame \
				      $var {temp @callframe} \
				      [list literal $vname]]
			my debug-callframe {
			    puts "    $newq"
			}
			lappend newbb $newq
		    }
		}
	    }
	}
    }
}
 
# quadcode::transformer method cleanupMoveFromCallFrame --
#
#	Removes and replaces 'moveFromCallFrame' where it is known that
#	a target procedure does not write a callframe variable
#
# Results:







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







29
30
31
32
33
34
35










































































































































































































































































































































































36
37
38
39
40
41
42
    foreach bb $bbcontent {
	if {[lsearch -exact -index 0 $bb upvar] >= 0} {
	    return 1
	}
    }
    return 0
}










































































































































































































































































































































































 
# quadcode::transformer method cleanupMoveFromCallFrame --
#
#	Removes and replaces 'moveFromCallFrame' where it is known that
#	a target procedure does not write a callframe variable
#
# Results:

Changes to quadcode/transformer.tcl.

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#	    renameTemps

	foreach pass {
	    bbpartition
	    constJumpPeephole
	    sortbb
	    loopinv
	    callFrameMotion
	    ssa
	    ud_du_chain
	    copyprop
	    fqcmd
	    varargs
	    deadbb
	    bbidom







<







314
315
316
317
318
319
320

321
322
323
324
325
326
327
#	    renameTemps

	foreach pass {
	    bbpartition
	    constJumpPeephole
	    sortbb
	    loopinv

	    ssa
	    ud_du_chain
	    copyprop
	    fqcmd
	    varargs
	    deadbb
	    bbidom

Changes to quadcode/translate.tcl.

49
50
51
52
53
54
55




56
57

58
59
60
61
62
63
64
..
65
66
67
68
69
70
71
72
73


74
75
76
77
78
79
80











81
82
83
84
85
86
87
...
499
500
501
502
503
504
505

506
507
508
509
510
511

512
513

514
515
516
517
518
519
520
...
537
538
539
540
541
542
543

544
545
546
547
548
549

550
551
552
553
554
555
556
557
558
559

560
561
562
563
564
565
566
...
574
575
576
577
578
579
580

581
582
583
584
585
586
587
588
589
590
591
592
593

594
595
596
597
598
599
600
...
607
608
609
610
611
612
613

614
615
616
617
618
619
620
...
625
626
627
628
629
630
631

632
633
634
635
636
637
638
...
806
807
808
809
810
811
812

813
814
815
816
817
818
819
...
821
822
823
824
825
826
827

828
829
830
831
832
833
834
835
836
837
838

839
840
841
842
843
844
845
846
847
848
849

850
851
852
853
854
855
856
...
999
1000
1001
1002
1003
1004
1005


1006
1007
1008
1009
1010
1011
1012


1013
1014
1015
1016
1017
1018
1019
....
1081
1082
1083
1084
1085
1086
1087

1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099

1100
1101
1102
1103
1104
1105
1106
....
1177
1178
1179
1180
1181
1182
1183

1184
1185
1186
1187
1188
1189
1190
....
1192
1193
1194
1195
1196
1197
1198

1199
1200
1201
1202
1203
1204
1205
....
1215
1216
1217
1218
1219
1220
1221

1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232

1233
1234
1235
1236
1237
1238
1239
....
1241
1242
1243
1244
1245
1246
1247

1248
1249
1250
1251
1252
1253
1254
....
1257
1258
1259
1260
1261
1262
1263

1264
1265
1266
1267
1268
1269
1270
1271
1272
1273


1274
1275
1276
1277
1278
1279
1280
1281

1282
1283
1284
1285
1286
1287
1288
....
1531
1532
1533
1534
1535
1536
1537


1538
1539
1540
1541
1542
1543
1544
....
1578
1579
1580
1581
1582
1583
1584




1585




1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
....
1627
1628
1629
1630
1631
1632
1633




1634




1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
....
1784
1785
1786
1787
1788
1789
1790





















1791
1792
1793
1794
1795
1796
1797

    my debug-translate {
	puts "Translate bytecode: $originProc"
    }

    # Construct a header that sets parameters and provides a fake point
    # to which live-on-entry variables can be attached.





    set vlist [lmap x [dict get $bytecode variables] {lindex $x 1}]

    my quads entry {temp @callframe} [list literal $vlist]
    my quads @debug-context {} [list literal [list proc $originProc]]
    if {[dict exists $bytecode sourcefile]} {
	my quads @debug-file {} [list literal [dict get $bytecode sourcefile]]
    }
    if {[dict exists $bytecode initiallinenumber]} {
	my quads @debug-line {} \
................................................................................
	    [list literal [list [dict get $bytecode initiallinenumber] 0]]
    }
    if {[dict exists $bytecode script]} {
	my quads @debug-script {} [list literal [dict get $bytecode script]]
    }

    # Also set the variable list in this transformer for later.

    set vars [lmap v $vlist {list var $v}]


    set idx 0
    foreach v [dict get $bytecode variables] {
	if {{arg} in [lindex $v 0]} {
	    my quads param [list var [lindex $v 1]] [list arg $idx]
	}
	incr idx
    }












    # Iterate the instruction list
    dict for {pc insn} [dict get $bytecode instructions] {
	if {![dict exists $bytecode stackState $pc]} {
	    my debug-translate {
		puts "$pc: (unreachable): $insn"
	    }
................................................................................
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		set auxNum [string range [lindex $insn 2] 1 end]
		set aux [lindex [dict get $bytecode auxiliary] $auxNum]
		set mid [list temp opnd0]
		set val [list temp [incr depth -1]]
		set idx 0

		foreach v [dict get $aux variables] {
		    set r [my index-to-var $v]
		    my generate-scalar-check $pc $r {TCL WRITE VARNAME} \
			"can't set \"%s\": variable is array"
		    my error-quads $pc listIndex $mid $val [list literal $idx]
		    my error-quads $pc dictGetOrNexist $r $var $mid

		    incr idx
		}

	    }
	    dictUpdateEnd {
		set var [my index-to-var [lindex $insn 1]]
		set auxNum [string range [lindex $insn 2] 1 end]
		set aux [lindex [dict get $bytecode auxiliary] $auxNum]
		set key [list temp opnd1]
		set isary [list temp opnd2]
................................................................................
		    my quads copy $mid Nothing
		    my quads jump [list pc [expr {$n + 4}]]
		    my quads extractScalar $mid $r
		    # Write the value to the right key of the dict
		    my error-quads $pc dictSetOrUnset $updating $updating $key $mid
		}
		my quads copy $var $updating

	    }
	    unsetScalar {
		# TODO - This doesn't complain on unsetting a nonexistent
		#        variable, it ignores '-nocomplain'
		set var [my index-to-var [lindex $insn 2]]
		my quads unset $var

	    }
	    unsetArray {
		# TODO - This doesn't complain on unsetting a nonexistent
		#        element, it ignores '-nocomplain'
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 2]]
		my generate-array-check $pc $ary $idx {TCL LOOKUP VARNAME} \
		    "can't unset \"%s(%s)\": variable isn't array"
		my quads initArrayIfNotExists $ary $ary
		my quads arrayUnset $ary $ary $idx

	    }
	    dictDone {
		# Do nothing; general free will clean up.
	    }
	    verifyDict {
		set r [list temp [incr depth -1]]
		my error-quads $pc dictSize $r $r
................................................................................
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal 0}
	        my quads copy $result [list literal $delta]
		my generate-arith-domain-check $pc incr $var $result
		my quads purify {temp opd0} $var
		my quads purify {temp opd1} $result
		my quads add $var {temp opd0} {temp opd1}

		my quads copy $result $var
	    }
	    incrScalar1 {
		set result [list temp $depth]
		set val [list temp [incr depth -1]]
		set var [my index-to-var [lindex $insn 1]]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal 0}
		my generate-arith-domain-check $pc incr $var $val
		my quads purify {temp opd0} $var
		my quads purify {temp opd1} $val
		my quads add $var {temp opd0} {temp opd1}

		my quads copy $result $var
	    }
	    incrArray1Imm {
		set tmp [list temp $depth]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set delta [list literal [lindex $insn 2]]
................................................................................
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal 0}
		my generate-arith-domain-check $pc incr $inval $tmp
		my quads purify {temp opd1} $inval
		my quads purify {temp opd2} $tmp
		my quads add $inval {temp opd1} {temp opd2}
		my quads arraySet $ary $ary $idx $inval

		my quads copy $res $inval
	    }
	    incrArray1 {
		set delta [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		my generate-array-check $pc $ary $idx {TCL LOOKUP VARNAME} \
................................................................................
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal 0}
		my generate-arith-domain-check $pc incr $inval $delta
		my quads purify {temp opd1} $inval
		my quads purify {temp opd2} $delta
		my quads add $inval {temp opd1} {temp opd2}
		my quads arraySet $ary $ary $idx $inval

		my quads copy $res $inval
	    }
	    incrStkImm {
		set var [list temp [incr depth -1]]
		set delta [list literal [lindex $insn 1]]
		# TODO: This assumes we're dealing with qualified names!
		set val {temp opd2}
................................................................................
		}
		set r [list temp $depth]
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		my error-quads $pc dictSet $r $var $val {*}[lreverse $q]
		my quads copy $var $r

	    }
	    dictUnset {
		set idxNum [expr [lindex $insn 1]]
		set var [my index-to-var [lindex $insn 2]]
		set q {}
		for {set i 0} {$i < $idxNum} {incr i} {
		    # NOTE: Reversed
................................................................................
		}
		set r [list temp $depth]
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		my error-quads $pc dictUnset $r $var {*}[lreverse $q]
		my quads copy $var $r

	    }
	    dictAppend - dictLappend {
		set var [my index-to-var [lindex $insn 1]]
		set val [list temp [incr depth -1]]
		set key [list temp [incr depth -1]]
		set res [list temp $depth]
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		my error-quads $pc [lindex $insn 0] $res $var $key $val
		my quads copy $var $res

	    }
	    dictIncrImm {
		set delta [list literal [lindex $insn 1]]
		set var [my index-to-var [lindex $insn 2]]
		set key [list temp [incr depth -1]]
		set res [list temp $depth]
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		my error-quads $pc dictIncr $res $var $key $delta
		my quads copy $var $res

	    }
	    list {
		set acount [lindex $insn 1]
		set depth [expr {$depth - $acount}]
		set result [list temp $depth]
		set qd {}
		for {set i 0} {$i < $acount} {incr i} {
................................................................................
		    set to_pc [expr {$pc + $offset}]
		    my generate-jump $to_pc true $test
		}
	    }
	    existScalar {
		set result [list temp $depth]
		set var [my index-to-var [lindex $insn 1]]


		my quads exists $result $var
	    }
	    existArray {
		set tmp [list temp $depth]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set result $idx


		my quads initArrayIfNotExists $tmp $ary
		my quads arrayElementExists $result $tmp $idx
	    }

	    loadScalar1 -
	    loadScalar4 {	# Load a variable
		set result [list temp $depth]
................................................................................
	    storeScalar1 -
	    storeScalar4 {	# Store a variable
		incr depth -1
		set var [my index-to-var [lindex $insn 1]]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads copy $var [list temp $depth]

	    }
	    storeArray1 -
	    storeArray4 {	# Store into an array
		set val [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set res $idx
		my generate-array-check $pc $ary $idx {TCL LOOKUP VARNAME} \
		    "can't set \"%s(%s)\": variable isn't array"
		my quads initArrayIfNotExists $ary $ary
		my quads arraySet $ary $ary $idx $val
		my quads copy $res $val

	    }
	    tryCvtToNumeric {	# No effect on value
	    }
	    tryCvtToBoolean {	# Push whether we're dealing with a boolean
		set val [list temp [expr {$depth - 1}]]
		set res [list temp $depth]
		set typecode [quadcode::dataType::typeUnion \
................................................................................
		set var [my index-to-var [lindex $insn 1]]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		set result [list temp $depth]
		my quads strcat $var $var $val
		my quads copy $result $var

	    }
	    appendArray1 - appendArray4 {
		set val [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set res [list temp $depth]
		set inval {temp opd0}
................................................................................
		    "can't set \"%s(%s)\": variable isn't array"
		my quads initArrayIfNotExists $ary $ary
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal {}}
		my quads strcat $inval $inval $val
		my quads arraySet $ary $ary $idx $inval
		my quads copy $res $inval

	    }
	    strcat - concatStk {
		lassign $insn op count
		set strcatvals {}
		for {set i 0} {$i < $count} {incr i} {
		    lappend strcatvals [list temp [incr depth -1]]
		}
................................................................................
		set res [list temp $depth]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		# TODO: Typecheck: need list in $var
		my error-quads $pc listAppend $res $var $val
		my quads copy $var $res

	    }
	    lappendList {
		set listval [list temp [incr depth -1]]
		set var [my index-to-var [lindex $insn 1]]
		set res [list temp $depth]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		# TODO: Typecheck: need lists in $var and $listval
		my error-quads $pc listConcat $res $var $listval
		my quads copy $var $res

	    }
	    lappendArray1 - lappendArray4 {
		set val [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set res [list temp $depth]
		set inval {temp opd0}
................................................................................
		    "can't set \"%s(%s)\": variable isn't array"
		my quads initArrayIfNotExists $var $var
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal {}}
		my error-quads $pc listAppend $inval $inval $val
		my quads arraySet $ary $ary $idx $inval
		my quads copy $res $inval

	    }
	    lappendListArray {
		set listval [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set res [list temp $depth]
		set inval {temp opd0}
................................................................................
		my quads initArrayIfNotExists $ary $ary
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal {}}
		# TODO: Typecheck: need lists in $inval and $listval
		my error-quads $pc listConcat $inval $inval $listval
		my quads arraySet $ary $ary $idx $inval
		my quads copy $res $inval

	    }
	    listConcat {
		set list2 [list temp [incr depth -1]]
		set list1 [list temp [incr depth -1]]
		set res [list temp $depth]
		# TODO: Typecheck: need lists in $list1 and $list2
		my error-quads $pc listConcat $res $list1 $list2
	    }
	    arrayExistsImm {
		set ary [my index-to-var [lindex $insn 1]]


		set res [list temp $depth]
		my quads arrayExists $res $ary
	    }
	    arrayMakeImm {
		set ary [my index-to-var [lindex $insn 1]]
		my generate-array-check $pc $ary {} {TCL WRITE ARRAY} \
		    "can't array set \"%s\": variable isn't array"
		my quads initArrayIfNotExists $ary $ary

	    }
	    arrayExistsStk {
		set ary [list temp [incr depth -1]]
		set res [list temp $depth]
		my error-quads $pc directIsArray $res $ary
	    }
	    arrayMakeStk {
................................................................................
	error "Not implemented! generate-existence-check on non-constant name"
    }
    set varname [lindex $varname 1]

    set where [llength $quads]
    set newtemp [list temp @exists]



    my quads exists $newtemp $var					;# 0
    my quads jumpTrue [list pc [expr {$where + 5}]] $newtemp		;# 1
    set msg [list literal "can't read \"$varname\": no such variable"]
    set optionsDict \
	[list literal [list -errorcode [list TCL LOOKUP VARNAME $varname]]]
    my quads initException {temp @exception} $msg $optionsDict \
	{literal 1} {literal 0}						;# 2
................................................................................
    ;				# Name of the variable being checked

    set ok [list pc [expr {[llength $quads] + 11}]]
    ;				# Quadcode address to jump to on success
    set fail [my exception-target $pc catch]
    ;				# Bytecode address to jump to on failure
    




    # 0




    my quads exists {temp @exists} $var
    # 1
    my quads jumpFalse $ok {temp @exists}
    # 2
    my quads arrayExists {temp @exists} $var
    # 3
    my quads jumpTrue $ok {temp @exists}
    # 4
    if {$idx ne {}} {
	my quads invoke {temp @callframe} {temp @callframe} \
	    {literal ::format} [list literal $msg] \
	    [list literal $varname] $idx
    } else {
	my quads invoke {temp @callframe} {temp @callframe} \
	    {literal ::format} [list literal $msg] \
	    [list literal $varname]
    }
    # 5
    my quads retrieveResult {temp @msg} {temp @callframe}
    # 6
    my quads extractCallFrame {temp @callframe} {temp @callframe}
    # 7
    my quads extractMaybe {temp @msg} {temp @msg}
    # 8
    my quads initException {temp @exception} {temp @msg} \
	[list literal [list -errorcode $error]] \
	{literal 1} {literal 0}
    # 9
    my quads extractFail {temp @exception} {temp @exception}
    # 10
    my generate-jump $fail
    # 11

    # The narrowing pass will insert any necessary 'extract...' instructions
}

oo::define quadcode::transformer method generate-scalar-check {pc var error msg} {
    
    set varname [lindex $var 1]
................................................................................
    ;				# Preformatted message to return on error
    
    set ok [list pc [expr {[llength $quads] + 5}]]
    ;				# Quadcode address to jump to on success
    set fail [my exception-target $pc catch]
    ;				# Bytecode address to jump to on failure
    




    # 0




    my quads arrayExists {temp @exists} $var
    # 1
    my quads jumpFalse $ok {temp @exists}
    # 2
    my quads initException {temp @exception} $msg \
	[list literal [list -errorcode $error]] \
	{literal 1} {literal 0}
    # 3
    my quads extractFail {temp @exception} {temp @exception}
    # 4
    my generate-jump $fail
    # 5

    # The narrowing pass will insert any necessary 'extract...' instructions
}

# generate-arith-domain-check --
#
#	Generates a check to make sure that a value is NUMERIC or INT
................................................................................
	my generate-jump $target
	# 5 - ok:
        my quads [list purifyParam $i] $val $val $functionName
	# 6
	# (next instruction)
	
    }





















}

# quads --
#
#	Generate the given quadcode.
#
# Parameters:







>
>
>
>
|
<
>







 







<

>
>







>
>
>
>
>
>
>
>
>
>
>







 







>






>


>







 







>






>










>







 







>













>







 







>







 







>







 







>







 







>











>











>







 







>
>







>
>







 







>












>







 







>







 







>







 







>











>







 







>







 







>










>
>








>







 







>
>







 







>
>
>
>
|
>
>
>
>

|

|

|

|









|

|

|

|



|

|

|







 







>
>
>
>
|
>
>
>
>

|

|



|

|

|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







49
50
51
52
53
54
55
56
57
58
59
60

61
62
63
64
65
66
67
68
..
69
70
71
72
73
74
75

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
...
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
...
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
...
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
...
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
...
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
...
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
...
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
....
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
....
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
....
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
....
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
....
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
....
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
....
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
....
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
....
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
....
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
....
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881

    my debug-translate {
	puts "Translate bytecode: $originProc"
    }

    # Construct a header that sets parameters and provides a fake point
    # to which live-on-entry variables can be attached.
    set vlist {}
    foreach v [dict get $bytecode variables] {
	if {!("temp" in [lindex $v 0])} {
	    lappend vlist [lindex $v 1]
	}

    }
    my quads entry {temp @callframe} [list literal $vlist]
    my quads @debug-context {} [list literal [list proc $originProc]]
    if {[dict exists $bytecode sourcefile]} {
	my quads @debug-file {} [list literal [dict get $bytecode sourcefile]]
    }
    if {[dict exists $bytecode initiallinenumber]} {
	my quads @debug-line {} \
................................................................................
	    [list literal [list [dict get $bytecode initiallinenumber] 0]]
    }
    if {[dict exists $bytecode script]} {
	my quads @debug-script {} [list literal [dict get $bytecode script]]
    }

    # Also set the variable list in this transformer for later.

    set vars [lmap v $vlist {list var $v}]

    # Put in code to transfer arguments
    set idx 0
    foreach v [dict get $bytecode variables] {
	if {{arg} in [lindex $v 0]} {
	    my quads param [list var [lindex $v 1]] [list arg $idx]
	}
	incr idx
    }

    # Make sure that the args are in the callframe.
    set q [list moveToCallFrame {temp @callframe} {temp @callframe}]
    foreach v [dict get $bytecode variables] {
	if {{arg} in [lindex $v 0]} {
	    lappend q [list literal [lindex $v 1]] [list var [lindex $v 1]]
	}
    }
    if {[llength $q] > 3} {
	my quads {*}$q
    }

    # Iterate the instruction list
    dict for {pc insn} [dict get $bytecode instructions] {
	if {![dict exists $bytecode stackState $pc]} {
	    my debug-translate {
		puts "$pc: (unreachable): $insn"
	    }
................................................................................
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		set auxNum [string range [lindex $insn 2] 1 end]
		set aux [lindex [dict get $bytecode auxiliary] $auxNum]
		set mid [list temp opnd0]
		set val [list temp [incr depth -1]]
		set idx 0
		set toUpdate {}
		foreach v [dict get $aux variables] {
		    set r [my index-to-var $v]
		    my generate-scalar-check $pc $r {TCL WRITE VARNAME} \
			"can't set \"%s\": variable is array"
		    my error-quads $pc listIndex $mid $val [list literal $idx]
		    my error-quads $pc dictGetOrNexist $r $var $mid
		    lappend toUpdate [list literal [lindex $r 1]] $r
		    incr idx
		}
		my update-in-callframe {*}$toUpdate
	    }
	    dictUpdateEnd {
		set var [my index-to-var [lindex $insn 1]]
		set auxNum [string range [lindex $insn 2] 1 end]
		set aux [lindex [dict get $bytecode auxiliary] $auxNum]
		set key [list temp opnd1]
		set isary [list temp opnd2]
................................................................................
		    my quads copy $mid Nothing
		    my quads jump [list pc [expr {$n + 4}]]
		    my quads extractScalar $mid $r
		    # Write the value to the right key of the dict
		    my error-quads $pc dictSetOrUnset $updating $updating $key $mid
		}
		my quads copy $var $updating
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    unsetScalar {
		# TODO - This doesn't complain on unsetting a nonexistent
		#        variable, it ignores '-nocomplain'
		set var [my index-to-var [lindex $insn 2]]
		my quads unset $var
		my update-in-callframe [list literal [lindex $var 1]] Nothing
	    }
	    unsetArray {
		# TODO - This doesn't complain on unsetting a nonexistent
		#        element, it ignores '-nocomplain'
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 2]]
		my generate-array-check $pc $ary $idx {TCL LOOKUP VARNAME} \
		    "can't unset \"%s(%s)\": variable isn't array"
		my quads initArrayIfNotExists $ary $ary
		my quads arrayUnset $ary $ary $idx
		my update-in-callframe [list literal [lindex $ary 1]] $ary
	    }
	    dictDone {
		# Do nothing; general free will clean up.
	    }
	    verifyDict {
		set r [list temp [incr depth -1]]
		my error-quads $pc dictSize $r $r
................................................................................
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal 0}
	        my quads copy $result [list literal $delta]
		my generate-arith-domain-check $pc incr $var $result
		my quads purify {temp opd0} $var
		my quads purify {temp opd1} $result
		my quads add $var {temp opd0} {temp opd1}
		my update-in-callframe [list literal [lindex $var 1]] $var
		my quads copy $result $var
	    }
	    incrScalar1 {
		set result [list temp $depth]
		set val [list temp [incr depth -1]]
		set var [my index-to-var [lindex $insn 1]]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal 0}
		my generate-arith-domain-check $pc incr $var $val
		my quads purify {temp opd0} $var
		my quads purify {temp opd1} $val
		my quads add $var {temp opd0} {temp opd1}
		my update-in-callframe [list literal [lindex $var 1]] $var
		my quads copy $result $var
	    }
	    incrArray1Imm {
		set tmp [list temp $depth]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set delta [list literal [lindex $insn 2]]
................................................................................
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal 0}
		my generate-arith-domain-check $pc incr $inval $tmp
		my quads purify {temp opd1} $inval
		my quads purify {temp opd2} $tmp
		my quads add $inval {temp opd1} {temp opd2}
		my quads arraySet $ary $ary $idx $inval
		my update-in-callframe [list literal [lindex $ary 1]] $ary
		my quads copy $res $inval
	    }
	    incrArray1 {
		set delta [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		my generate-array-check $pc $ary $idx {TCL LOOKUP VARNAME} \
................................................................................
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal 0}
		my generate-arith-domain-check $pc incr $inval $delta
		my quads purify {temp opd1} $inval
		my quads purify {temp opd2} $delta
		my quads add $inval {temp opd1} {temp opd2}
		my quads arraySet $ary $ary $idx $inval
		my update-in-callframe [list literal [lindex $ary 1]] $ary
		my quads copy $res $inval
	    }
	    incrStkImm {
		set var [list temp [incr depth -1]]
		set delta [list literal [lindex $insn 1]]
		# TODO: This assumes we're dealing with qualified names!
		set val {temp opd2}
................................................................................
		}
		set r [list temp $depth]
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		my error-quads $pc dictSet $r $var $val {*}[lreverse $q]
		my quads copy $var $r
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    dictUnset {
		set idxNum [expr [lindex $insn 1]]
		set var [my index-to-var [lindex $insn 2]]
		set q {}
		for {set i 0} {$i < $idxNum} {incr i} {
		    # NOTE: Reversed
................................................................................
		}
		set r [list temp $depth]
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		my error-quads $pc dictUnset $r $var {*}[lreverse $q]
		my quads copy $var $r
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    dictAppend - dictLappend {
		set var [my index-to-var [lindex $insn 1]]
		set val [list temp [incr depth -1]]
		set key [list temp [incr depth -1]]
		set res [list temp $depth]
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		my error-quads $pc [lindex $insn 0] $res $var $key $val
		my quads copy $var $res
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    dictIncrImm {
		set delta [list literal [lindex $insn 1]]
		set var [my index-to-var [lindex $insn 2]]
		set key [list temp [incr depth -1]]
		set res [list temp $depth]
		my generate-scalar-check $pc $var {TCL READ VARNAME} \
		    "can't read \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		my error-quads $pc dictIncr $res $var $key $delta
		my quads copy $var $res
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    list {
		set acount [lindex $insn 1]
		set depth [expr {$depth - $acount}]
		set result [list temp $depth]
		set qd {}
		for {set i 0} {$i < $acount} {incr i} {
................................................................................
		    set to_pc [expr {$pc + $offset}]
		    my generate-jump $to_pc true $test
		}
	    }
	    existScalar {
		set result [list temp $depth]
		set var [my index-to-var [lindex $insn 1]]
		my quads moveFromCallFrame \
		    $var {temp @callframe} [list literal [lindex $var 1]]
		my quads exists $result $var
	    }
	    existArray {
		set tmp [list temp $depth]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set result $idx
		my quads moveFromCallFrame \
		    $var {temp @callframe} [list literal [lindex $var 1]]
		my quads initArrayIfNotExists $tmp $ary
		my quads arrayElementExists $result $tmp $idx
	    }

	    loadScalar1 -
	    loadScalar4 {	# Load a variable
		set result [list temp $depth]
................................................................................
	    storeScalar1 -
	    storeScalar4 {	# Store a variable
		incr depth -1
		set var [my index-to-var [lindex $insn 1]]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads copy $var [list temp $depth]
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    storeArray1 -
	    storeArray4 {	# Store into an array
		set val [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set res $idx
		my generate-array-check $pc $ary $idx {TCL LOOKUP VARNAME} \
		    "can't set \"%s(%s)\": variable isn't array"
		my quads initArrayIfNotExists $ary $ary
		my quads arraySet $ary $ary $idx $val
		my quads copy $res $val
		my update-in-callframe [list literal [lindex $ary 1]] $ary
	    }
	    tryCvtToNumeric {	# No effect on value
	    }
	    tryCvtToBoolean {	# Push whether we're dealing with a boolean
		set val [list temp [expr {$depth - 1}]]
		set res [list temp $depth]
		set typecode [quadcode::dataType::typeUnion \
................................................................................
		set var [my index-to-var [lindex $insn 1]]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		set result [list temp $depth]
		my quads strcat $var $var $val
		my quads copy $result $var
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    appendArray1 - appendArray4 {
		set val [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set res [list temp $depth]
		set inval {temp opd0}
................................................................................
		    "can't set \"%s(%s)\": variable isn't array"
		my quads initArrayIfNotExists $ary $ary
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal {}}
		my quads strcat $inval $inval $val
		my quads arraySet $ary $ary $idx $inval
		my quads copy $res $inval
		my update-in-callframe [list literal [lindex $ary 1]] $ary
	    }
	    strcat - concatStk {
		lassign $insn op count
		set strcatvals {}
		for {set i 0} {$i < $count} {incr i} {
		    lappend strcatvals [list temp [incr depth -1]]
		}
................................................................................
		set res [list temp $depth]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		# TODO: Typecheck: need list in $var
		my error-quads $pc listAppend $res $var $val
		my quads copy $var $res
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    lappendList {
		set listval [list temp [incr depth -1]]
		set var [my index-to-var [lindex $insn 1]]
		set res [list temp $depth]
		my generate-scalar-check $pc $var {TCL WRITE VARNAME} \
		    "can't set \"%s\": variable is array"
		my quads initIfNotExists $var $var {literal {}}
		# TODO: Typecheck: need lists in $var and $listval
		my error-quads $pc listConcat $res $var $listval
		my quads copy $var $res
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    lappendArray1 - lappendArray4 {
		set val [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set res [list temp $depth]
		set inval {temp opd0}
................................................................................
		    "can't set \"%s(%s)\": variable isn't array"
		my quads initArrayIfNotExists $var $var
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal {}}
		my error-quads $pc listAppend $inval $inval $val
		my quads arraySet $ary $ary $idx $inval
		my quads copy $res $inval
		my update-in-callframe [list literal [lindex $ary 1]] $ary
	    }
	    lappendListArray {
		set listval [list temp [incr depth -1]]
		set idx [list temp [incr depth -1]]
		set ary [my index-to-var [lindex $insn 1]]
		set res [list temp $depth]
		set inval {temp opd0}
................................................................................
		my quads initArrayIfNotExists $ary $ary
		my quads arrayGet $inval $ary $idx
		my quads initIfNotExists $inval $inval {literal {}}
		# TODO: Typecheck: need lists in $inval and $listval
		my error-quads $pc listConcat $inval $inval $listval
		my quads arraySet $ary $ary $idx $inval
		my quads copy $res $inval
		my update-in-callframe [list literal [lindex $ary 1]] $ary
	    }
	    listConcat {
		set list2 [list temp [incr depth -1]]
		set list1 [list temp [incr depth -1]]
		set res [list temp $depth]
		# TODO: Typecheck: need lists in $list1 and $list2
		my error-quads $pc listConcat $res $list1 $list2
	    }
	    arrayExistsImm {
		set ary [my index-to-var [lindex $insn 1]]
		my quads moveFromCallFrame \
		    $ary {temp @callframe} [list literal [lindex $ary 1]]
		set res [list temp $depth]
		my quads arrayExists $res $ary
	    }
	    arrayMakeImm {
		set ary [my index-to-var [lindex $insn 1]]
		my generate-array-check $pc $ary {} {TCL WRITE ARRAY} \
		    "can't array set \"%s\": variable isn't array"
		my quads initArrayIfNotExists $ary $ary
		my update-in-callframe [list literal [lindex $var 1]] $var
	    }
	    arrayExistsStk {
		set ary [list temp [incr depth -1]]
		set res [list temp $depth]
		my error-quads $pc directIsArray $res $ary
	    }
	    arrayMakeStk {
................................................................................
	error "Not implemented! generate-existence-check on non-constant name"
    }
    set varname [lindex $varname 1]

    set where [llength $quads]
    set newtemp [list temp @exists]

    my quads moveFromCallFrame \
	$var {temp @callframe} [list literal [lindex $var 1]]
    my quads exists $newtemp $var					;# 0
    my quads jumpTrue [list pc [expr {$where + 5}]] $newtemp		;# 1
    set msg [list literal "can't read \"$varname\": no such variable"]
    set optionsDict \
	[list literal [list -errorcode [list TCL LOOKUP VARNAME $varname]]]
    my quads initException {temp @exception} $msg $optionsDict \
	{literal 1} {literal 0}						;# 2
................................................................................
    ;				# Name of the variable being checked

    set ok [list pc [expr {[llength $quads] + 11}]]
    ;				# Quadcode address to jump to on success
    set fail [my exception-target $pc catch]
    ;				# Bytecode address to jump to on failure
    
    # If this is a real variable and not a temporary (e.g. a dict iterator),
    # make sure that it's pulled from the callframe. Load elimination will
    # delete this instruction if possible.
    if {[lindex $var 0] eq "var"} {
	# 0
	my quads moveFromCallFrame $var {temp @callframe} \
	    [list literal [lindex $var 1]]
    }
    # 1
    my quads exists {temp @exists} $var
    # 2
    my quads jumpFalse $ok {temp @exists}
    # 3
    my quads arrayExists {temp @exists} $var
    # 4
    my quads jumpTrue $ok {temp @exists}
    # 5
    if {$idx ne {}} {
	my quads invoke {temp @callframe} {temp @callframe} \
	    {literal ::format} [list literal $msg] \
	    [list literal $varname] $idx
    } else {
	my quads invoke {temp @callframe} {temp @callframe} \
	    {literal ::format} [list literal $msg] \
	    [list literal $varname]
    }
    # 6
    my quads retrieveResult {temp @msg} {temp @callframe}
    # 7
    my quads extractCallFrame {temp @callframe} {temp @callframe}
    # 8
    my quads extractMaybe {temp @msg} {temp @msg}
    # 9
    my quads initException {temp @exception} {temp @msg} \
	[list literal [list -errorcode $error]] \
	{literal 1} {literal 0}
    # 10
    my quads extractFail {temp @exception} {temp @exception}
    # 11
    my generate-jump $fail
    # 12

    # The narrowing pass will insert any necessary 'extract...' instructions
}

oo::define quadcode::transformer method generate-scalar-check {pc var error msg} {
    
    set varname [lindex $var 1]
................................................................................
    ;				# Preformatted message to return on error
    
    set ok [list pc [expr {[llength $quads] + 5}]]
    ;				# Quadcode address to jump to on success
    set fail [my exception-target $pc catch]
    ;				# Bytecode address to jump to on failure
    
    # If this is a real variable and not a temporary (e.g. a dict iterator),
    # make sure that it's pulled from the callframe. Load elimination will
    # delete this instruction if possible.
    if {[lindex $var 0] eq "var"} {
	# 0
	my quads moveFromCallFrame $var {temp @callframe} \
	    [list literal [lindex $var 1]]
    }
    # 1
    my quads arrayExists {temp @exists} $var
    # 2
    my quads jumpFalse $ok {temp @exists}
    # 3
    my quads initException {temp @exception} $msg \
	[list literal [list -errorcode $error]] \
	{literal 1} {literal 0}
    # 4
    my quads extractFail {temp @exception} {temp @exception}
    # 5
    my generate-jump $fail
    # 6

    # The narrowing pass will insert any necessary 'extract...' instructions
}

# generate-arith-domain-check --
#
#	Generates a check to make sure that a value is NUMERIC or INT
................................................................................
	my generate-jump $target
	# 5 - ok:
        my quads [list purifyParam $i] $val $val $functionName
	# 6
	# (next instruction)
	
    }
    return
}

# update-in-callframe --
#
#	Generates code to update a variable's value in the callframe.
#
# Parameters:
#	args - Alternating list of variables names and variable values
#
# Results:
#	None.
#
# Side effects:
#	Emits `moveToCallFrame`.

oo::define quadcode::transformer method update-in-callframe {args} {
    if {[llength $args] > 0} {
	my quads moveToCallFrame {temp @callframe} {temp @callframe} {*}$args
    }
    return
}

# quads --
#
#	Generate the given quadcode.
#
# Parameters: