Check-in [a41b93130e]

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

Overview
Comment:Finish jump threading - actually do the block duplication and redirection of jumps. Add the logic for SSA deconstruction (required by jump threading) and make SSA construction work with the deconstructed result.
Timelines: family | ancestors | descendants | both | notworking | kbk-jumpthread
Files: files | file ages | folders
SHA3-256: a41b93130ee45c3523f3555fb2aab904e9645fd7ce960ec17572bf94795d41e0
User & Date: kbk 2018-12-17 22:08:21.794
Context
2018-12-17
23:13
result, returnCode, returnOptions must be split into FAIL and non-FAIL paths because the backend isn't prepared to deal with all combinations of FAIL + someOtherType. Closed-Leaf check-in: 94358b53ea user: kbk tags: kbk-jumpthread
22:08
Finish jump threading - actually do the block duplication and redirection of jumps. Add the logic for SSA deconstruction (required by jump threading) and make SSA construction work with the deconstructed result. check-in: a41b93130e user: kbk tags: notworking, kbk-jumpthread
2018-12-16
05:18
Add constant folding for unary +. It probably ought to be folded out altogether, since it serves only to trigger type checking. Add an outline of the actual process of jump threading once the threads have been determined. check-in: c388c8737b user: kbk tags: notworking, kbk-jumpthread
Changes
Unified Diff Ignore Whitespace Patch
Changes to quadcode/jumpthread.tcl.
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
    # variant block.

    my jt_forward

    # Determine whether the division into variants is trying to split
    # anything.

    if {[my jt_has_multiple_variants]} {


        # TODO - IMPLEMENT THE FOLLOWING








        my jt_cleanup; return 0




        # We will be splitting paths.  This is a relatively 'violent'
        # change to the program, and it's insanely difficult to maintain SSA


        # through it. Instead, we deconstruct the SSA form, split the paths
        # in the deconstructed version, and then rebuild SSA. (We then have


        # to solve for ud- and du-chains again.
        
        my ssa_deconstruct






        
        my jt_split_paths
        

        my ssa




        my ud_du_chain





    }
        
    # Clean up the working storage
    
    my jt_cleanup

    return 0
}

# quadcode::transformer method jt_unpackPhis --
#
#	Unpacks phi operations for fast lookup when doing jump threading.
#
# Results:







|
>

<
>
>
>
>
>

>
>
|
>
>
>
|
|
<
>
>
|
<
>
>
|
|
|
>
>
>
>
>
>
|
<
|
>

>
>
>
>

>
>
>
>







|







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
    # variant block.

    my jt_forward

    # Determine whether the division into variants is trying to split
    # anything.

    set changed [my jt_has_multiple_variants]
    if {$changed} {


        # We will be doing a 'violent' rewrite of the control flow. Rather
        # than trying to maintain data flows in the face of this, it is
        # easier to deconstruct SSA form, perform the rewriting using
        # conventional assignments, and then reconvert to SSA.
        my deconstructSSA

        # Split the blocks into the variants computed by jt_forward, and
        # recompute the control flow (bbpred and block successors).
        my jt_split_paths
        my debug-jumpthread {
            puts "After splitting the paths:"
            my dump-bb
        }


        # Splitting the paths may have introduced new critical edges, so
        # make sure that they get resplit. 
        my splitCritical

        my debug-jumpthread {
            puts "After splitting critical edges:"
            my dump-bb
        }

        # Splitting critical edges requires that topologic order be restored
        # to the blocks.
        my sortbb
        my debug-jumpthread {
            puts "After re-sorting basic blocks:"
            my dump-bb
        }


        # Restore SSA form, compute ud- and du-chains, and propagate copies.
        my ssa
        my debug-jumpthread {
            puts "After reconstructing SSA:"
            my dump-bb
        }
        my ud_du_chain
        my copyprop

        # The code should now be ready to repeat type analysis and cleanup
        # optimizations.

    }
        
    # Clean up the working storage
    
    my jt_cleanup

    return $changed
}

# quadcode::transformer method jt_unpackPhis --
#
#	Unpacks phi operations for fast lookup when doing jump threading.
#
# Results:
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#
#	Constructs the list, jt_antin, indexed by basic block number,
#	containing dictionaries.  The dictionaries describe the conditions
#	that will inform jump threading downstream of the entry to the basic
#	blocks. The dictionaries have two levels. The first level key gives
#	the name of a value in the quadcode, and the second gives a condition
#	on that value's type.  The second key is either 'is TYPECONST'
#	or 'isnot TYPECONST' for a given value in quadcode::dataTypes.
#	The values in the dictionary are ordinal numbers of conditions in the
#	block, and will be used to construct bit vectors of what conditions
#	are satisfied in a copy of the block.

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

    namespace upvar ::quadcode jt_removable jt_removable







