mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-01-08 19:30:29 +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:
|
||||
|
||||
* routines shouldn't need to be listed as inputs.
|
||||
* A more involved demo for the C64 — one that sets up an interrupt.
|
||||
|
||||
For 0.7:
|
||||
|
||||
* always analyze before executing or compiling, unless told not to
|
||||
* `word` type.
|
||||
* `word table` type.
|
||||
* `trash` instruction.
|
||||
|
@ -29,19 +29,27 @@ class ForbiddenWriteError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
class InconsistentConstraintsError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
class TypeMismatchError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
class IncompatibleConstraintsError(StaticAnalysisError):
|
||||
class IllegalJumpError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
class IllegalJumpError(StaticAnalysisError):
|
||||
class ConstraintsError(StaticAnalysisError):
|
||||
pass
|
||||
|
||||
|
||||
class ConstantConstraintError(ConstraintsError):
|
||||
pass
|
||||
|
||||
|
||||
class InconsistentConstraintsError(ConstraintsError):
|
||||
pass
|
||||
|
||||
|
||||
class IncompatibleConstraintsError(ConstraintsError):
|
||||
pass
|
||||
|
||||
|
||||
@ -57,31 +65,39 @@ class Context(object):
|
||||
A location is writeable if it was listed in the outputs and trashes
|
||||
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._touched = set()
|
||||
self._meaningful = set()
|
||||
self._writeable = set()
|
||||
|
||||
for ref in inputs:
|
||||
if ref.is_constant():
|
||||
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
|
||||
self._meaningful.add(ref)
|
||||
output_names = set()
|
||||
for ref in outputs:
|
||||
if ref.is_constant():
|
||||
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
|
||||
output_names.add(ref.name)
|
||||
self._writeable.add(ref)
|
||||
for ref in trashes:
|
||||
if ref.is_constant():
|
||||
raise ConstantConstraintError('%s in %s' % (ref.name, routine.name))
|
||||
if ref.name in output_names:
|
||||
raise InconsistentConstraintsError(ref.name)
|
||||
raise InconsistentConstraintsError('%s in %s' % (ref.name, routine.name))
|
||||
self._writeable.add(ref)
|
||||
|
||||
def clone(self):
|
||||
c = Context(self.routine, [], [], [])
|
||||
c = Context(self.routines, self.routine, [], [], [])
|
||||
c._touched = set(self._touched)
|
||||
c._meaningful = set(self._meaningful)
|
||||
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)
|
||||
@ -98,7 +114,7 @@ class Context(object):
|
||||
def assert_meaningful(self, *refs, **kwargs):
|
||||
exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
|
||||
for ref in refs:
|
||||
if isinstance(ref, ConstantRef):
|
||||
if isinstance(ref, ConstantRef) or ref in self.routines:
|
||||
pass
|
||||
elif isinstance(ref, LocationRef):
|
||||
if ref not in self._meaningful:
|
||||
@ -141,14 +157,15 @@ class Analyzer(object):
|
||||
def __init__(self):
|
||||
self.current_routine = None
|
||||
self.has_encountered_goto = False
|
||||
self.routines = {}
|
||||
|
||||
def analyze_program(self, 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:
|
||||
self.analyze_routine(routine, routines)
|
||||
self.analyze_routine(routine)
|
||||
|
||||
def analyze_routine(self, routine, routines):
|
||||
def analyze_routine(self, routine):
|
||||
assert isinstance(routine, Routine)
|
||||
self.current_routine = routine
|
||||
self.has_encountered_goto = False
|
||||
@ -156,8 +173,8 @@ class Analyzer(object):
|
||||
# it's an extern, that's fine
|
||||
return
|
||||
type = routine.location.type
|
||||
context = Context(routine, type.inputs, type.outputs, type.trashes)
|
||||
self.analyze_block(routine.block, context, routines)
|
||||
context = Context(self.routines, routine, type.inputs, type.outputs, type.trashes)
|
||||
self.analyze_block(routine.block, context)
|
||||
if not self.has_encountered_goto:
|
||||
for ref in type.outputs:
|
||||
context.assert_meaningful(ref, exception_class=UnmeaningfulOutputError)
|
||||
@ -167,14 +184,14 @@ class Analyzer(object):
|
||||
raise ForbiddenWriteError(message)
|
||||
self.current_routine = None
|
||||
|
||||
def analyze_block(self, block, context, routines):
|
||||
def analyze_block(self, block, context):
|
||||
assert isinstance(block, Block)
|
||||
for i in block.instrs:
|
||||
if self.has_encountered_goto:
|
||||
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)
|
||||
opcode = instr.opcode
|
||||
dest = instr.dest
|
||||
@ -228,9 +245,9 @@ class Analyzer(object):
|
||||
elif opcode == 'if':
|
||||
context1 = context.clone()
|
||||
context2 = context.clone()
|
||||
self.analyze_block(instr.block1, context1, routines)
|
||||
self.analyze_block(instr.block1, context1)
|
||||
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?
|
||||
# probably not; if it wasn't meaningful in the first place, it
|
||||
# doesn't really matter if you modified it or not, coming out.
|
||||
@ -242,11 +259,11 @@ class Analyzer(object):
|
||||
elif opcode == 'repeat':
|
||||
# it will always be executed at least once, so analyze it having
|
||||
# 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
|
||||
# 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?
|
||||
elif opcode == 'copy':
|
||||
@ -275,7 +292,7 @@ class Analyzer(object):
|
||||
context.set_touched(REG_A, FLAG_Z, FLAG_N)
|
||||
context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
|
||||
elif opcode == 'with-sei':
|
||||
self.analyze_block(instr.block, context, routines)
|
||||
self.analyze_block(instr.block, context)
|
||||
elif opcode == 'goto':
|
||||
location = instr.location
|
||||
type = location.type
|
||||
|
@ -57,7 +57,11 @@ class VectorType(ExecutableType):
|
||||
|
||||
|
||||
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):
|
||||
@ -80,6 +84,9 @@ class LocationRef(Ref):
|
||||
def __repr__(self):
|
||||
return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.name)
|
||||
|
||||
def is_constant(self):
|
||||
return isinstance(self.type, RoutineType)
|
||||
|
||||
|
||||
class ConstantRef(Ref):
|
||||
def __init__(self, type, value):
|
||||
@ -97,6 +104,9 @@ class ConstantRef(Ref):
|
||||
def __repr__(self):
|
||||
return '%s(%r, %r)' % (self.__class__.__name__, self.type, self.value)
|
||||
|
||||
def is_constant(self):
|
||||
return True
|
||||
|
||||
|
||||
REG_A = LocationRef(TYPE_BYTE, 'a')
|
||||
REG_X = LocationRef(TYPE_BYTE, 'x')
|
||||
|
@ -1048,7 +1048,8 @@ Unless of course you subsequently initialize them.
|
||||
| }
|
||||
= 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
|
||||
| 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
|
||||
| }
|
||||
? 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
|
||||
|
||||
But not if the vector is declared inappropriately.
|
||||
@ -1088,7 +1155,6 @@ But not if the vector is declared inappropriately.
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| inputs foo
|
||||
| outputs vec
|
||||
| trashes a, z, n
|
||||
| {
|
||||
@ -1112,7 +1178,6 @@ Routines are read-only.
|
||||
| }
|
||||
|
|
||||
| routine main
|
||||
| inputs foo
|
||||
| outputs vec
|
||||
| trashes a, z, n
|
||||
| {
|
||||
@ -1128,7 +1193,7 @@ Indirect call.
|
||||
| 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
|
||||
| call foo
|
||||
| }
|
||||
@ -1142,7 +1207,7 @@ Calling the vector does indeed trash the things the vector says it does.
|
||||
| ld x, 200
|
||||
| }
|
||||
|
|
||||
| routine main inputs bar outputs x, foo trashes z, n {
|
||||
| routine main outputs x, foo trashes z, n {
|
||||
| ld x, 0
|
||||
| copy bar, foo
|
||||
| call foo
|
||||
@ -1242,7 +1307,7 @@ Indirect goto.
|
||||
| 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
|
||||
| goto foo
|
||||
| }
|
||||
@ -1260,14 +1325,13 @@ vector says it does.
|
||||
| }
|
||||
|
|
||||
| routine sub
|
||||
| inputs bar
|
||||
| trashes foo, a, x, z, n {
|
||||
| ld x, 0
|
||||
| copy bar, foo
|
||||
| goto foo
|
||||
| }
|
||||
|
|
||||
| routine main inputs bar
|
||||
| routine main
|
||||
| outputs a
|
||||
| trashes foo, x, z, n {
|
||||
| call sub
|
||||
@ -1286,7 +1350,6 @@ vector says it does.
|
||||
| }
|
||||
|
|
||||
| routine sub
|
||||
| inputs bar
|
||||
| outputs x
|
||||
| trashes foo, a, z, n {
|
||||
| ld x, 0
|
||||
@ -1294,7 +1357,7 @@ vector says it does.
|
||||
| goto foo
|
||||
| }
|
||||
|
|
||||
| routine main inputs bar
|
||||
| routine main
|
||||
| outputs a
|
||||
| trashes foo, x, z, n {
|
||||
| call sub
|
||||
|
Loading…
Reference in New Issue
Block a user