1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-05-31 21:41:29 +00:00

Drop the check for "consistent initialization" inside if blocks.

This commit is contained in:
Chris Pressey 2018-12-11 19:27:40 +00:00
parent d86612acce
commit d13c6a94a2
5 changed files with 46 additions and 51 deletions

View File

@ -4,6 +4,9 @@ History of SixtyPical
0.18
----
* The "consistent initialization" check inside `if` blocks has
been dropped. If a location is initialized inside one block
but not the other, it is treated as uninitialized afterwards.
* Syntactically, `goto` may only appear at the end of a block.
It need no longer be the final instruction in a routine,
as long as the type context is consistent at every exit.

View File

@ -44,15 +44,6 @@ buffer; and the ones you establish must be disjoint.
An alternative would be `static` pointers, which are currently not possible because
pointers must be zero-page, thus `@`, thus uninitialized.
### Question "consistent initialization"
Question the value of the "consistent initialization" principle for `if` statement analysis.
Part of this is the trashes at the end; I think what it should be is that the trashes
after the `if` is the union of the trashes in each of the branches; this would obviate the
need to `trash` values explicitly, but if you tried to access them afterwards, it would still
error.
### Tail-call optimization
If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can,

View File

@ -311,14 +311,6 @@ define player_logic logic_routine
ld a, 1
st a, player_died
}
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
// but currently the compiler cares a little too much about values that are
// initialized in one branch of an `if`, but not the other, but are trashed
// at the end of the routine anyway.
trash ptr
trash y
trash v
}
}
@ -355,13 +347,6 @@ define enemy_logic logic_routine
add ptr, pos
copy 82, [ptr] + y
}
// FIXME these trashes, strictly speaking, probably shouldn't be needed,
// but currently the compiler cares too much about values that are
// initialized in one branch of an `if`, but not the other, but trashed
// at the end of the routine anyway.
trash ptr
trash y
} else {
copy delta, compare_target
st on, c

View File

@ -414,11 +414,11 @@ class Analyzer(object):
if self.debug:
print("at end of routine `{}`:".format(routine.name))
print(context)
print("trashed: ", LocationRef.format_set(trashed))
#print("trashed: ", LocationRef.format_set(trashed))
print("outputs: ", LocationRef.format_set(type_.outputs))
trashed_outputs = type_.outputs & trashed
if trashed_outputs:
print("TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs))
#trashed_outputs = type_.outputs & trashed
#if trashed_outputs:
# print("TRASHED OUTPUTS: ", LocationRef.format_set(trashed_outputs))
print('')
print('-' * 79)
print('')
@ -801,24 +801,6 @@ class Analyzer(object):
outgoing_meaningful = set(context1.each_meaningful()) & set(context2.each_meaningful())
outgoing_trashes = incoming_meaningful - outgoing_meaningful
# TODO may we need to deal with touched separately here too?
# probably not; if it wasn't meaningful in the first place, it
# doesn't really matter if you modified it or not, coming out.
for ref in context1.each_meaningful():
if ref in outgoing_trashes:
continue
context2.assert_meaningful(
ref, exception_class=InconsistentInitializationError,
message='initialized in block 1 but not in block 2 of `if {}`'.format(instr.src)
)
for ref in context2.each_meaningful():
if ref in outgoing_trashes:
continue
context1.assert_meaningful(
ref, exception_class=InconsistentInitializationError,
message='initialized in block 2 but not in block 1 of `if {}`'.format(instr.src)
)
# merge the contexts.
# first, the easy case: if one of the contexts has terminated, just use the other one.

View File

@ -1607,7 +1607,9 @@ Both blocks of an `if` are analyzed.
| }
= ok
If a location is initialized in one block, is must be initialized in the other as well.
If a location is initialized in one block, is must be initialized in the other as well
in order to be considered to be initialized after the block. If it is not consistent,
it will be considered uninitialized.
| define foo routine
| inputs a
@ -1621,7 +1623,7 @@ If a location is initialized in one block, is must be initialized in the other a
| ld a, 23
| }
| }
? InconsistentInitializationError: x
? UnmeaningfulOutputError: x
| define foo routine
| inputs a
@ -1635,7 +1637,7 @@ If a location is initialized in one block, is must be initialized in the other a
| ld x, 7
| }
| }
? InconsistentInitializationError: x
? UnmeaningfulOutputError: x
| define foo routine
| inputs a
@ -1649,7 +1651,39 @@ If a location is initialized in one block, is must be initialized in the other a
| ld x, 7
| }
| }
? InconsistentInitializationError: x
? UnmeaningfulOutputError: x
If we don't care if it's uninitialized after the `if`, that's okay then.
| define foo routine
| inputs a
| trashes a, x, z, n, c
| {
| cmp a, 42
| if not z {
| ld a, 6
| } else {
| ld x, 7
| }
| }
= ok
Or, if it does get initialized on both branches, that's okay then.
| define foo routine
| inputs a
| outputs x
| trashes a, z, n, c
| {
| cmp a, 42
| if not z {
| ld x, 0
| ld a, 6
| } else {
| ld x, 7
| }
| }
= ok
However, this only pertains to initialization. If a value is already
initialized, either because it was set previous to the `if`, or is an
@ -1698,7 +1732,7 @@ An `if` with a single block is analyzed as if it had an empty `else` block.
| ld x, 7
| }
| }
? InconsistentInitializationError: x
? UnmeaningfulOutputError: x
| define foo routine
| inputs a