git @ Cat's Eye Technologies SixtyPical / b20b664
Implement the "union rule for trashes" when analyzing `if` blocks. Chris Pressey 3 years ago
4 changed file(s) with 23 addition(s) and 15 deletion(s). Raw diff Collapse all Expand all
55
66 * `copy` is now understood to trash `a`, thus `copy ..., a` is not valid.
77 Indirect addressing is supported in `ld`, as in `ld a, [ptr] + y`, to compensate.
8 * Implements the "union rule for trashes" when analyzing `if` blocks.
89 * Fixed bug where `trash` was not marking the location as being virtually altered.
910
1011 0.11
6666 These might be forced to specify an initial value so that they can always be
6767 assumed to be meaningful.
6868
69 ### Union rule for trashes in `if`
70
71 If one branch trashes {`a`} and the other branch trashes {`b`} then the whole
72 `if` statement trashes {`a`, `b`}.
73
7469 ### Re-order routines and optimize tail-calls to fallthroughs
7570
7671 Not because it saves 3 bytes, but because it's a neat trick. Doing it optimally
9999 c._meaningful = set(self._meaningful)
100100 c._writeable = set(self._writeable)
101101 return c
102
103 def set_from(self, c):
104 assert c.routines == self.routines
105 assert c.routine == self.routine
106 self._touched = set(c._touched)
107 self._meaningful = set(c._meaningful)
108 self._writeable = set(c._writeable)
109102
110103 def each_meaningful(self):
111104 for ref in self._meaningful:
329322 context.set_touched(ref)
330323 context.set_unmeaningful(ref)
331324 elif opcode == 'if':
325 incoming_meaningful = set(context.each_meaningful())
326
332327 context1 = context.clone()
333328 context2 = context.clone()
334329 self.analyze_block(instr.block1, context1)
335330 if instr.block2 is not None:
336331 self.analyze_block(instr.block2, context2)
332
333 outgoing_meaningful = set(context1.each_meaningful()) & set(context2.each_meaningful())
334 outgoing_trashes = incoming_meaningful - outgoing_meaningful
335
337336 # TODO may we need to deal with touched separately here too?
338337 # probably not; if it wasn't meaningful in the first place, it
339338 # doesn't really matter if you modified it or not, coming out.
340339 for ref in context1.each_meaningful():
340 if ref in outgoing_trashes:
341 continue
341342 context2.assert_meaningful(
342343 ref, exception_class=InconsistentInitializationError,
343344 message='initialized in block 1 but not in block 2 of `if {}`'.format(src)
344345 )
345346 for ref in context2.each_meaningful():
347 if ref in outgoing_trashes:
348 continue
346349 context1.assert_meaningful(
347350 ref, exception_class=InconsistentInitializationError,
348351 message='initialized in block 2 but not in block 1 of `if {}`'.format(src)
349352 )
350 context.set_from(context1)
353
354 # merge the contexts. this used to be a method called `set_from`
355 context._touched = set(context1._touched) | set(context2._touched)
356 context._meaningful = outgoing_meaningful
357 context._writeable = set(context1._writeable) | set(context2._writeable)
358
359 for ref in outgoing_trashes:
360 context.set_touched(ref)
361 context.set_unmeaningful(ref)
362
351363 elif opcode == 'repeat':
352364 # it will always be executed at least once, so analyze it having
353365 # been executed the first time.
14161416 | trash x
14171417 | }
14181418 | }
1419 ? UnmeaningfulOutputError: x in foo
1419 ? ForbiddenWriteError: x in foo
14201420
14211421 | routine foo
14221422 | inputs a, x, z
14281428 | trash x
14291429 | }
14301430 | }
1431 ? UnmeaningfulOutputError: a in foo
1431 ? ForbiddenWriteError: a in foo
14321432
14331433 ### repeat ###
14341434