0 | 0 |
# encoding: UTF-8
|
1 | 1 |
|
2 | 2 |
from sixtypical.ast import (
|
3 | |
Program, Routine, Block, SingleOp, If, Repeat, For, WithInterruptsOff, Save, PointInto
|
|
3 |
Program, Routine, Block, SingleOp, Call, GoTo, If, Repeat, For, WithInterruptsOff, Save, PointInto
|
4 | 4 |
)
|
5 | 5 |
from sixtypical.model import (
|
6 | 6 |
TYPE_BYTE, TYPE_WORD,
|
|
492 | 492 |
def analyze_instr(self, instr, context):
|
493 | 493 |
if isinstance(instr, SingleOp):
|
494 | 494 |
self.analyze_single_op(instr, context)
|
|
495 |
elif isinstance(instr, Call):
|
|
496 |
self.analyze_call(instr, context)
|
|
497 |
elif isinstance(instr, GoTo):
|
|
498 |
self.analyze_goto(instr, context)
|
495 | 499 |
elif isinstance(instr, If):
|
496 | 500 |
self.analyze_if(instr, context)
|
497 | 501 |
elif isinstance(instr, Repeat):
|
|
666 | 670 |
self.assert_type(TYPE_BYTE, dest)
|
667 | 671 |
context.set_written(dest, FLAG_Z, FLAG_N, FLAG_C)
|
668 | 672 |
context.invalidate_range(dest)
|
669 | |
elif opcode == 'call':
|
670 | |
type = instr.location.type
|
671 | |
if not isinstance(type, (RoutineType, VectorType)):
|
672 | |
raise TypeMismatchError(instr, instr.location)
|
673 | |
if isinstance(type, VectorType):
|
674 | |
type = type.of_type
|
675 | |
for ref in type.inputs:
|
676 | |
context.assert_meaningful(ref)
|
677 | |
for ref in type.outputs:
|
678 | |
context.set_written(ref)
|
679 | |
for ref in type.trashes:
|
680 | |
context.assert_writeable(ref)
|
681 | |
context.set_touched(ref)
|
682 | |
context.set_unmeaningful(ref)
|
683 | 673 |
elif opcode == 'copy':
|
684 | 674 |
if dest == REG_A:
|
685 | 675 |
raise ForbiddenWriteError(instr, "{} cannot be used as destination for copy".format(dest))
|
|
788 | 778 |
|
789 | 779 |
context.set_touched(REG_A, FLAG_Z, FLAG_N)
|
790 | 780 |
context.set_unmeaningful(REG_A, FLAG_Z, FLAG_N)
|
791 | |
elif opcode == 'goto':
|
792 | |
location = instr.location
|
793 | |
type_ = location.type
|
794 | |
|
795 | |
if not isinstance(type_, (RoutineType, VectorType)):
|
796 | |
raise TypeMismatchError(instr, location)
|
797 | |
|
798 | |
# assert that the dest routine's inputs are all initialized
|
799 | |
if isinstance(type_, VectorType):
|
800 | |
type_ = type_.of_type
|
801 | |
for ref in type_.inputs:
|
802 | |
context.assert_meaningful(ref)
|
803 | |
|
804 | |
# and that this routine's trashes and output constraints are a
|
805 | |
# superset of the called routine's
|
806 | |
current_type = self.current_routine.location.type
|
807 | |
self.assert_affected_within('outputs', type_, current_type)
|
808 | |
self.assert_affected_within('trashes', type_, current_type)
|
809 | |
|
810 | |
context.encounter_gotos(set([instr.location]))
|
811 | |
|
812 | |
# Now that we have encountered a goto, we update the
|
813 | |
# context here to match what someone calling the goto'ed
|
814 | |
# function directly, would expect. (which makes sense
|
815 | |
# when you think about it; if this goto's F, then calling
|
816 | |
# this is like calling F, from the perspective of what is
|
817 | |
# returned.)
|
818 | |
#
|
819 | |
# However, this isn't the current context anymore. This
|
820 | |
# is an exit context of this routine.
|
821 | |
|
822 | |
exit_context = context.clone()
|
823 | |
|
824 | |
for ref in type_.outputs:
|
825 | |
exit_context.set_touched(ref) # ?
|
826 | |
exit_context.set_written(ref)
|
827 | |
|
828 | |
for ref in type_.trashes:
|
829 | |
exit_context.assert_writeable(ref)
|
830 | |
exit_context.set_touched(ref)
|
831 | |
exit_context.set_unmeaningful(ref)
|
832 | |
|
833 | |
self.exit_contexts.append(exit_context)
|
834 | |
|
835 | |
# When we get to the end, we'll check that all the
|
836 | |
# exit contexts are consistent with each other.
|
837 | |
|
838 | |
# We set the current context as having terminated.
|
839 | |
# If we are in a branch, the merge will deal with
|
840 | |
# having terminated. If we are at the end of the
|
841 | |
# routine, the routine end will deal with that.
|
842 | |
|
843 | |
context.set_terminated()
|
844 | 781 |
|
845 | 782 |
elif opcode == 'trash':
|
846 | 783 |
context.set_touched(instr.dest)
|
|
849 | 786 |
pass
|
850 | 787 |
else:
|
851 | 788 |
raise NotImplementedError(opcode)
|
|
789 |
|
|
790 |
def analyze_call(self, instr, context):
|
|
791 |
type = instr.location.type
|
|
792 |
if not isinstance(type, (RoutineType, VectorType)):
|
|
793 |
raise TypeMismatchError(instr, instr.location)
|
|
794 |
if isinstance(type, VectorType):
|
|
795 |
type = type.of_type
|
|
796 |
for ref in type.inputs:
|
|
797 |
context.assert_meaningful(ref)
|
|
798 |
for ref in type.outputs:
|
|
799 |
context.set_written(ref)
|
|
800 |
for ref in type.trashes:
|
|
801 |
context.assert_writeable(ref)
|
|
802 |
context.set_touched(ref)
|
|
803 |
context.set_unmeaningful(ref)
|
|
804 |
|
|
805 |
def analyze_goto(self, instr, context):
|
|
806 |
location = instr.location
|
|
807 |
type_ = location.type
|
|
808 |
|
|
809 |
if not isinstance(type_, (RoutineType, VectorType)):
|
|
810 |
raise TypeMismatchError(instr, location)
|
|
811 |
|
|
812 |
# assert that the dest routine's inputs are all initialized
|
|
813 |
if isinstance(type_, VectorType):
|
|
814 |
type_ = type_.of_type
|
|
815 |
for ref in type_.inputs:
|
|
816 |
context.assert_meaningful(ref)
|
|
817 |
|
|
818 |
# and that this routine's trashes and output constraints are a
|
|
819 |
# superset of the called routine's
|
|
820 |
current_type = self.current_routine.location.type
|
|
821 |
self.assert_affected_within('outputs', type_, current_type)
|
|
822 |
self.assert_affected_within('trashes', type_, current_type)
|
|
823 |
|
|
824 |
context.encounter_gotos(set([instr.location]))
|
|
825 |
|
|
826 |
# Now that we have encountered a goto, we update the
|
|
827 |
# context here to match what someone calling the goto'ed
|
|
828 |
# function directly, would expect. (which makes sense
|
|
829 |
# when you think about it; if this goto's F, then calling
|
|
830 |
# this is like calling F, from the perspective of what is
|
|
831 |
# returned.)
|
|
832 |
#
|
|
833 |
# However, this isn't the current context anymore. This
|
|
834 |
# is an exit context of this routine.
|
|
835 |
|
|
836 |
exit_context = context.clone()
|
|
837 |
|
|
838 |
for ref in type_.outputs:
|
|
839 |
exit_context.set_touched(ref) # ?
|
|
840 |
exit_context.set_written(ref)
|
|
841 |
|
|
842 |
for ref in type_.trashes:
|
|
843 |
exit_context.assert_writeable(ref)
|
|
844 |
exit_context.set_touched(ref)
|
|
845 |
exit_context.set_unmeaningful(ref)
|
|
846 |
|
|
847 |
self.exit_contexts.append(exit_context)
|
|
848 |
|
|
849 |
# When we get to the end, we'll check that all the
|
|
850 |
# exit contexts are consistent with each other.
|
|
851 |
|
|
852 |
# We set the current context as having terminated.
|
|
853 |
# If we are in a branch, the merge will deal with
|
|
854 |
# having terminated. If we are at the end of the
|
|
855 |
# routine, the routine end will deal with that.
|
|
856 |
|
|
857 |
context.set_terminated()
|
852 | 858 |
|
853 | 859 |
def analyze_if(self, instr, context):
|
854 | 860 |
incoming_meaningful = set(context.each_meaningful())
|