1
0
mirror of https://github.com/catseye/SixtyPical.git synced 2024-06-06 15:29:30 +00:00

Implement the "union rule for trashes" when analyzing if blocks.

This commit is contained in:
Chris Pressey 2018-02-08 14:04:51 +00:00
parent 3f1f3bf16e
commit b20b664748
4 changed files with 23 additions and 15 deletions

View File

@ -6,6 +6,7 @@ History of SixtyPical
* `copy` is now understood to trash `a`, thus `copy ..., a` is not valid.
Indirect addressing is supported in `ld`, as in `ld a, [ptr] + y`, to compensate.
* Implements the "union rule for trashes" when analyzing `if` blocks.
* Fixed bug where `trash` was not marking the location as being virtually altered.
0.11

View File

@ -67,11 +67,6 @@ that call this routine.
These might be forced to specify an initial value so that they can always be
assumed to be meaningful.
### Union rule for trashes in `if`
If one branch trashes {`a`} and the other branch trashes {`b`} then the whole
`if` statement trashes {`a`, `b`}.
### Re-order routines and optimize tail-calls to fallthroughs
Not because it saves 3 bytes, but because it's a neat trick. Doing it optimally

View File

@ -101,13 +101,6 @@ class Context(object):
c._writeable = set(self._writeable)
return c
def set_from(self, c):
assert c.routines == self.routines
assert c.routine == self.routine
self._touched = set(c._touched)
self._meaningful = set(c._meaningful)
self._writeable = set(c._writeable)
def each_meaningful(self):
for ref in self._meaningful:
yield ref
@ -330,25 +323,44 @@ class Analyzer(object):
context.set_touched(ref)
context.set_unmeaningful(ref)
elif opcode == 'if':
incoming_meaningful = set(context.each_meaningful())
context1 = context.clone()
context2 = context.clone()
self.analyze_block(instr.block1, context1)
if instr.block2 is not None:
self.analyze_block(instr.block2, context2)
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(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(src)
)
context.set_from(context1)
# merge the contexts. this used to be a method called `set_from`
context._touched = set(context1._touched) | set(context2._touched)
context._meaningful = outgoing_meaningful
context._writeable = set(context1._writeable) | set(context2._writeable)
for ref in outgoing_trashes:
context.set_touched(ref)
context.set_unmeaningful(ref)
elif opcode == 'repeat':
# it will always be executed at least once, so analyze it having
# been executed the first time.

View File

@ -1417,7 +1417,7 @@ trashes {`a`, `b`}.
| trash x
| }
| }
? UnmeaningfulOutputError: x in foo
? ForbiddenWriteError: x in foo
| routine foo
| inputs a, x, z
@ -1429,7 +1429,7 @@ trashes {`a`, `b`}.
| trash x
| }
| }
? UnmeaningfulOutputError: a in foo
? ForbiddenWriteError: a in foo
### repeat ###