Check-in [68b8d45ad1]

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

Overview
Comment:All test cases pass once more, but only by virtue of a hideous hack inside typeunroll to keep it below a certain number of splits. The five cases, lcmRange, linesearch::getAllLines2, qsort, wordcounter2 and xsum2 all appear to go into runaway behaviour, growing program size exponentially (and perhaps not even converging).
Timelines: family | ancestors | descendants | both | kbk-impure
Files: files | file ages | folders
SHA1: 68b8d45ad121b2eb1b83ee303bbd6d292e99d5f8
User & Date: kbk 2017-02-15 20:54:17.397
Context
2017-02-19
20:27
Avoid runaways by constraining how many copies may be introduced by splitting a basic block check-in: 54c6d0489d user: kbk tags: kbk-impure
2017-02-15
20:54
All test cases pass once more, but only by virtue of a hideous hack inside typeunroll to keep it below a certain number of splits. The five cases, lcmRange, linesearch::getAllLines2, qsort, wordcounter2 and xsum2 all appear to go into runaway behaviour, growing program size exponentially (and perhaps not even converging). check-in: 68b8d45ad1 user: kbk tags: kbk-impure
03:57
NOT WORKING: Fix bugs to make a few more test cases compile check-in: 4bdc54e7fd user: kbk tags: kbk-impure, not working
Changes
Unified Diff Ignore Whitespace Patch
Changes to demo.tcl.
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
    # Nonexistent variables
    nextest1
    nextest2
    nextest3
    nextest4
    # Array operations
    wordcounter1
    # wordcounter2				BUG
    wordcounter3
    # Calls of uncompiled code
    calltest
    calltest2
    calltest3
    # The interprocedural tests
    mrtest::*
    coscaller
    xsum
    # xsum2					BUG
    # Miscellaneous other tests
    bctest
    asmtest
    # Combined feature tests
    # lcmRange					BUG
    bug-0616bcf08e::*
    # qsort					BUG
    impure
    # impure2					BUG
    comps
    linesearch::colinear
    linesearch::sameline
    # linesearch::getAllLines1			BUG
    # linesearch::getAllLines2			BUG
    # flightawarebench::*
}

#########################################################################
#
# Support procedures for the demonstration runner.








|









|




|

|

|



|
|







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
    # Nonexistent variables
    nextest1
    nextest2
    nextest3
    nextest4
    # Array operations
    wordcounter1
    wordcounter2
    wordcounter3
    # Calls of uncompiled code
    calltest
    calltest2
    calltest3
    # The interprocedural tests
    mrtest::*
    coscaller
    xsum
    xsum2	
    # Miscellaneous other tests
    bctest
    asmtest
    # Combined feature tests
    lcmRange
    bug-0616bcf08e::*
    qsort
    impure
    # impure2					BUG FAIL IMPURE
    comps
    linesearch::colinear
    linesearch::sameline
    linesearch::getAllLines1			BUG
    linesearch::getAllLines2
    # flightawarebench::*
}

#########################################################################
#
# Support procedures for the demonstration runner.

Changes to quadcode/duchain.tcl.
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
    if {[dict exists $duchain $v]} {
	set uses [dict get $duchain $v]
	
	if {[dict size $uses] > 1} {
	    
	    # The variable appears in more than one block, so ipso facto
	    # is not local.

	    return 1
	}
	dict for {k count} $uses break
	
	if {$k != $b} {
	    
	    # All uses are in some other block, so variable isn't local
	    # to this block.
	    
	    return 1
	}
	
	# Scan the phi's in this block for a use.
	
	foreach q [lindex $bbcontent $b] {
	    if {[lindex $q 0 0] ne "phi"} {
		break
	    }
	    dict for {from what} $q {
		if {$v eq $what} {
		    return 1;	# used in a phi in the current block
		}
	    }
	}
    }








>


















