From bda0202dee41566034ad635d43aa540d5b95cd6c Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Tue, 6 Mar 2018 15:42:12 +0000 Subject: [PATCH] Confirm that AND clips the range and INC/DEC invalidate it. --- README.md | 8 ++++-- src/sixtypical/analyzer.py | 11 +++++++ tests/SixtyPical Analysis.md | 56 ++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 34ddc1d..f55f6ef 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ Documentation * [SixtyPical specification](doc/SixtyPical.md) * [SixtyPical revision history](HISTORY.md) * [Literate test suite for SixtyPical syntax](tests/SixtyPical%20Syntax.md) -* [Literate test suite for SixtyPical execution](tests/SixtyPical%20Execution.md) * [Literate test suite for SixtyPical analysis](tests/SixtyPical%20Analysis.md) * [Literate test suite for SixtyPical compilation](tests/SixtyPical%20Compilation.md) * [6502 Opcodes used/not used in SixtyPical](doc/6502%20Opcodes.md) @@ -78,11 +77,14 @@ are trashed inside the block. ### 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 -is probably NP-complete. But doing it adeuqately is probably not that hard. +is probably NP-complete. But doing it adequately is probably not that hard. + +### Different preludes for different architectures + +`--prelude=c64-basic` ### And at some point... -* Confirm that `and` can be used to restrict the range of table reads/writes. * `low` and `high` address operators - to turn `word` type into `byte`. * `const`s that can be used in defining the size of tables, etc. * Tests, and implementation, ensuring a routine can be assigned to a vector of "wider" type diff --git a/src/sixtypical/analyzer.py b/src/sixtypical/analyzer.py index d3b977b..88088aa 100644 --- a/src/sixtypical/analyzer.py +++ b/src/sixtypical/analyzer.py @@ -194,6 +194,7 @@ class Context(object): def set_touched(self, *refs): for ref in refs: self._touched.add(ref) + # TODO: it might be possible to invalidate the range here def set_meaningful(self, *refs): for ref in refs: @@ -220,6 +221,10 @@ class Context(object): src_range = src.max_range() self._range[dest] = src_range + def invalidate_range(self, ref): + self.assert_meaningful(ref) + self._range[ref] = ref.max_range() + def set_unmeaningful(self, *refs): for ref in refs: if ref in self._range: @@ -377,6 +382,7 @@ class Analyzer(object): raise TypeMismatchError(instr, '{} and {}'.format(src, name)) else: context.set_written(dest) + # FIXME: context.copy_range(src, dest) ? context.assert_meaningful(src) elif opcode == 'add': context.assert_meaningful(src, dest, FLAG_C) @@ -395,6 +401,7 @@ class Analyzer(object): context.set_unmeaningful(REG_A) else: self.assert_type(TYPE_WORD, dest) + context.invalidate_range(dest) elif opcode == 'sub': context.assert_meaningful(src, dest, FLAG_C) if src.type == TYPE_BYTE: @@ -405,10 +412,12 @@ class Analyzer(object): context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C, FLAG_V) context.set_touched(REG_A) context.set_unmeaningful(REG_A) + context.invalidate_range(dest) elif opcode in ('inc', 'dec'): self.assert_type(TYPE_BYTE, dest) context.assert_meaningful(dest) context.set_written(dest, FLAG_Z, FLAG_N) + context.invalidate_range(dest) elif opcode == 'cmp': self.assert_type(TYPE_BYTE, src, dest) context.assert_meaningful(src, dest) @@ -425,10 +434,12 @@ class Analyzer(object): self.assert_type(TYPE_BYTE, src, dest) context.assert_meaningful(src, dest) context.set_written(dest, FLAG_Z, FLAG_N) + context.invalidate_range(dest) elif opcode in ('shl', 'shr'): self.assert_type(TYPE_BYTE, dest) context.assert_meaningful(dest, FLAG_C) context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C) + context.invalidate_range(dest) elif opcode == 'call': type = instr.location.type if isinstance(type, VectorType): diff --git a/tests/SixtyPical Analysis.md b/tests/SixtyPical Analysis.md index 42d2173..47a9124 100644 --- a/tests/SixtyPical Analysis.md +++ b/tests/SixtyPical Analysis.md @@ -623,6 +623,62 @@ This applies to `copy` as well. | } ? RangeExceededError +`AND`'ing a register with a value ensures the range of the +register will not exceed the range of the value. This can +be used to "clip" the range of an index so that it fits in +a table. + + | word one: 77 + | word table[32] many + | + | routine main + | inputs a, many, one + | outputs many, one + | trashes a, x, n, z + | { + | and a, 31 + | ld x, a + | copy one, many + x + | copy many + x, one + | } + = ok + +Test for "clipping", but not enough. + + | word one: 77 + | word table[32] many + | + | routine main + | inputs a, many, one + | outputs many, one + | trashes a, x, n, z + | { + | and a, 63 + | ld x, a + | copy one, many + x + | copy many + x, one + | } + ? RangeExceededError + +If you alter the value after "clipping" it, the range can +no longer be guaranteed. + + | word one: 77 + | word table[32] many + | + | routine main + | inputs a, many, one + | outputs many, one + | trashes a, x, n, z + | { + | and a, 31 + | ld x, a + | inc x + | copy one, many + x + | copy many + x, one + | } + ? RangeExceededError + ### add ### Can't `add` from or to a memory location that isn't initialized.