git @ Cat's Eye Technologies SixtyPical / dd29b6f
Implement local locations that aren't statically initialized. Chris Pressey 2 years ago
8 changed file(s) with 209 addition(s) and 64 deletion(s). Raw diff Collapse all Expand all
00 History of SixtyPical
11 =====================
2
3 0.20
4 ----
5
6 * Fixed a bug where two local statics could be declared with
7 the same name.
8 * Local locations need no longer be static. If they are not
9 static, they are considered uninitialized until assigned.
210
311 0.19
412 ----
5252
5353 These can co-exist with general, non-specific-table-linked `pointer` variables.
5454
55 ### Local non-statics
55 ### Space optimization of local non-statics
5656
57 Somewhat related to the above, it should be possible to declare a local storage
58 location which is not static.
57 If there are two routines A and B, and A never calls B (even indirectly), and
58 B never calls A (even indirectly), then their non-static locals can
59 be allocated at the same space.
5960
60 In this case, it would be considered uninitialized each time the routine was
61 entered.
61 This is more an impressive trick than a really useful feature, but still.
62 Impressive tricks are impressive.
6263
63 So, you do not have a guarantee that it has a valid value. But you are guaranteed
64 that no other routine can read or modify it.
65
66 It also enables a trick: if there are two routines A and B, and A never calls B
67 (even indirectly), and B never calls A (even indirectly), then their locals can
68 be allocated at the same space.
64 ### Locals with explicit addresses
6965
7066 A local could also be given an explicit address. In this case, two locals in
7167 different routines could be given the same address, and as long as the condition
182182 def assert_meaningful(self, *refs, **kwargs):
183183 exception_class = kwargs.get('exception_class', UnmeaningfulReadError)
184184 for ref in refs:
185 # statics are always meaningful
186 if self.symtab.has_static(self.routine.name, ref.name):
187 continue
185 if self.symtab.has_local(self.routine.name, ref.name):
186 if ref not in self._range:
187 message = ref.name
188 if kwargs.get('message'):
189 message += ' (%s)' % kwargs['message']
190 raise exception_class(self.routine, message)
191 else:
192 continue
188193 if self.is_constant(ref):
189194 pass
190195 elif isinstance(ref, LocationRef):
202207 def assert_writeable(self, *refs, **kwargs):
203208 exception_class = kwargs.get('exception_class', ForbiddenWriteError)
204209 for ref in refs:
205 # statics are always writeable
206 if self.symtab.has_static(self.routine.name, ref.name):
210 # locals are always writeable
211 if self.symtab.has_local(self.routine.name, ref.name):
207212 continue
208213 if ref not in self._writeable:
209214 message = ref.name
383388 def max_range(self, ref):
384389 if isinstance(ref, ConstantRef):
385390 return (ref.value, ref.value)
386 elif self.symtab.has_static(self.routine.name, ref.name):
387 return self.symtab.fetch_static_type(self.routine.name, ref.name).max_range
391 elif self.symtab.has_local(self.routine.name, ref.name):
392 return self.symtab.fetch_local_type(self.routine.name, ref.name).max_range
388393 else:
389394 return self.symtab.fetch_global_type(ref.name).max_range
390395
400405 # - - - - helper methods - - - -
401406
402407 def get_type_for_name(self, name):
403 if self.current_routine and self.symtab.has_static(self.current_routine.name, name):
404 return self.symtab.fetch_static_type(self.current_routine.name, name)
408 if self.current_routine and self.symtab.has_local(self.current_routine.name, name):
409 return self.symtab.fetch_local_type(self.current_routine.name, name)
405410 return self.symtab.fetch_global_type(name)
406411
407412 def get_type(self, ref):
461466 self.current_routine = routine
462467 type_ = self.get_type_for_name(routine.name)
463468 context = AnalysisContext(self.symtab, routine, type_.inputs, type_.outputs, type_.trashes)
469
470 # register any local statics as already-initialized
471 for local_name, local_symentry in self.symtab.locals.get(routine.name, {}).items():
472 ref = self.symtab.fetch_local_ref(routine.name, local_name)
473 if local_symentry.ast_node.initial is not None:
474 context.set_meaningful(ref)
475 context.set_range(ref, local_symentry.ast_node.initial, local_symentry.ast_node.initial)
476
464477 self.exit_contexts = []
465478
466479 self.analyze_block(routine.block, context)
504517
505518 # if something was touched, then it should have been declared to be writable.
506519 for ref in context.each_touched():
507 if ref not in type_.outputs and ref not in type_.trashes and not self.symtab.has_static(routine.name, ref.name):
520 if ref not in type_.outputs and ref not in type_.trashes and not self.symtab.has_local(routine.name, ref.name):
508521 raise ForbiddenWriteError(routine, ref.name)
509522
510523 self.exit_contexts = None
5858
5959 class Routine(AST):
6060 value_attrs = ('name', 'addr', 'initial',)
61 children_attrs = ('statics',)
61 children_attrs = ('locals',)
6262 child_attrs = ('block',)
6363
6464
3333 self.symtab = symtab
3434 self.emitter = emitter
3535 self.routines = {} # routine.name -> Routine
36 self.routine_statics = {} # routine.name -> { static.name -> Label }
36 self.routine_locals = {} # routine.name -> { local.name -> Label }
3737 self.labels = {} # global.name -> Label ("global" includes routines)
3838 self.trampolines = {} # Location -> Label
3939 self.current_routine = None
4141 # - - - - helper methods - - - -
4242
4343 def get_type_for_name(self, name):
44 if self.current_routine and self.symtab.has_static(self.current_routine.name, name):
45 return self.symtab.fetch_static_type(self.current_routine.name, name)
44 if self.current_routine and self.symtab.has_local(self.current_routine.name, name):
45 return self.symtab.fetch_local_type(self.current_routine.name, name)
4646 return self.symtab.fetch_global_type(name)
4747
4848 def get_type(self, ref):
7575
7676 def get_label(self, name):
7777 if self.current_routine:
78 static_label = self.routine_statics.get(self.current_routine.name, {}).get(name)
79 if static_label:
80 return static_label
78 local_label = self.routine_locals.get(self.current_routine.name, {}).get(name)
79 if local_label:
80 return local_label
8181 return self.labels[name]
8282
8383 def absolute_or_zero_page(self, label):
106106 label.set_addr(routine.addr)
107107 self.labels[routine.name] = label
108108
109 if hasattr(routine, 'statics'):
110 self.current_routine = routine
111 static_labels = {}
112 for defn in routine.statics:
113 length = self.compute_length_of_defn(defn)
114 label = Label(defn.name, addr=defn.addr, length=length)
115 static_labels[defn.name] = label
116 declarations.append((defn, self.symtab.fetch_static_type(routine.name, defn.name), label))
117 self.routine_statics[routine.name] = static_labels
118 self.current_routine = None
109 self.current_routine = routine
110 local_labels = {}
111 for defn in routine.locals:
112 length = self.compute_length_of_defn(defn)
113 label = Label(defn.name, addr=defn.addr, length=length)
114 local_labels[defn.name] = label
115 declarations.append((defn, self.symtab.fetch_local_type(routine.name, defn.name), label))
116 self.routine_locals[routine.name] = local_labels
117 self.current_routine = None
119118
120119 if compilation_roster is None:
121120 compilation_roster = [['main']] + [[routine.name] for routine in program.routines if routine.name != 'main']
3030 class SymbolTable(object):
3131 def __init__(self):
3232 self.symbols = {} # symbol name -> SymEntry
33 self.statics = {} # routine name -> (symbol name -> SymEntry)
33 self.locals = {} # routine name -> (symbol name -> SymEntry)
3434 self.typedefs = {} # type name -> Type AST
3535 self.consts = {} # const name -> ConstantRef
3636
4040 self.symbols[name] = SymEntry(None, TYPE_BIT)
4141
4242 def __str__(self):
43 return "Symbols: {}\nStatics: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.statics, self.typedefs, self.consts)
44
45 def has_static(self, routine_name, name):
46 return name in self.statics.get(routine_name, {})
43 return "Symbols: {}\nLocals: {}\nTypedefs: {}\nConsts: {}".format(self.symbols, self.locals, self.typedefs, self.consts)
44
45 def has_local(self, routine_name, name):
46 return name in self.locals.get(routine_name, {})
4747
4848 def fetch_global_type(self, name):
4949 return self.symbols[name].type_
5050
51 def fetch_static_type(self, routine_name, name):
52 return self.statics[routine_name][name].type_
51 def fetch_local_type(self, routine_name, name):
52 return self.locals[routine_name][name].type_
5353
5454 def fetch_global_ref(self, name):
5555 if name in self.symbols:
5656 return LocationRef(name)
5757 return None
5858
59 def fetch_static_ref(self, routine_name, name):
60 routine_statics = self.statics.get(routine_name, {})
61 if name in routine_statics:
59 def fetch_local_ref(self, routine_name, name):
60 routine_locals = self.locals.get(routine_name, {})
61 if name in routine_locals:
6262 return LocationRef(name)
6363 return None
6464
7575 def lookup(self, name, allow_forward=False, routine_name=None):
7676 model = self.symtab.fetch_global_ref(name)
7777 if model is None and routine_name:
78 model = self.symtab.fetch_static_ref(routine_name, name)
78 model = self.symtab.fetch_local_ref(routine_name, name)
7979 if model is None and allow_forward:
8080 return ForwardReference(name)
8181 if model is None:
8787 self.syntax_error('Symbol "%s" already declared' % name)
8888 self.symtab.symbols[name] = SymEntry(ast_node, type_)
8989
90 def declare_static(self, routine_name, name, ast_node, type_):
90 def declare_local(self, routine_name, name, ast_node, type_):
91 if self.symtab.fetch_local_ref(routine_name, name):
92 self.syntax_error('Symbol "%s" already declared locally' % name)
9193 if self.symtab.fetch_global_ref(name):
92 self.syntax_error('Symbol "%s" already declared' % name)
93 self.symtab.statics.setdefault(routine_name, {})[name] = SymEntry(ast_node, type_)
94 self.syntax_error('Symbol "%s" already declared globally' % name)
95 self.symtab.locals.setdefault(routine_name, {})[name] = SymEntry(ast_node, type_)
9496
9597 # ---- symbol resolution
9698
305307 type_ = self.defn_type()
306308 if not isinstance(type_, RoutineType):
307309 self.syntax_error("Can only define a routine, not {}".format(repr(type_)))
308 statics = []
310 locals_ = []
309311 if self.scanner.consume('@'):
310312 self.scanner.check_type('integer literal')
311313 block = None
312314 addr = int(self.scanner.token)
313315 self.scanner.scan()
314316 else:
315 statics = self.statics()
317 locals_ = self.locals()
316318 block = self.block()
317319 addr = None
318 return type_, Routine(self.scanner.line_number, name=name, block=block, addr=addr, statics=statics)
320 return type_, Routine(self.scanner.line_number, name=name, block=block, addr=addr, locals=locals_)
319321
320322 def labels(self):
321323 accum = []
369371 loc = IndexedRef(loc, offset, index)
370372 return loc
371373
372 def statics(self):
374 def locals(self):
373375 defns = []
374376 while self.scanner.consume('static'):
375377 type_, defn = self.defn()
376378 if defn.initial is None:
377379 self.syntax_error("Static definition {} must have initial value".format(defn))
378 self.declare_static(self.current_routine_name, defn.name, defn, type_)
380 self.declare_local(self.current_routine_name, defn.name, defn, type_)
381 defns.append(defn)
382 while self.scanner.consume('local'):
383 type_, defn = self.defn()
384 if defn.initial is not None:
385 self.syntax_error("Local definition {} may not have initial value".format(defn))
386 self.declare_local(self.current_routine_name, defn.name, defn, type_)
379387 defns.append(defn)
380388 return defns
381389
42504250 | }
42514251 = ok
42524252
4253 ### static ###
4253 ### locals ###
42544254
42554255 When memory locations are defined static to a routine, they cannot be
42564256 directly input, nor directly output; and since they are always initialized,
42754275 | call foo
42764276 | }
42774277 = ok
4278
4279 When memory locations are defined local to a routine, but not static,
4280 they cannot be directly input, nor directly output; but they are considered
4281 uninitialized from the time the routine is called to until a value is stored
4282 in them.
4283
4284 | define main routine
4285 | inputs x
4286 | outputs x
4287 | trashes z, n
4288 | local byte t
4289 | {
4290 | st x, t
4291 | inc t
4292 | ld x, t
4293 | }
4294 = ok
4295
4296 | define main routine
4297 | outputs x
4298 | trashes z, n
4299 | local byte t
4300 | {
4301 | inc t
4302 | ld x, t
4303 | }
4304 ? UnmeaningfulReadError: t
745745 | }
746746 ? SyntaxError
747747
748 Memory locations can be defined static to a routine.
748 Memory locations can be defined local to a routine.
749749
750750 | define foo routine
751751 | inputs x
752752 | outputs x
753753 | trashes z, n
754754 | static byte t : 0
755 | local word w
755756 | {
756757 | st x, t
757758 | inc t
761762 | define main routine
762763 | trashes a, x, z, n
763764 | static byte t : 0
765 | local word w
764766 | {
765767 | ld x, t
766768 | call foo
767769 | }
768770 = ok
769771
770 Static memory locations must always be given an initial value.
772 Local static memory locations must always be given an initial value.
771773
772774 | define main routine
773775 | inputs x
781783 | }
782784 ? SyntaxError
783785
784 Name of a static cannot shadow an existing global or static.
786 Local dynamic memory locations may not be given an initial value.
787
788 | define main routine
789 | inputs x
790 | outputs x
791 | trashes z, n
792 | local byte t : 10
793 | {
794 | st x, t
795 | inc t
796 | ld x, t
797 | }
798 ? SyntaxError
799
800 Name of a local cannot shadow an existing global or local.
785801
786802 | byte t
787803 |
789805 | inputs x
790806 | outputs x
791807 | trashes z, n
792 | static byte t
808 | static byte t : 10
793809 | {
794810 | st x, t
795811 | inc t
801817 | inputs x
802818 | outputs x
803819 | trashes z, n
804 | static byte t
805 | static byte t
820 | static byte t : 10
821 | static byte t : 20
806822 | {
807823 | st x, t
808824 | inc t
809825 | ld x, t
810826 | }
811827 ? SyntaxError
828
829 | byte t
830 |
831 | define main routine
832 | inputs x
833 | outputs x
834 | trashes z, n
835 | local byte t
836 | {
837 | st x, t
838 | inc t
839 | ld x, t
840 | }
841 ? SyntaxError
842
843 | define main routine
844 | inputs x
845 | outputs x
846 | trashes z, n
847 | local word w
848 | local word w
849 | {
850 | st x, t
851 | inc t
852 | ld x, t
853 | }
854 ? SyntaxError
855
856 Since the names of locals are lexically local to a routine, they cannot
857 appear in the inputs, outputs, trashes list of the routine.
858
859 | define main routine
860 | inputs t
861 | static byte t : 0
862 | {
863 | inc t
864 | }
865 ? SyntaxError
866
867 | define main routine
868 | outputs t
869 | static byte t : 0
870 | {
871 | inc t
872 | }
873 ? SyntaxError
874
875 | define main routine
876 | trashes t
877 | static byte t : 0
878 | {
879 | inc t
880 | }
881 ? SyntaxError
882
883 | define main routine
884 | inputs t
885 | local byte t
886 | {
887 | inc t
888 | }
889 ? SyntaxError
890
891 | define main routine
892 | outputs t
893 | local byte t
894 | {
895 | inc t
896 | }
897 ? SyntaxError
898
899 | define main routine
900 | trashes t
901 | local byte t
902 | {
903 | inc t
904 | }
905 ? SyntaxError