|







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
    if {[dict exists $duchain $v]} {
	set uses [dict get $duchain $v]
	
	if {[dict size $uses] > 1} {
	    
	    # The variable appears in more than one block, so ipso facto
	    # is not local.

	    return 1
	}
	dict for {k count} $uses break
	
	if {$k != $b} {
	    
	    # All uses are in some other block, so variable isn't local
	    # to this block.
	    
	    return 1
	}
	
	# Scan the phi's in this block for a use.
	
	foreach q [lindex $bbcontent $b] {
	    if {[lindex $q 0 0] ne "phi"} {
		break
	    }
	    foreach {from what} [lrange $q 2 end] {
		if {$v eq $what} {
		    return 1;	# used in a phi in the current block
		}
	    }
	}
    }

285
286
287
288
289
290
291
292

293

294
295

296

297

    my variable bbcontent

    set retval {}
    set bb [lindex $bbcontent $b]
    foreach q $bb {
	set res [lindex $q 1]
	if {[lindex $res 0] in {"var" "temp"} && [my usedElsewhere $res $b]} {

	    dict set retval [lrange $res 0 1] $res

	}
    }

    return [dict values $retval]

}







|
>
|
>
|
|
>
|
>

286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302

    my variable bbcontent

    set retval {}
    set bb [lindex $bbcontent $b]
    foreach q $bb {
	set res [lindex $q 1]
	if {[lindex $res 0] in {"var" "temp"}} {
	    if {[my usedElsewhere $res $b]} {
#		dict set retval [lrange $res 0 1] $res
		lappend retval $res
	    }
	}
    }
#   return [dict values $retval]
    return $retval
}
Changes to quadcode/flatten.tcl.
132
133
134
135
136
137
138

139
140
	puts "param types: $ptype"
	puts "var types:"
	foreach {v type} [lsort -dictionary -index 0 -stride 2 $newtypes] {
	    puts "  $v: $type ([nameOfType $type])"
	}
	my dump-quadcode
    }

    return [list [dict get $types return] $ptype $newtypes $quads]
}







>


132
133
134
135
136
137
138
139
140
141
	puts "param types: $ptype"
	puts "var types:"
	foreach {v type} [lsort -dictionary -index 0 -stride 2 $newtypes] {
	    puts "  $v: $type ([nameOfType $type])"
	}
	my dump-quadcode
    }
    puts "[llength $quads] quads after flattening"
    return [list [dict get $types return] $ptype $newtypes $quads]
}
Changes to quadcode/specializer.tcl.
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
oo::define quadcode::specializer method makeInstance {procName argTypes} {

    # Run type widening on the instance, and record its new dependencies.
    unset -nocomplain instanceBeingAnalyzed
    set instance [list $procName $argTypes]
    set inf [dict get $typeInf $instance]
    puts ${procName}([lmap ty $argTypes {nameOfType $ty}]);		# TEMP
    $inf retransform
    return [$inf getFlattenedQuads]
}

# quadcode::specializer method PrecedenceWorker --
#
#	Method that visits each node of a call graph to assign precedence.
#







<







348
349
350
351
352
353
354

355
356
357
358
359
360
361
oo::define quadcode::specializer method makeInstance {procName argTypes} {

    # Run type widening on the instance, and record its new dependencies.
    unset -nocomplain instanceBeingAnalyzed
    set instance [list $procName $argTypes]
    set inf [dict get $typeInf $instance]
    puts ${procName}([lmap ty $argTypes {nameOfType $ty}]);		# TEMP

    return [$inf getFlattenedQuads]
}