|







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#
#	Constructs the list, jt_antin, indexed by basic block number,
#	containing dictionaries.  The dictionaries describe the conditions
#	that will inform jump threading downstream of the entry to the basic
#	blocks. The dictionaries have two levels. The first level key gives
#	the name of a value in the quadcode, and the second gives a condition
#	on that value's type.  The second key is either 'is TYPECONST'
#	or 'isnot TYPECONST' for a given value in quadcode::dataType.
#	The values in the dictionary are ordinal numbers of conditions in the
#	block, and will be used to construct bit vectors of what conditions
#	are satisfied in a copy of the block.

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

    namespace upvar ::quadcode jt_removable jt_removable
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
#	conditions forward into the successor. If the successor has
#	a set of conditions that has not yet been visited, adds it
#	to 'jt_variants' and stacks it for processing.

oo::define quadcode::transformer method jt_forward_worker {b mask} {

    namespace upvar ::quadcode::dataType ARRAY ARRAY CONST0 CONST0 \
        CONST1 CONST1 IMPURE IMPURE NEXIST NEXIST

    my debug-jumpthread {
        puts "  bb $b:"
    }

    # Find out what assertions are guaranteed by $mask
    set asserted [my jt_maskToAssertions $b $mask]







|







507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
#	conditions forward into the successor. If the successor has
#	a set of conditions that has not yet been visited, adds it
#	to 'jt_variants' and stacks it for processing.

oo::define quadcode::transformer method jt_forward_worker {b mask} {

    namespace upvar ::quadcode::dataType ARRAY ARRAY CONST0 CONST0 \
        CONST1 CONST1 IMPURE IMPURE NEXIST NEXIST ZEROONE ZEROONE

    my debug-jumpthread {
        puts "  bb $b:"
    }

    # Find out what assertions are guaranteed by $mask
    set asserted [my jt_maskToAssertions $b $mask]
519
520
521
522
523
524
525


526
527
528
529
530
531
532
533
534
535
536
537


538
539
540
541
542
543
544
545


546
547
548
549
550
551
552
        switch -exact -- $op {

            "arrayExists" {
                if {[quadcode::dataType::isa $fromtype $ARRAY]} {
                    dict set localtypes $result $CONST1
                } elseif {![quadcode::dataType::mightbea $fromtype $ARRAY]} {
                    dict set localtypes $result $CONST0


                }
            }

            "copy" {
                dict set localtypes $result $fromtype
            }

            "exists" {
                if {$fromtype == $NEXIST} {
                    dict set localtypes $result $CONST0
                } elseif {!($fromtype & $NEXIST)} {
                    dict set localtypes $result $CONST1


                }
            }

            "instanceOf" {
                if {[quadcode::dataType::isa $fromtype $totype]} {
                    dict set localtypes $result $CONST1
                } elseif {![quadcode::dataType::mightbea $fromtype $totype]} {
                    dict set localtypes $result $CONST0


                }
            }
            
            "jump" {
                my jt_processSuccessor $b $mask \
                    [lindex $result 1] $localtypes
                break







>
>












>
>








>
>







545
546
547
548
549
550
551
552
553
554
555
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
        switch -exact -- $op {

            "arrayExists" {
                if {[quadcode::dataType::isa $fromtype $ARRAY]} {
                    dict set localtypes $result $CONST1
                } elseif {![quadcode::dataType::mightbea $fromtype $ARRAY]} {
                    dict set localtypes $result $CONST0
                } else {
                    dict set localtypes $result $ZEROONE
                }
            }

            "copy" {
                dict set localtypes $result $fromtype
            }

            "exists" {
                if {$fromtype == $NEXIST} {
                    dict set localtypes $result $CONST0
                } elseif {!($fromtype & $NEXIST)} {
                    dict set localtypes $result $CONST1
                } else {
                    dict set localtypes $result $ZEROONE
                }
            }

            "instanceOf" {
                if {[quadcode::dataType::isa $fromtype $totype]} {
                    dict set localtypes $result $CONST1
                } elseif {![quadcode::dataType::mightbea $fromtype $totype]} {
                    dict set localtypes $result $CONST0
                } else {
                    dict set localtypes $result $ZEROONE
                }
            }
            
            "jump" {
                my jt_processSuccessor $b $mask \
                    [lindex $result 1] $localtypes
                break
575
576
577
578
579
580
581






582
583
584
585
586
587





588





589
590
591
592
593
594
595
                    $fromtype $failbranch $okbranch $localtypes
                break
            }

            "narrowToType" {
                dict set localtypes $result [expr {$fromtype & $totype}]
            }







            "purify" {
                dict set localtypes $result [expr {$fromtype & ~$IMPURE}]
            }

            default {





                # do nothing





            }
        }
    }

    return
}








>
>
>
>
>
>






>
>
>
>
>
|
>
>
>
>
>







607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
                    $fromtype $failbranch $okbranch $localtypes
                break
            }

            "narrowToType" {
                dict set localtypes $result [expr {$fromtype & $totype}]
            }

            "phi" {
                if {![dict exists $localtypes $result]} {
                    dict set localtypes $result [dict get $types $result]
                }
            }

            "purify" {
                dict set localtypes $result [expr {$fromtype & ~$IMPURE}]
            }

            default {
                if {[lindex $result 0] in {"temp" "var"}} {
                    dict set localtypes $result [dict get $types $result]
                }
            }
        }

        my debug-jumpthread {
            if {[lindex $result 0] in {"temp" "var"}} {
                puts [format "      local type of %s is %#llx (%s)" \
                          $result [dict get $localtypes $result] \
                          [nameOfType [dict get $localtypes $result]]]
            }
        }
    }

    return
}

