mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-01-08 19:30:29 +00:00
Implement the "union rule for trashes" when analyzing if
blocks.
This commit is contained in:
parent
3f1f3bf16e
commit
b20b664748
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 ###
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user