# quadcode::specializer method PrecedenceWorker --
#
#	Method that visits each node of a call graph to assign precedence.
#
Changes to quadcode/transformer.tcl.
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
    #
    # Side effects:
    #	Infers types one final time, eliminates any redundant type checks,
    #	and runs the cleanup optimizations.

    method retransform {} {
	foreach pass {
	    inferTypes
	    cleanupNarrow
	    deadjump
	    deadbb
	    deadvars
	    deadphis
	    typeunroll
	    inferTypes
	} {
	    dict set timings $pass [lindex [time [list my $pass]] 0]
	}
	my debug-timings {
	    dict for {pass usec} $timings {







<
<
<
<
<
<







330
331
332
333
334
335
336






337
338
339
340
341
342
343
    #
    # Side effects:
    #	Infers types one final time, eliminates any redundant type checks,
    #	and runs the cleanup optimizations.

    method retransform {} {
	foreach pass {






	    typeunroll
	    inferTypes
	} {
	    dict set timings $pass [lindex [time [list my $pass]] 0]
	}
	my debug-timings {
	    dict for {pass usec} $timings {
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
    #
    # Results:
    #	Returns a four-element list: return type, list of parameter types,
    #	list of variable types, list of quadcode instructions.

    method getFlattenedQuads {} {

	# Do a final pass of type inference.
	my retransform

	# Insert instructions to widen types at phis.
	my widen

	# Perform the analysis of live ranges, inserting instructions
	# as needed to adjust reference counts at phis and to free







|







377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
    #
    # Results:
    #	Returns a four-element list: return type, list of parameter types,
    #	list of variable types, list of quadcode instructions.

    method getFlattenedQuads {} {

	# Do a final pass of type inference and perform loop transformations
	my retransform

	# Insert instructions to widen types at phis.
	my widen

	# Perform the analysis of live ranges, inserting instructions
	# as needed to adjust reference counts at phis and to free
Changes to quadcode/types.tcl.
357
358
359
360
361
362
363
364
365

366
367
368
369
370
371
372
    
    my debug-inferTypes {
	puts "Before type inference:"
	my dump-bb
    }

    namespace upvar ::quadcode::dataType BOTTOM BOTTOM FAIL FAIL
    
    # Initialize all types to BOTTOM

    dict for {v -} $udchain {
	dict set types $v $BOTTOM
    }
    dict set types return $BOTTOM
    
    # Put all basic blocks on the worklist for processing in depth-first
    # order







|

>







357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    
    my debug-inferTypes {
	puts "Before type inference:"
	my dump-bb
    }

    namespace upvar ::quadcode::dataType BOTTOM BOTTOM FAIL FAIL

    # Initialize all types to BOTTOM
    set types {}
    dict for {v -} $udchain {
	dict set types $v $BOTTOM
    }
    dict set types return $BOTTOM
    
    # Put all basic blocks on the worklist for processing in depth-first
    # order
420
421
422
423
424
425
426



427
428
429
430
431
432
433
	    }
	}
    }
    my debug-inferTypes {
	puts "Types inferred:"
	foreach {v type} [lsort -dictionary -index 0 -stride 2 $types] {
	    puts [format "%s: %#x (%s)" $v: $type [nameOfType $type]]



	}
    }
}

# typeOfResult --
#
#	Computes the type of the result of an operation







>
>
>







421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
	    }
	}
    }
    my debug-inferTypes {
	puts "Types inferred:"
	foreach {v type} [lsort -dictionary -index 0 -stride 2 $types] {
	    puts [format "%s: %#x (%s)" $v: $type [nameOfType $type]]
	    if {$type < 0} {
		puts "                ~[nameOfType [expr {~$type}]]"
	    }
	}
    }
}

# typeOfResult --
#
#	Computes the type of the result of an operation
Changes to quadcode/typeunroll.tcl.
436
437
438
439
440
441
442





443
444
445
446
447

448
449
450
451
452
453