917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
#	Returns a bit vector of anticipated conditions satisfied at entry
#	to block $s.

oo::define quadcode::transformer method jt_assertionsToMask {p pmask ptypes s} {

    my variable jt_antin

    my debug-pre {
        puts "\t    make assertion mask for $p -> $s"
    }

    set smask 0
    set cn -1
    dict for {v conds} [lindex $jt_antin $s] {
        dict for {c -} $conds {
            incr cn
            if {[my jt_test_assertion $p $pmask $ptypes $s $v $c]} {
                my debug-pre {
                    puts "\t      include $v $c"
                }
                set smask [expr {$smask | (1 << $cn)}]
            }
        }
    }
    return $smask







|









|







965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
#	Returns a bit vector of anticipated conditions satisfied at entry
#	to block $s.

oo::define quadcode::transformer method jt_assertionsToMask {p pmask ptypes s} {

    my variable jt_antin

    my debug-jumpthread {
        puts "\t    make assertion mask for $p -> $s"
    }

    set smask 0
    set cn -1
    dict for {v conds} [lindex $jt_antin $s] {
        dict for {c -} $conds {
            incr cn
            if {[my jt_test_assertion $p $pmask $ptypes $s $v $c]} {
                my debug-jumpthread {
                    puts "\t      include $v $c"
                }
                set smask [expr {$smask | (1 << $cn)}]
            }
        }
    }
    return $smask
957
958
959
960
961
962
963


964
965
966
967
968


969
970
971
972
973
974
975
976
#	cond - Condition being tested
#
# Results:
#	Returns 1 if the condition is satisfied, 0 otherwise

oo::define quadcode::transformer method jt_test_assertion {p pmask ptypes
                                                           s v cond} {


    lassign $cond kind t

    # Find the name of the variable in the predecessor
    set w [my jt_translate_phi $s $v $p]



    if {[lindex $w 0] eq "literal"} {

        # The operand is a literal - extract its data type
        set u [::quadcode::typeOfLiteral [lindex $w 1]]
    } elseif {[dict exists $ptypes $w]} {

        # The operand is defined in the predecessor block, get its type
        set u [dict get $ptypes $w]







>
>





>
>
|







1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
#	cond - Condition being tested
#
# Results:
#	Returns 1 if the condition is satisfied, 0 otherwise

oo::define quadcode::transformer method jt_test_assertion {p pmask ptypes
                                                           s v cond} {
    namespace upvar ::quadcode::dataType NEXIST NEXIST

    lassign $cond kind t

    # Find the name of the variable in the predecessor
    set w [my jt_translate_phi $s $v $p]

    if {$w eq "Nothing"} {
        set u $NEXIST
    } elseif {[lindex $w 0] eq "literal"} {

        # The operand is a literal - extract its data type
        set u [::quadcode::typeOfLiteral [lindex $w 1]]
    } elseif {[dict exists $ptypes $w]} {

        # The operand is defined in the predecessor block, get its type
        set u [dict get $ptypes $w]
1042
1043
1044
1045
1046
1047
1048


1049



1050
1051
1052
1053
1054
1055
1056
#
# Results:
#	Returns the type of the operand, or 0 if the operand does not
#	represent a value.

oo::define quadcode::transformer method jt_localtype {opd localtypes} {



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



        "literal" {
            return [quadcode::typeOfLiteral [lindex $opd 1]]
        }
        "temp" - "var" {
            if {[dict exists $localtypes $opd]} {
                return [dict get $localtypes $opd]
            } else {







>
>

>
>
>







1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
#
# Results:
#	Returns the type of the operand, or 0 if the operand does not
#	represent a value.

oo::define quadcode::transformer method jt_localtype {opd localtypes} {

    namespace upvar ::quadcode::dataType NEXIST NEXIST

    switch -exact -- [lindex $opd 0] {
        "Nothing" {
            return $NEXIST
        }
        "literal" {
            return [quadcode::typeOfLiteral [lindex $opd 1]]
        }
        "temp" - "var" {
            if {[dict exists $localtypes $opd]} {
                return [dict get $localtypes $opd]
            } else {
1141
1142
1143
1144
1145
1146
1147
1148









































































































































































































1149
1150
1151
1152
1153
1154
1155
        if {[dict size $vs] > 1} {
            return 1
        }
    }

    return 0
}











































































































































































































# quadcode::transformer method jt_cleanup --
#
#	Cleans up working storage after the jump threading pass.
#
# Results:
#	None.







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
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
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
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
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
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
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
        if {[dict size $vs] > 1} {
            return 1
        }
    }

    return 0
}

# quadcode::transformer method jt_split_paths --
#
#	Duplicates basic blocks in the program to allow for jump threading.
#
# Results:
#	None.
#
# Side effects:
#	The 'bbcontent' array is augmented to hold the additional copies.
#	The 'bbpred' array is updated to reflect the revised control flows.

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

    # Destroy the predecessor relation. It will be recomputed as we
    # reconstruct the control flow
    set bbpred [lrepeat [llength $bbcontent] {}]

    # Make the required number of copies of each basic block
    set bmap [my jt_duplicate_blocks]

    # Rewrite the jumps at the end of each block to go to the new block.
    my jt_retarget_jumps $bmap
    
    return
}

# quadcode::transformer method jt_duplicate_blocks --
#
#	Makes the required number of duplicates of each block in the
#	program when performing jump threading.
#
# Results:
#	Returns a dictionary, bmap,  where
#	    [dict get $bmap $block $variant]
#	gives the number of the basic block in the new program that
#	corresponds to the requeste variant in the old program.

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

    my variable jt_variants

    # Make the required number of duplicates of each basic block.
    set newcontent {}
    set bmap {}
    set b -1
    set newb -1
    foreach vs $jt_variants bb $bbcontent {
        incr b
        lappend bbpred {}
        dict for {mask -} $vs {
            dict set bmap $b $mask [incr newb]
            lappend newcontent $bb
            my debug-jumpthread {
                puts [format "  Block %d (%#llx) -> new block %d" \
                          $b $mask $newb]
            }
        }
    }
    set bbcontent $newcontent
    return $bmap
}

# quadcode::transformer method jt_retarget_jumps --
#
#	Rewrites the jump instructions at the end of blocks after
#	performing block copying for jump threading.
#
# Parameters:
#	bmap - Dictionary describing where to find block copies.
#	       [dict get $bmap $b $variant] is the block number in
#	       the new program corresponding to variant $variant of
#	       block $b in the old program.
#
# Results:
#	None.
#
# Side effects:
#	Rewrites the jumps in the program, and re-establishes the control
#	flow graph.
#
# The rewrites encompass several cases:
#
#	0  - The block has no exits, do nothing
#	1a - The block has 1 exit, and the original had 1. End the
#	     block with an unconditional jump
#	1b - The block has 1 exit, but the original had 2. End the block
#	     with an unconditional jump, deleting any conditional jump that
#	     may have been there.
#	2  - The block has 2 exits, Rewrite both jumps to target the correct
#	     copies of the successor.

oo::define quadcode::transformer method jt_retarget_jumps {bmap} {

    my variable jt_variants

    # Walk through the blocks of the original program
    set b -1
    set oldb -1
    foreach vs $jt_variants {
        incr oldb

        # Walk through the variants, which are the blocks of the new program.
        dict for {mask targets} $vs {
            incr b
            set bb [lindex $bbcontent $b]
            lset bbcontent $b {}

            # How many jumps need to be rewritten in the block?
            
            switch -exact -- [dict size $targets] {

                0 {
                    # 0-exit block - do nothing
                    my debug-jumpthread {
                        puts "        Block $b has no exits"
                    }
                }

                1 {
                    # 1-exit block
                    if {[lindex $bb end-1 1 0] eq "bb"} {
                        my debug-jumpthread {
                            puts "        Block $b: reduce a 2-exit block\
                                          to 1-exit"
                        }
                        # Original block was two-exit
                        set start end-1
                    } else {
                        my debug-jumpthread {
                            puts "        Block $b has one exit"
                        }
                        # Original block was one-exit
                        set start end
                    }
                    # Replace jump(s) at the end of the block
                    dict for {bwas mask} $targets break
                    set newtarget [dict get $bmap $bwas $mask]
                    set newq [list jump [list bb $newtarget]]
                    set bb [lreplace $bb[set bb ""] $start end $newq]
                    my bblink $b $newtarget
                    my debug-jumpthread {
                        puts "  $b:end: $newq   # $bwas ([format %llx $mask])"
                    }
                }

                2 {
                    my debug-jumpthread {
                        puts "        Block $b has two exits"
                    }
                    # Two-exit block - rewrite the jumps
                    set q [lindex $bb end-1]
                    set newq [my jt_retarget $q $targets $bmap]
                    lset bb end-1 $newq
                    my bblink $b [lindex $newq 1 1]
                    my debug-jumpthread {
                        puts "  $b:end-1: $newq"
                    }
                    
                    set q [lindex $bb end]
                    set newq [my jt_retarget $q $targets $bmap]
                    lset bb end $newq
                    my bblink $b [lindex $newq 1 1]
                    my debug-jumpthread {
                        puts "  $b:end: $newq"
                    }
                }
            }

            # Put the basic block content back.
            lset bbcontent $b $bb
        }
    }

    return
}

# quadcode::transformer method jt_retarget --
#
#	Rewrites a single jump instruction during jump threading.
#
# Parameters:
#	q - Jump instruction being rewritten
#	targets - Dictionary whose keys are jump targets in the original
#	          program and whose values are block variants for those
#	          targets
#	bmap - Dictionary describing where to find the block variants.
#	       [dict get $bmap $b $variant] gives the basic block
#	       number in the new program corresponding to variant $variant
#	       of block $b in the original program.
#
# Results:
#	Returns the rewritten instruction.

oo::define quadcode::transformer method jt_retarget {q targets bmap} {

    set tgt [lindex $q 1 1]
    set var [dict get $targets $tgt]
    lset q 1 1 [dict get $bmap $tgt $var]

    return $q
}

# quadcode::transformer method jt_cleanup --
#
#	Cleans up working storage after the jump threading pass.
#
# Results:
#	None.
Changes to quadcode/ssa.tcl.
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#		 eliminates variable-to-variable copies in the process,
#		 and fills in the arguments to phi functions.

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

	my bbidom
	my bblevel
	my bbssa1
	my bbssa2
    }


# bbidom --
#
#	Compute the immediate dominators of the basic blocks
#







|
|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#		 eliminates variable-to-variable copies in the process,
#		 and fills in the arguments to phi functions.

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

	my bbidom
	my bblevel
	set aliasFor [my bbssa1]
	my bbssa2 $aliasFor
    }


# bbidom --
#
#	Compute the immediate dominators of the basic blocks
#
220
221
222
223
224
225
226





227
228
229
230
231
232
233



234
235
236
237
238
239
240
#
#	First pass of the SSA conversion
#
# Preconditions:
#	The immediate dominance tree (bbidom and bbkids) must be known,
#	and dominance depths (bblevel) must have been calculated.
#





# Side effects:
#	Contents of basic blocks are rewritten to add phi functions at the
#	beginning of the blocks for the variables that converge on the block.
#	The 'vars' variable is set to a list of variable names in
#	the program.

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



    # Find out the 'globals' - the variables that flow from one block
    # to another - and the basic blocks in which variables are written.

    set global {}
    set vardict {}
    set writers {}
    set b -1







>
>
>
>
>







>
>
>







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
#
#	First pass of the SSA conversion
#
# Preconditions:
#	The immediate dominance tree (bbidom and bbkids) must be known,
#	and dominance depths (bblevel) must have been calculated.
#
# Results:
#	Returns a dictionary that describes the placed phi operations.
#	Keys are the result variables of the phi's; values are the
#	variable names that the phi's replace.
#
# Side effects:
#	Contents of basic blocks are rewritten to add phi functions at the
#	beginning of the blocks for the variables that converge on the block.
#	The 'vars' variable is set to a list of variable names in
#	the program.

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

    set varcount {}
    
    # Find out the 'globals' - the variables that flow from one block
    # to another - and the basic blocks in which variables are written.

    set global {}
    set vardict {}
    set writers {}
    set b -1
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
		}
	    }
	}
    }

    # Find places to insert phi nodes


    set phis [lrepeat [llength $bbcontent] {}]
    dict for {v -} $global {
	if {[dict exists $writers $v]} {
	    if {[dict exists $writers $v]} {
		set w [dict keys [dict get $writers $v]]
	    } else {
		set w {}
	    }
	    foreach n [my bbfrontier+ $w] {

		set list [lindex $phis $n]
		lset phis $n {}
		lappend list [list phi $v]
		lset phis $n $list

	    }
	}
    }

    # Insert phi nodes

    set b 0
    foreach content $bbcontent phi $phis {
	lset bbcontent $b [concat $phi $content]
	incr b
    }

    set vars [dict keys $vardict]
    return

}

