2018-03-29 15:44:06 +00:00
|
|
|
SixtyPical Fallthru
|
|
|
|
===================
|
|
|
|
|
|
|
|
This is a test suite, written in [Falderal][] format, for SixtyPical's
|
|
|
|
ability to detect which routines make tail calls to other routines,
|
|
|
|
and thus can be re-arranged to simply "fall through" to them.
|
|
|
|
|
2018-04-04 09:15:06 +00:00
|
|
|
The theory is as follows.
|
|
|
|
|
|
|
|
SixtyPical supports a `goto`, but it can only appear in tail position.
|
|
|
|
If a routine r1 ends with a unique `goto` to a fixed routine r2 it is said
|
|
|
|
to *potentially fall through* to r2.
|
|
|
|
|
|
|
|
A *unique* `goto` means that there are not multiple different `goto`s in
|
|
|
|
tail position (which can happen if, for example, an `if` is the last thing
|
|
|
|
in a routine, and each branch of that `if` ends with a different `goto`.)
|
|
|
|
|
|
|
|
A *fixed* routine means, a routine which is known at compile time, not a
|
|
|
|
`goto` through a vector.
|
|
|
|
|
|
|
|
Consider the set R of all routines in the program.
|
|
|
|
|
|
|
|
Every routine r1 ∈ R either potentially falls through to a single routine
|
|
|
|
r2 ∈ R (r2 ≠ r1) or it does not potentially fall through to any routine.
|
|
|
|
We can say out(r1) = {r2} or out(r1) = ∅.
|
|
|
|
|
|
|
|
Every routine r ∈ R in this set also has a set of zero or more
|
|
|
|
routines from which it is potentially falled through to by. Call this
|
|
|
|
in(r). It is the case that out(r1) = {r2} → r1 ∈ in(r2).
|
|
|
|
|
|
|
|
We can trace out the connections by following the in- or our- sets of
|
|
|
|
a given routine. Because each routine potentially falls through to only
|
|
|
|
a single routine, the structures we find will be tree-like, not DAG-like.
|
|
|
|
|
|
|
|
But they do permit cycles.
|
|
|
|
|
|
|
|
So, we first break those cycles. We will be left with out() sets which
|
|
|
|
are disjoint trees, i.e. if r1 ∈ in(r2), then r1 ∉ in(r3) for all r3 ≠ r2.
|
|
|
|
|
|
|
|
We then follow an algorithm something like this. Treat R as a mutable
|
|
|
|
set and start with an empty list L. Then,
|
|
|
|
|
|
|
|
- Pick a routine r from R where out(r) = ∅.
|
|
|
|
- Find the longest chain of routines r1,r2,...rn in R where out(r1) = {r2},
|
|
|
|
out(r2} = {r3}, ... out(rn-1) = {rn}, and rn = r.
|
|
|
|
- Remove (r1,r2,...,rn) from R and append them to L in that order.
|
|
|
|
Mark (r1,r2,...rn-1) as "will have their final `goto` removed."
|
|
|
|
- Repeat until R is empty.
|
|
|
|
|
|
|
|
When times comes to generate code, generate it in the order given by L.
|
|
|
|
|
2018-03-29 15:44:06 +00:00
|
|
|
[Falderal]: http://catseye.tc/node/Falderal
|
|
|
|
|
2018-04-04 10:54:50 +00:00
|
|
|
-> Functionality "Dump fallthru info for SixtyPical program" is implemented by
|
|
|
|
-> shell command "bin/sixtypical --optimize-fallthru --dump-fallthru-info --analyze-only --traceback %(test-body-file)"
|
2018-03-29 15:44:06 +00:00
|
|
|
|
2018-04-04 10:54:50 +00:00
|
|
|
-> Tests for functionality "Dump fallthru info for SixtyPical program"
|
2018-03-29 15:44:06 +00:00
|
|
|
|
|
|
|
A single routine, obviously, falls through to nothing and has nothing fall
|
|
|
|
through to it.
|
|
|
|
|
|
|
|
| define main routine
|
|
|
|
| {
|
|
|
|
| }
|
|
|
|
= {}
|
|
|
|
|
|
|
|
If main does a `goto foo`, then it can fall through to `foo`.
|
|
|
|
|
|
|
|
| define foo routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define main routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| goto foo
|
|
|
|
| }
|
|
|
|
= {
|
|
|
|
= "foo": [
|
|
|
|
= "main"
|
|
|
|
= ]
|
|
|
|
= }
|
|
|
|
|
|
|
|
More than one routine can fall through to a routine.
|
|
|
|
|
|
|
|
If main does a `goto foo`, then it can fall through to `foo`.
|
|
|
|
|
|
|
|
| define foo routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define bar routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| goto foo
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define main routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| goto foo
|
|
|
|
| }
|
|
|
|
= {
|
|
|
|
= "foo": [
|
|
|
|
= "bar",
|
|
|
|
= "main"
|
|
|
|
= ]
|
|
|
|
= }
|
|
|
|
|
|
|
|
There is nothing stopping two routines from tail-calling each
|
|
|
|
other, but we will only be able to make one of them, at most,
|
|
|
|
fall through to the other.
|
|
|
|
|
|
|
|
| define foo routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| goto bar
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define bar routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| goto foo
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define main routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| }
|
|
|
|
= {
|
|
|
|
= "bar": [
|
|
|
|
= "foo"
|
|
|
|
= ],
|
|
|
|
= "foo": [
|
|
|
|
= "bar"
|
|
|
|
= ]
|
|
|
|
= }
|
2018-04-04 12:37:32 +00:00
|
|
|
= *** cycles found:
|
|
|
|
= [
|
|
|
|
= "bar",
|
|
|
|
= "foo"
|
|
|
|
= ]
|
2018-04-04 13:01:17 +00:00
|
|
|
= *** after breaking cycle:
|
2018-04-04 12:37:32 +00:00
|
|
|
= {
|
|
|
|
= "bar": [
|
|
|
|
= "foo"
|
|
|
|
= ]
|
|
|
|
= }
|
2018-03-29 15:44:06 +00:00
|
|
|
|
|
|
|
If a routine does two tail calls (which is possible because they
|
|
|
|
can be in different branches of an `if`) it cannot fall through to another
|
|
|
|
routine.
|
|
|
|
|
|
|
|
| define foo routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define bar routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define main routine inputs z trashes a, z, n
|
|
|
|
| {
|
|
|
|
| if z {
|
|
|
|
| goto foo
|
|
|
|
| } else {
|
|
|
|
| goto bar
|
|
|
|
| }
|
|
|
|
| }
|
|
|
|
= {}
|
|
|
|
|
|
|
|
Similarly, a tail call to a vector can't be turned into a fallthru,
|
|
|
|
because we don't necessarily know what actual routine the vector contains.
|
|
|
|
|
|
|
|
| vector routine trashes a, z, n
|
|
|
|
| vec
|
|
|
|
|
|
|
|
|
| define foo routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define bar routine trashes a, z, n
|
|
|
|
| {
|
|
|
|
| ld a, 0
|
|
|
|
| }
|
|
|
|
|
|
|
|
|
| define main routine outputs vec trashes a, z, n
|
|
|
|
| {
|
|
|
|
| copy bar, vec
|
|
|
|
| goto vec
|
|
|
|
| }
|
|
|
|
= {}
|