mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-01-24 17:31:46 +00:00
Constants (such as routines) cannot be given in call-constraints.
This commit is contained in:
parent
421727e107
commit
b5763e84b4
@ -44,11 +44,11 @@ TODO
|
|||||||
|
|
||||||
For 0.6:
|
For 0.6:
|
||||||
|
|
||||||
* routines shouldn't need to be listed as inputs.
|
|
||||||
* A more involved demo for the C64 — one that sets up an interrupt.
|
* A more involved demo for the C64 — one that sets up an interrupt.
|
||||||
|
|
||||||
For 0.7:
|
For 0.7:
|
||||||
|
|
||||||
|
* always analyze before executing or compiling, unless told not to
|
||||||
* `word` type.
|
* `word` type.
|
||||||
* `word table` type.
|
* `word table` type.
|
||||||
* `trash` instruction.
|
* `trash` instruction.
|
||||||
|
@ -29,19 +29,27 @@ class ForbiddenWriteError(StaticAnalysisError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class InconsistentConstraintsError(StaticAnalysisError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class TypeMismatchError(StaticAnalysisError):
|
class TypeMismatchError(StaticAnalysisError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IncompatibleConstraintsError(StaticAnalysisError):
|
class IllegalJumpError(StaticAnalysisError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class IllegalJumpError(StaticAnalysisError):
|
class ConstraintsError(StaticAnalysisError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ConstantConstraintError(ConstraintsError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InconsistentConstraintsError(ConstraintsError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class IncompatibleConstraintsError(ConstraintsError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -57,31 +65,39 @@ class Context(object):
|
|||||||
A location is writeable if it was listed in the outputs and trashes
|
A location is writeable if it was listed in the outputs and trashes
|
||||||
lists of this routine.
|
lists of this routine.
|
||||||
"""
|
"""
|
||||||
def __init__(self, routine, inputs, outputs, trashes):
|
def __init__(self, routines, routine, inputs, outputs, trashes):
|
||||||
|
self.routines = routines # Location -> AST node
|
||||||
self.routine = routine
|
self.routine = routine
|
||||||
self._touched = set()
|
self._touched = set()
|
||||||
self._meaningful = set()
|
self._meaningful = set()
|
||||||
self._writeable = set()
|
self._writeable = set()
|
||||||
|
|
||||||
for ref in inputs:
|
for ref in inputs:
|
||||||
|
if ref.is_constant():
|
||||||
|
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
|
||||||
self._meaningful.add(ref)
|
self._meaningful.add(ref)
|
||||||
output_names = set()
|
output_names = set()
|
||||||
for ref in outputs:
|
for ref in outputs:
|
||||||
|
if ref.is_constant():
|
||||||
|
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
|
||||||
output_names.add(ref.name)
|
output_names.add(ref.name)
|
||||||
self._writeable.add(ref)
|
self._writeable.add(ref)
|
||||||
for ref in trashes:
|
for ref in trashes:
|
||||||
|
if ref.is_constant():
|
||||||
|
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
|
||||||
if ref.name in output_names:
|
if ref.name in output_names:
|
||||||
raise InconsistentConstraintsError(ref.name)
|
raise InconsistentConstraintsError('%s in %s' % (ref.name, routine.name))
|
||||||
self._writeable.add(ref)
|
self._writeable.add(ref)
|
||||||
|
|
||||||
def clone(self):
|
def clone(self):
|
||||||
c = Context(self.routine, [], [], [])
|
c = Context(self.routines, self.routine, [], [], [])
|
||||||
c._touched = set(self._touched)
|
c._touched = set(self._touched)
|
||||||
c._meaningful = set(self._meaningful)
|
c._meaningful = set(self._meaningful)
|
||||||
c._writeable = set(self._writeable)
|
c._writeable = set(self._writeable)
|
||||||
return c
|
return c
|
||||||
|
|
||||||
def set_from(self, c):
|
def set_from(self, c):
|
||||||
|
assert c.routines == self.routines
|
||||||
assert c.routine == self.routine
|
assert c.routine == self.routine
|
||||||
self._touched = set(c._touched)
|
self._touched = set(c._touched)
|
||||||
self._meaningful = set(c._meaningful)
|
self._meaningful = set(c._meaningful)
|
||||||
@ -98,7 +114,7 @@ class Context(object):
|
|||||||
def assert_meaningful(self, *refs, **kwargs):
|
def assert_meaningful(self, *refs, **kwargs):
|
||||||
exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
|
exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
|
||||||
for ref in refs:
|
for ref in refs:
|
||||||
if isinstance(ref, ConstantRef):
|
if isinstance(ref, ConstantRef) or ref in self.routines:
|
||||||
pass
|
pass
|
||||||
elif isinstance(ref, LocationRef):
|
elif isinstance(ref, LocationRef):
|
||||||
if ref not in self._meaningful:
|
if ref not in self._meaningful:
|
||||||
@ -141,14 +157,15 @@ class Analyzer(object):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.current_routine = None
|
self.current_routine = None
|
||||||
self.has_encountered_goto = False
|
self.has_encountered_goto = False
|
||||||
|
self.routines = {}
|
||||||
|
|
||||||
def analyze_program(self, program):
|
def analyze_program(self, program):
|
||||||
assert isinstance(program, Program)
|
assert isinstance(program, Program)
|
||||||
routines = {r.name: r for r in program.routines}
|
self.routines = {r.location: r for r in program.routines}
|
||||||
for routine in program.routines:
|
for routine in program.routines:
|
||||||
self.analyze_routine(routine, routines)
|
self.analyze_routine(routine)
|
||||||
|
|
||||||
def analyze_routine(self, routine, routines):
|
def analyze_routine(self, routine):
|
||||||
assert isinstance(routine, Routine)
|
assert isinstance(routine, Routine)
|
||||||
self.current_routine = routine
|
self.current_routine = routine
|
||||||
self.has_encountered_goto = False
|
self.has_encountered_goto = False
|
||||||
@ -156,8 +173,8 @@ class Analyzer(object):
|
|||||||
# it's an extern, that's fine
|
# it's an extern, that's fine
|
||||||
return
|
return
|
||||||
type = routine.location.type
|
type = routine.location.type
|
||||||
context = Context(routine, type.inputs, type.outputs, type.trashes)
|
context = Context(self.routines, routine, type.inputs, type.outputs, type.trashes)
|
||||||
self.analyze_block(routine.block, context, routines)
|
self.analyze_block(routine.block, context)
|
||||||
if not self.has_encountered_goto:
|
if not self.has_encountered_goto:
|
||||||
for ref in type.outputs:
|
for ref in type.outputs:
|
||||||
context.assert_meaningful(ref, exception_class=UnmeaningfulOutputError)
|
context.assert_meaningful(ref, exception_class=UnmeaningfulOutputError)
|
||||||
@ -167,14 +184,14 @@ class Analyzer(object):
|
|||||||
raise ForbiddenWriteError(message)
|
raise ForbiddenWriteError(message)
|
||||||
self.current_routine = None
|
self.current_routine = None
|
||||||
|
|
||||||
def analyze_block(self, block, context, routines):
|
def analyze_block(self, block, context):
|
||||||
assert isinstance(block, Block)
|
assert isinstance(block, Block)
|
||||||
for i in block.instrs:
|
for i in block.instrs:
|
||||||
if self.has_encountered_goto:
|
if self.has_encountered_goto:
|
||||||
raise IllegalJumpError(i)
|
raise IllegalJumpError(i)
|
||||||
self.analyze_instr(i, context, routines)
|
self.analyze_instr(i, context)
|
||||||
|
|
||||||
def analyze_instr(self, instr, context, routines):
|
def analyze_instr(self, instr, context):
|
||||||
assert isinstance(instr, Instr)
|
assert isinstance(instr, Instr)
|
||||||
opcode = instr.opcode
|
opcode = instr.opcode
|
||||||
dest = instr.dest
|
dest = instr.dest
|
||||||
@ -228,9 +245,9 @@ class Analyzer(object):
|
|||||||
elif opcode == 'if':
|
elif opcode == 'if':
|
||||||
context1 = context.clone()
|
context1 = context.clone()
|
||||||
context2 = context.clone()
|
context2 = context.clone()
|
||||||
self.analyze_block(instr.block1, context1, routines)
|
self.analyze_block(instr.block1, context1)
|
||||||
if instr.block2 is not None:
|
if instr.block2 is not None:
|
||||||
self.analyze_block(instr.block2, context2, routines)
|
self.analyze_block(instr.block2, context2)
|
||||||
# TODO may we need to deal with touched separately here too?
|
# TODO may we need to deal with touched separately here too?
|
||||||
# probably not; if it wasn't meaningful in the first place, it
|
# probably not; if it wasn't meaningful in the first place, it
|
||||||
# doesn't really matter if you modified it or not, coming out.
|
# doesn't really matter if you modified it or not, coming out.
|
||||||
@ -242,11 +259,11 @@ class Analyzer(object):
|
|||||||
elif opcode == 'repeat':
|
elif opcode == 'repeat':
|
||||||
# it will always be executed at least once, so analyze it having
|
# it will always be executed at least once, so analyze it having
|
||||||
# been executed the first time.
|
# been executed the first time.
|
||||||
self.analyze_block(instr.block, context, routines)
|
self.analyze_block(instr.block, context)
|
||||||
|
|
||||||
# now analyze it having been executed a second time, with the context
|
# now analyze it having been executed a second time, with the context
|
||||||
# of it having already been executed.
|
# of it having already been executed.
|
||||||
self.analyze_block(instr.block, context, routines)
|
self.analyze_block(instr.block, context)
|
||||||
|
|
||||||
# NB I *think* that's enough... but it might not be?
|
# NB I *think* that's enough... but it might not be?
|
||||||
elif opcode == 'copy':
|
elif opcode == 'copy':
|
||||||
@ -275,7 +292,7 @@ class Analyzer(object):
|
|||||||
context.set_touched(REG_A, FLAG_Z, FLAG_N)
|
context.set_touched(REG_A, FLAG_Z, FLAG_N)
|
||||||
context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
|
context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
|
||||||
elif opcode == 'with-sei':
|
elif opcode == 'with-sei':
|
||||||
self.analyze_block(instr.block, context, routines)
|
self.analyze_block(instr.block, context)
|
||||||
elif opcode == 'goto':
|
elif opcode == 'goto':
|
||||||
location = instr.location
|
location = instr.location
|
||||||
type = location.type
|
type = location.type
|
||||||
|
@ -57,7 +57,11 @@ class VectorType(ExecutableType):
|
|||||||
|
|
||||||
|
|
||||||
class Ref(object):
|
class Ref(object):
|
||||||
pass
|
def is_constant(self):
|
||||||
|
"""read-only means that the program cannot change the value
|
||||||
|
of a location. constant means that the value of the location
|
||||||
|
will not change during the lifetime of the program."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class LocationRef(Ref):
|
class LocationRef(Ref):
|
||||||
@ -80,6 +84,9 @@ class LocationRef(Ref):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.name)
|
return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.name)
|
||||||
|
|
||||||
|
def is_constant(self):
|
||||||
|
return isinstance(self.type, RoutineType)
|
||||||
|
|
||||||
|
|
||||||
class ConstantRef(Ref):
|
class ConstantRef(Ref):
|
||||||
def __init__(self, type, value):
|
def __init__(self, type, value):
|
||||||
@ -97,6 +104,9 @@ class ConstantRef(Ref):
|
|||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.value)
|
return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.value)
|
||||||
|
|
||||||
|
def is_constant(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
REG_A = LocationRef(TYPE_BYTE, 'a')
|
REG_A = LocationRef(TYPE_BYTE, 'a')
|
||||||
REG_X = LocationRef(TYPE_BYTE, 'x')
|
REG_X = LocationRef(TYPE_BYTE, 'x')
|
||||||
|
@ -1048,7 +1048,8 @@ Unless of course you subsequently initialize them.
|
|||||||
| }
|
| }
|
||||||
= ok
|
= ok
|
||||||
|
|
||||||
You can copy the address of a routine into a vector, if that vector is declared appropriately.
|
Routines are constants. You need not, and in fact cannot, specify a constant
|
||||||
|
as an input to, an output of, or as a trashed value of a routine.
|
||||||
|
|
||||||
| vector vec
|
| vector vec
|
||||||
| inputs x
|
| inputs x
|
||||||
@ -1070,6 +1071,72 @@ You can copy the address of a routine into a vector, if that vector is declared
|
|||||||
| {
|
| {
|
||||||
| copy foo, vec
|
| copy foo, vec
|
||||||
| }
|
| }
|
||||||
|
? ConstantConstraintError: foo in main
|
||||||
|
|
||||||
|
| vector vec
|
||||||
|
| inputs x
|
||||||
|
| outputs x
|
||||||
|
| trashes z, n
|
||||||
|
|
|
||||||
|
| routine foo
|
||||||
|
| inputs x
|
||||||
|
| outputs x
|
||||||
|
| trashes z, n
|
||||||
|
| {
|
||||||
|
| inc x
|
||||||
|
| }
|
||||||
|
|
|
||||||
|
| routine main
|
||||||
|
| outputs vec, foo
|
||||||
|
| trashes a, z, n
|
||||||
|
| {
|
||||||
|
| copy foo, vec
|
||||||
|
| }
|
||||||
|
? ConstantConstraintError: foo in main
|
||||||
|
|
||||||
|
| vector vec
|
||||||
|
| inputs x
|
||||||
|
| outputs x
|
||||||
|
| trashes z, n
|
||||||
|
|
|
||||||
|
| routine foo
|
||||||
|
| inputs x
|
||||||
|
| outputs x
|
||||||
|
| trashes z, n
|
||||||
|
| {
|
||||||
|
| inc x
|
||||||
|
| }
|
||||||
|
|
|
||||||
|
| routine main
|
||||||
|
| outputs vec
|
||||||
|
| trashes a, z, n, foo
|
||||||
|
| {
|
||||||
|
| copy foo, vec
|
||||||
|
| }
|
||||||
|
? ConstantConstraintError: foo in main
|
||||||
|
|
||||||
|
You can copy the address of a routine into a vector, if that vector is
|
||||||
|
declared appropriately.
|
||||||
|
|
||||||
|
| vector vec
|
||||||
|
| inputs x
|
||||||
|
| outputs x
|
||||||
|
| trashes z, n
|
||||||
|
|
|
||||||
|
| routine foo
|
||||||
|
| inputs x
|
||||||
|
| outputs x
|
||||||
|
| trashes z, n
|
||||||
|
| {
|
||||||
|
| inc x
|
||||||
|
| }
|
||||||
|
|
|
||||||
|
| routine main
|
||||||
|
| outputs vec
|
||||||
|
| trashes a, z, n
|
||||||
|
| {
|
||||||
|
| copy foo, vec
|
||||||
|
| }
|
||||||
= ok
|
= ok
|
||||||
|
|
||||||
But not if the vector is declared inappropriately.
|
But not if the vector is declared inappropriately.
|
||||||
@ -1088,7 +1155,6 @@ But not if the vector is declared inappropriately.
|
|||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine main
|
| routine main
|
||||||
| inputs foo
|
|
||||||
| outputs vec
|
| outputs vec
|
||||||
| trashes a, z, n
|
| trashes a, z, n
|
||||||
| {
|
| {
|
||||||
@ -1112,7 +1178,6 @@ Routines are read-only.
|
|||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine main
|
| routine main
|
||||||
| inputs foo
|
|
||||||
| outputs vec
|
| outputs vec
|
||||||
| trashes a, z, n
|
| trashes a, z, n
|
||||||
| {
|
| {
|
||||||
@ -1128,7 +1193,7 @@ Indirect call.
|
|||||||
| ld x, 200
|
| ld x, 200
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine main inputs bar outputs x, foo trashes a, z, n {
|
| routine main outputs x, foo trashes a, z, n {
|
||||||
| copy bar, foo
|
| copy bar, foo
|
||||||
| call foo
|
| call foo
|
||||||
| }
|
| }
|
||||||
@ -1142,7 +1207,7 @@ Calling the vector does indeed trash the things the vector says it does.
|
|||||||
| ld x, 200
|
| ld x, 200
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine main inputs bar outputs x, foo trashes z, n {
|
| routine main outputs x, foo trashes z, n {
|
||||||
| ld x, 0
|
| ld x, 0
|
||||||
| copy bar, foo
|
| copy bar, foo
|
||||||
| call foo
|
| call foo
|
||||||
@ -1242,7 +1307,7 @@ Indirect goto.
|
|||||||
| ld x, 200
|
| ld x, 200
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine main inputs bar outputs x trashes foo, a, z, n {
|
| routine main outputs x trashes foo, a, z, n {
|
||||||
| copy bar, foo
|
| copy bar, foo
|
||||||
| goto foo
|
| goto foo
|
||||||
| }
|
| }
|
||||||
@ -1260,14 +1325,13 @@ vector says it does.
|
|||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine sub
|
| routine sub
|
||||||
| inputs bar
|
|
||||||
| trashes foo, a, x, z, n {
|
| trashes foo, a, x, z, n {
|
||||||
| ld x, 0
|
| ld x, 0
|
||||||
| copy bar, foo
|
| copy bar, foo
|
||||||
| goto foo
|
| goto foo
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine main inputs bar
|
| routine main
|
||||||
| outputs a
|
| outputs a
|
||||||
| trashes foo, x, z, n {
|
| trashes foo, x, z, n {
|
||||||
| call sub
|
| call sub
|
||||||
@ -1286,7 +1350,6 @@ vector says it does.
|
|||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine sub
|
| routine sub
|
||||||
| inputs bar
|
|
||||||
| outputs x
|
| outputs x
|
||||||
| trashes foo, a, z, n {
|
| trashes foo, a, z, n {
|
||||||
| ld x, 0
|
| ld x, 0
|
||||||
@ -1294,7 +1357,7 @@ vector says it does.
|
|||||||
| goto foo
|
| goto foo
|
||||||
| }
|
| }
|
||||||
|
|
|
|
||||||
| routine main inputs bar
|
| routine main
|
||||||
| outputs a
|
| outputs a
|
||||||
| trashes foo, x, z, n {
|
| trashes foo, x, z, n {
|
||||||
| call sub
|
| call sub
|
||||||
|
Loading…
x
Reference in New Issue
Block a user