# quadcode::transformer method bbssa2 -
#
#	Second pass of SSA transformation
#
# Preconditions:
#	A list of variables in the program must be in the 'vars' variable
#	(quads-list-vars will compute this). The immediate dominance tree
#	(bbidom and bbkids) must have been computed, and dominance frontiers
#	(bbfrontier) must be known. Dummy phi functions must have already
#	been placed at confluence points (bbssa1).
#





# Results:
#	None.
#
# Side effects:
#	Rewrites all basic blocks to begin with phi functions for confluent
#	variables, and removes copies.

oo::define quadcode::transformer method bbssa2 {} {
    my debug-ssa {
	puts "before variable renaming:"
	my dump-bb
    }

    set stack {}
    set varcount {}
    foreach v $vars {
	dict set stack $v [list "Nothing"]
    }
    my renamevars 0 stack

    my debug-ssa {
	puts "after variable renaming:"
	my dump-bb
    }

    unset vars







>









>


|

>













|














>
>
>
>
>







|






<



|







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
		}
	    }
	}
    }

    # Find places to insert phi nodes

    set aliasFor {}
    set phis [lrepeat [llength $bbcontent] {}]
    dict for {v -} $global {
	if {[dict exists $writers $v]} {
	    if {[dict exists $writers $v]} {
		set w [dict keys [dict get $writers $v]]
	    } else {
		set w {}
	    }
	    foreach n [my bbfrontier+ $w] {
		set newv [my newVarInstance $v]
		set list [lindex $phis $n]
		lset phis $n {}
		lappend list [list phi $newv]
		lset phis $n $list
		dict set aliasFor $newv $v
	    }
	}
    }

    # Insert phi nodes

    set b 0
    foreach content $bbcontent phi $phis {
	lset bbcontent $b [concat $phi $content]
	incr b
    }

    set vars [dict keys $vardict]
    return $aliasFor

}