454
455
456
457
458
459
460

    my debug-typeunroll {
	puts "Before unrolling loops for data types:"
	my dump-bb
    }

    set done 1





    set maxSplits [llength $bbcontent]
    if {$maxSplits > 100} {
	set maxSplits 100
    }
    # set maxSplits 0

    set splitCount 0
    
    while {$done && $splitCount < $maxSplits} {

	incr splitCount
	puts "SPLIT $splitCount"



	# The preceding few rounds of optimization have made various changes
	# to the flow graph without keeping bbidom, bbkids, bbnlevels, bblevel
	# in sync. Rebuild them now.
	
	my inferTypes
	my cleanupNarrow







>
>
>
>
>




|
>

|
|



>
>







436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468

    my debug-typeunroll {
	puts "Before unrolling loops for data types:"
	my dump-bb
    }

    set done 1

    # FIXME: It would be better to track down the source of runaways and
    #        resolve the underlying problem. For now, defend against runaways
    #	     by limiting the number of splits that we will perform

    set maxSplits [llength $bbcontent]
    if {$maxSplits > 100} {
	set maxSplits 100
    }
    set maxSplits 20; # for now... just to keep runaways in check
    puts "limit splits to $maxSplits"
    set splitCount 0

    while {$done} {

	incr splitCount
	puts "SPLIT $splitCount"

	if {$splitCount > $maxSplits} break

	# The preceding few rounds of optimization have made various changes
	# to the flow graph without keeping bbidom, bbkids, bbnlevels, bblevel
	# in sync. Rebuild them now.
	
	my inferTypes
	my cleanupNarrow
469
470
471
472
473
474
475







476
477



478
479




480
481
482
483
484
485
486
	foreach b [my bborder] {
	    set bb [lindex $bbcontent $b]
	    set pc -1
	    foreach q $bb {
		set done 0
		incr pc
		if {[lindex $q 0] ne "phi"} break







		set v1 [lindex $q 3]
		set t1 [typeOfOperand $types $v1]



		foreach {origin v2} [lrange $q 4 end] {
		    set t2 [typeOfOperand $types $v2]





		    # Split if one value is IMPURE, EMPTY or OTHERSTRING,
		    # while the other value is none of the above.

		    set expensive1 \
			[expr {($t1 & ($IMPURE | $EMPTY | $OTHERSTRING)) != 0}]
		    set expensive2 \







>
>
>
>
>
>
>


>
>
>


>
>
>
>







477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
	foreach b [my bborder] {
	    set bb [lindex $bbcontent $b]
	    set pc -1
	    foreach q $bb {
		set done 0
		incr pc
		if {[lindex $q 0] ne "phi"} break
		# FIXME: Find out where runaways are coming from before
		#        just putting in an arbitrary safety limit
		# Safety: Limit number of predecessors to curb runaways
		if {[llength $q] > 18} {
		    puts "Bail out: too many predecessors to split"
		    break
		}
		set v1 [lindex $q 3]
		set t1 [typeOfOperand $types $v1]
		if {$t1 == 0} {
		    error "NOTHING ends up in $v1 at $b:$pc: $q"
		}
		foreach {origin v2} [lrange $q 4 end] {
		    set t2 [typeOfOperand $types $v2]

		    if {$t2 == 0} {
			error "NOTHING ends up in $v2 at $b:$pc: $q"
		    }

		    # Split if one value is IMPURE, EMPTY or OTHERSTRING,
		    # while the other value is none of the above.

		    set expensive1 \
			[expr {($t1 & ($IMPURE | $EMPTY | $OTHERSTRING)) != 0}]
		    set expensive2 \
500
501
502
503
504
505
506


507
508
509

510
511
512
513

514
515
516
517
518
519
520
521
		    if {!$done && ($t2 & $OTHERSTRING)
			&& [::quadcode::dataType::isa $t1 \
				[expr {$IMPURE | $NUMERIC | $BOOLEAN}]]} {
			set done 1
		    }

		    if {$done} {


			my debug-typeunroll {
			    puts "$b:$pc: $q"
			    puts "\tis a candidate for node splitting"

			    puts "   $v1 = $t1 [nameOfType $t1]"
			    if {$t1 < 0} {
				puts "       ~[nameOfType [expr {~$t1}]]"
			    }

			    puts "   $v2 = $t2 [nameOfType $t2]"
			    if {$t2 < 0} {
				puts "       ~[nameOfType [expr {~$t2}]]"
			    }
			}
			break
		    } else {
			my debug-typeunroll {







>
>


|
>
|



>
|







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
		    if {!$done && ($t2 & $OTHERSTRING)
			&& [::quadcode::dataType::isa $t1 \
				[expr {$IMPURE | $NUMERIC | $BOOLEAN}]]} {
			set done 1
		    }

		    if {$done} {
			puts "split block $b: [llength $bb] quads and\
                              [expr {([llength $q] - 1)/2}] predecessors"
			my debug-typeunroll {
			    puts "$b:$pc: $q"
			    puts [format "\tis a candidate for node splitting\
			                  %s = %#llx %s" \
				      $v1 $t1 [nameOfType $t1]]
			    if {$t1 < 0} {
				puts "       ~[nameOfType [expr {~$t1}]]"
			    }
			    puts [format "%s = %#llx %s" \
				      $v2 $t2 [nameOfType $t2]]
			    if {$t2 < 0} {
				puts "       ~[nameOfType [expr {~$t2}]]"
			    }
			}
			break
		    } else {
			my debug-typeunroll {
541
542
543
544
545
546
547










548
549
550
551
552
553
554
	    my tu_cloneBB $b
	    my debug-typeunroll {
		puts "After splitting:"
		my dump-bb
	    }
	}
    }










}
    
# quadcode::transformer method tu_cloneBB --
#
#	Clones a basic block that has been identified as a candidate
#	for splitting.
#







>
>
>
>
>
>
>
>
>
>







567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
	    my tu_cloneBB $b
	    my debug-typeunroll {
		puts "After splitting:"
		my dump-bb
	    }
	}
    }

    # Final cleanup

    my inferTypes
    my cleanupNarrow
    my deadjump
    my deadbb
    my deadvars
    my deadphis

}
    
# quadcode::transformer method tu_cloneBB --
#
#	Clones a basic block that has been identified as a candidate
#	for splitting.
#
Changes to quadcode/varargs.tcl.
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
    set commandList {}
    if {$specializer ne {}} {
	set commandList [$specializer commandList]
    }

    # Walk through all the quadcodes

    set tempidx 0
    set b -1
    foreach content $bbcontent {
	incr b
	set newcontent {}
	set pc -1
	foreach q $content {
	    incr pc







<







56
57
58
59
60
61
62

63
64
65
66
67
68
69
    set commandList {}
    if {$specializer ne {}} {
	set commandList [$specializer commandList]
    }

    # Walk through all the quadcodes


    set b -1
    foreach content $bbcontent {
	incr b
	set newcontent {}
	set pc -1
	foreach q $content {
	    incr pc
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
	    # Begin by handling 'args'. Construct a list of the arguments
	    # that will fill 'args', and add it to the 'invoke'.
	    # NOTE: This means that compiled-to-compiled 'invoke' MUST NOT
	    # call the thunk, which will expect the list to be flattened.

	    set argl $argv
	    if {[lindex $argv end] eq "args"} {
		set args [list temp args [incr tempidx]]
		set newq1 \
		    [lreplace $q 0 [expr {[llength $argv] + 1}] "list" $args]
		if {[llength $q] > [llength $argl] + 3} {
		    set newq2 [lreplace $q [expr {[llength $argl] + 2}] end]
		} else {
		    set newq2 $q
		}







|







94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
	    # Begin by handling 'args'. Construct a list of the arguments
	    # that will fill 'args', and add it to the 'invoke'.
	    # NOTE: This means that compiled-to-compiled 'invoke' MUST NOT
	    # call the thunk, which will expect the list to be flattened.

	    set argl $argv
	    if {[lindex $argv end] eq "args"} {
		set args [my newVarInstance {temp args}]
		set newq1 \
		    [lreplace $q 0 [expr {[llength $argv] + 1}] "list" $args]
		if {[llength $q] > [llength $argl] + 3} {
		    set newq2 [lreplace $q [expr {[llength $argl] + 2}] end]
		} else {
		    set newq2 $q
		}