# quadcode::transformer method bbssa2 -
#
#	Second pass of SSA transformation
#
# Preconditions:
#	A list of variables in the program must be in the 'vars' variable
#	(quads-list-vars will compute this). The immediate dominance tree
#	(bbidom and bbkids) must have been computed, and dominance frontiers
#	(bbfrontier) must be known. Dummy phi functions must have already
#	been placed at confluence points (bbssa1).
#
# Parameters:
#	aliasFor - Dictionary that, for each placed 'phi' operation, lists
#	           the variable that the 'phi' replaces. Keys are the result
#                  variables of the 'phi' instructions.
#
# Results:
#	None.
#
# Side effects:
#	Rewrites all basic blocks to begin with phi functions for confluent
#	variables, and removes copies.

oo::define quadcode::transformer method bbssa2 {aliasFor} {
    my debug-ssa {
	puts "before variable renaming:"
	my dump-bb
    }

    set stack {}

    foreach v $vars {
	dict set stack $v [list "Nothing"]
    }
    my renamevars 0 stack $aliasFor

    my debug-ssa {
	puts "after variable renaming:"
	my dump-bb
    }

    unset vars
548
549
550
551
552
553
554


555
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
#	Renames the variables in a basic block and its dominance children
#	to the correct names for SSA form
#
# Parameters:
#	b - Basic block number
#	vstack - Name of a variable in callers scope that maintains the
#		 stack of current variable names.


#
# Results:
#	None.
#
# Side effects:
#	Basic blocks are rewritten. Phi functions are filled in and copies
#	are removed.
#
# This procedure is called once in bbssa2 for the entry block. It
# recurses down the dominance tree to fill in the variables for all
# the other blocks.

oo::define quadcode::transformer method renamevars {b vstack} {
    upvar 1 $vstack stack





    # Iterate over the quads in the basic block
    set newcontent {}
    set oldcontent [lindex $bbcontent $b]

    foreach q $oldcontent {
	set op [lindex $q 0]
	set lhs [lindex $q 1]

	# Rewrite variable uses
	if {$op ne "phi"} {
	    set i 2







>
>












|

>
>
>
>




<







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
589
590
591
592
593

594
595
596
597
598
599
600
#	Renames the variables in a basic block and its dominance children
#	to the correct names for SSA form
#
# Parameters:
#	b - Basic block number
#	vstack - Name of a variable in callers scope that maintains the
#		 stack of current variable names.
#	aliasFor - Dictionary that maps the names of phi results to the
#	           names of the program variables that they replace.
#
# Results:
#	None.
#
# Side effects:
#	Basic blocks are rewritten. Phi functions are filled in and copies
#	are removed.
#
# This procedure is called once in bbssa2 for the entry block. It
# recurses down the dominance tree to fill in the variables for all
# the other blocks.

oo::define quadcode::transformer method renamevars {b vstack aliasFor} {
    upvar 1 $vstack stack

    my debug-ssa {
	puts "Rename vars in basic block $b"
    }

    # Iterate over the quads in the basic block
    set newcontent {}
    set oldcontent [lindex $bbcontent $b]

    foreach q $oldcontent {
	set op [lindex $q 0]
	set lhs [lindex $q 1]

	# Rewrite variable uses
	if {$op ne "phi"} {
	    set i 2
591
592
593
594
595
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
625
626
627
628
629
630
631
632
633



634
635
636






637
638
639
640
641
642
643
	# Replace unsets with Nothing
	if {$op eq "unset"} {
	    set newlhs Nothing
	    set stk [dict get $stack $lhs]
	    dict set stack $lhs {}
	    lappend stk $newlhs
	    dict set stack $lhs $stk










	} elseif {[lindex $lhs 0] in {"temp" "var"}} {
	    set newlhs [my newVarInstance $lhs]
	    lset q 1 $newlhs
	    lappend newcontent $q
	    set stk [dict get $stack $lhs]
	    dict set stack $lhs {}
	    lappend stk $newlhs
	    dict set stack $lhs $stk



	} else {
	    lappend newcontent $q
	}
    }

    # Patch the phi's in the successor blocks.
    foreach s [my bbsucc $b] {
	set j 0
	foreach q [lindex $bbcontent $s] {
	    if {[lindex $q 0] eq "phi"} {



		set lhs [lrange [dict get $q "phi"] 0 1]



		set source [lindex [dict get $stack $lhs] end]



		lappend q [list bb $b] $source
		lset bbcontent $s $j $q
	    } else {
		break
	    }
	    incr j
	}
    }
    lset bbcontent $b $newcontent

    # Recurse down the dominance tree
    foreach k [lindex $bbkids $b] {
	my renamevars $k stack
    }

    # Pop the variables that we pushed



    foreach q $oldcontent {
	set lhs [lindex $q 1]
	if {[lindex $lhs 0] in {"temp" "var"}} {






	    set stk [dict get $stack $lhs]
	    dict set stack $lhs {}
	    set stk [lreplace $stk[set stk {}] end end]
	    dict set stack $lhs $stk
	}
    }








>
>
>
>
>
>
>
>
>
>








>
>
>










>
>
>
|
>
>
>
|
>
>
>












|



>
>
>

|

>
>
>
>
>
>







611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
	# Replace unsets with Nothing
	if {$op eq "unset"} {
	    set newlhs Nothing
	    set stk [dict get $stack $lhs]
	    dict set stack $lhs {}
	    lappend stk $newlhs
	    dict set stack $lhs $stk
	} elseif {$op eq "phi"} {
	    set oldv [dict get $aliasFor $lhs]
	    my debug-ssa {
		puts "  rename $oldv -> $var"
	    }
	    lappend newcontent $q
	    set stk [dict get $stack $oldv]
	    dict set stack $oldv {}
	    lappend stk $lhs
	    dict set stack $oldv $stk
	} elseif {[lindex $lhs 0] in {"temp" "var"}} {
	    set newlhs [my newVarInstance $lhs]
	    lset q 1 $newlhs
	    lappend newcontent $q
	    set stk [dict get $stack $lhs]
	    dict set stack $lhs {}
	    lappend stk $newlhs
	    dict set stack $lhs $stk
	    my debug-ssa {
		puts "  rename $lhs -> $newlhs"
	    }
	} else {
	    lappend newcontent $q
	}
    }

    # Patch the phi's in the successor blocks.
    foreach s [my bbsucc $b] {
	set j 0
	foreach q [lindex $bbcontent $s] {
	    if {[lindex $q 0] eq "phi"} {
		my debug-ssa {
		    puts "Patch $q in successor block $s"
		}
		set oldv [dict get $aliasFor [lindex $q 1]]
		my debug-ssa {
		    puts "  it originally referred to $oldv"
		}
		set source [lindex [dict get $stack $oldv] end]
		my debug-ssa {
		    puts "  and now includes $b -> $source"
		}
		lappend q [list bb $b] $source
		lset bbcontent $s $j $q
	    } else {
		break
	    }
	    incr j
	}
    }
    lset bbcontent $b $newcontent

    # Recurse down the dominance tree
    foreach k [lindex $bbkids $b] {
	my renamevars $k stack $aliasFor
    }

    # Pop the variables that we pushed
    my debug-ssa {
	puts "Pop vars when leaving block $b"
    }
    foreach q $oldcontent {
	lassign $q op lhs
	if {[lindex $lhs 0] in {"temp" "var"}} {
	    if {$op eq "phi"} {
		set lhs [dict get $aliasFor $lhs]
	    }
	    my debug-ssa {
		puts "   pop $lhs"
	    }
	    set stk [dict get $stack $lhs]
	    dict set stack $lhs {}
	    set stk [lreplace $stk[set stk {}] end end]
	    dict set stack $lhs $stk
	}
    }

1153
1154
1155
1156
1157
1158
1159



































































































    }
    
    my debug-convssa {
	puts "convssa: after copy insertion:"
	my dump-bb
    }
}










































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
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
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
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
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
    }
    
    my debug-convssa {
	puts "convssa: after copy insertion:"
	my dump-bb
    }
}

# quadcode::transformer method deconstructSSA --
#
#	Converts a quadcode sequence from SSA form back to multiple
#	assignments.
#
# Results:
#	None.
#
# Side effects:
#	Phi operations are removed and assignment operations are added.
#
# This transformation is used in passes that make sweeping changes to
# program structure. In some of these passes, it is easier to destroy SSA
# form completely and reconstruct it afterward than it is to attempt to
# track the data flows.

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

    my debug-decontstructSSA {
	puts "Deconstruct SSA form for [my full-name]:"
    }

    # Walk through the basic blocks, rewriting each one in turn
    set newcontent {}
    set b -1
    foreach bb $bbcontent {
	incr b
	my debug-deconstructSSA {
	    puts "  bb $b:"
	}

	# Copy over the quads in one block, removing the phis and stopping
	# at the first jump at the end of the block
	set newb {}
	set newpc -1
	set pc -1
	set singleExit 1
	foreach q $bb {
	    incr pc
	    if {[lindex $q 0 0] eq "phi"} {
		continue
	    }
	    if {[lindex $q 0 0] eq "jump"} {
		break
	    }
	    if {[lindex $q 1 0] eq "bb"} {
		set singleExit 0
	    }
	    my debug-deconstructSSA {
		puts "    [incr newpc]: $q"
	    }
	    lappend newb $q
	}

	# If the block is single-exit, examine the phi operations
	# in the successor block and convert them to assignments here.

	if {[lindex $q 0 0] eq "jump" && $singleExit} {
	    set s [lindex $q 1 1]
	    set bkey [list bb $b]
	    my debug-deconstructSSA {
		puts "      # assignments from block $s:"
	    }
	    foreach q2 [lindex $bbcontent $s] {
		set argl [lassign $q2 opcode dest]
		if {[lindex $opcode 0] ne "phi"} {
		    break
		}
		set src [dict get $argl $bkey]
		if {$src eq "Nothing"} {
		    set q3 [list unset $dest]
		} else {
		    set q3 [list copy $dest $src]
		}
		my debug-deconstructSSA {
		    puts "    [incr newpc]: $q3"
		}
		lappend newb $q3
	    }
	}

	# Put the jump back in at the end of a single-exit block
	if {[lindex $q 0 0] eq "jump"} {
	    my debug-deconstructSSA {
		puts "    [incr newpc]: $q"
	    }
	    lappend newb $q
	}
	    
	# Done with the new basic block
	lappend newcontent $newb
    }

    # Replace the program with the rewritten one

    set bbcontent $newcontent
    return
}