git @ Cat's Eye Technologies SixtyPical / cfca03e
Merge pull request #17 from catseye/inconsistent-initialization Drop check for inconsistent initialization Chris Pressey authored 3 years ago GitHub committed 3 years ago
5 changed file(s) with 58 addition(s) and 49 deletion(s). Raw diff Collapse all Expand all
33 0.18
44 ----
55
6 * The "consistent initialization" check inside `if` blocks has
7 been dropped. If a location is initialized inside one block
8 but not the other, it is treated as uninitialized afterwards.
69 * Syntactically, `goto` may only appear at the end of a block.
710 It need no longer be the final instruction in a routine,
811 as long as the type context is consistent at every exit.
4343 An alternative would be `static` pointers, which are currently not possible because
4444 pointers must be zero-page, thus `@`, thus uninitialized.
4545
46 ### Question "consistent initialization"
47
48 Question the value of the "consistent initialization" principle for `if` statement analysis.
49
50 Part of this is the trashes at the end; I think what it should be is that the trashes
51 after the `if` is the union of the trashes in each of the branches; this would obviate the
52 need to `trash` values explicitly, but if you tried to access them afterwards, it would still
53 error.
54
5546 ### Tail-call optimization
5647
5748 If a block ends in a `call` can that be converted to end in a `goto`? Why not? I think it can,
310310 ld a, 1
311311 st a, player_died
312312 }
313
314 // FIXME these trashes, strictly speaking, probably shouldn't be needed,
315 // but currently the compiler cares a little too much about values that are
316 // initialized in one branch of an `if`, but not the other, but are trashed
317 // at the end of the routine anyway.
318 trash ptr
319 trash y
320 trash v
321313 }
322314 }
323315
354346 add ptr, pos
355347 copy 82, [ptr] + y
356348 }
357
358 // FIXME these trashes, strictly speaking, probably shouldn't be needed,
359 // but currently the compiler cares too much about values that are
360 // initialized in one branch of an `if`, but not the other, but trashed
361 // at the end of the routine anyway.
362 trash ptr
363 trash y
364349 } else {
365350 copy delta, compare_target
366351 st on, c
410410
411411 self.analyze_block(routine.block, context)
412412
413 trashed = set(context.each_touched()) - set(context.each_meaningful())
414
413415 if self.debug:
414416 print("at end of routine `{}`:".format(routine.name))
415417 print(context)
436438 if set(ex.each_writeable()) != exit_writeable:
437439 raise InconsistentExitError("Exit contexts are not consistent")
438440 context.update_from(exit_context)
439
440 trashed = set(context.each_touched()) - set(context.each_meaningful())
441441
442442 # these all apply whether we encountered goto(s) in this routine, or not...:
443443
800800 outgoing_meaningful = set(context1.each_meaningful()) & set(context2.each_meaningful())
801801 outgoing_trashes = incoming_meaningful - outgoing_meaningful
802802
803 # TODO may we need to deal with touched separately here too?
804 # probably not; if it wasn't meaningful in the first place, it
805 # doesn't really matter if you modified it or not, coming out.
806 for ref in context1.each_meaningful():
807 if ref in outgoing_trashes:
808 continue
809 context2.assert_meaningful(
810 ref, exception_class=InconsistentInitializationError,
811 message='initialized in block 1 but not in block 2 of `if {}`'.format(instr.src)
812 )
813 for ref in context2.each_meaningful():
814 if ref in outgoing_trashes:
815 continue
816 context1.assert_meaningful(
817 ref, exception_class=InconsistentInitializationError,
818 message='initialized in block 2 but not in block 1 of `if {}`'.format(instr.src)
819 )
820
821803 # merge the contexts.
822804
823805 # first, the easy case: if one of the contexts has terminated, just use the other one.
16281628 | }
16291629 = ok
16301630
1631 If a location is initialized in one block, is must be initialized in the other as well.
1631 If a location is initialized in one block, it must be initialized in the other as well
1632 in order to be considered to be initialized after the block. If it is not consistent,
1633 it will be considered uninitialized.
16321634
16331635 | define foo routine
16341636 | inputs a
16421644 | ld a, 23
16431645 | }
16441646 | }
1645 ? InconsistentInitializationError: x
1647 ? UnmeaningfulOutputError: x
16461648
16471649 | define foo routine
16481650 | inputs a
16561658 | ld x, 7
16571659 | }
16581660 | }
1659 ? InconsistentInitializationError: x
1661 ? UnmeaningfulOutputError: x
16601662
16611663 | define foo routine
16621664 | inputs a
16701672 | ld x, 7
16711673 | }
16721674 | }
1673 ? InconsistentInitializationError: x
1675 ? UnmeaningfulOutputError: x
1676
1677 | define foo routine
1678 | inputs a
1679 | trashes a, x, z, n, c
1680 | {
1681 | cmp a, 42
1682 | if not z {
1683 | ld a, 6
1684 | } else {
1685 | ld x, 7
1686 | }
1687 | ld a, x
1688 | }
1689 ? UnmeaningfulReadError: x
1690
1691 If we don't care if it's uninitialized after the `if`, that's okay then.
1692
1693 | define foo routine
1694 | inputs a
1695 | trashes a, x, z, n, c
1696 | {
1697 | cmp a, 42
1698 | if not z {
1699 | ld a, 6
1700 | } else {
1701 | ld x, 7
1702 | }
1703 | }
1704 = ok
1705
1706 Or, if it does get initialized on both branches, that's okay then.
1707
1708 | define foo routine
1709 | inputs a
1710 | outputs x
1711 | trashes a, z, n, c
1712 | {
1713 | cmp a, 42
1714 | if not z {
1715 | ld x, 0
1716 | ld a, 6
1717 | } else {
1718 | ld x, 7
1719 | }
1720 | }
1721 = ok
16741722
16751723 However, this only pertains to initialization. If a value is already
16761724 initialized, either because it was set previous to the `if`, or is an
17191767 | ld x, 7
17201768 | }
17211769 | }
1722 ? InconsistentInitializationError: x
1770 ? UnmeaningfulOutputError: x
17231771
17241772 | define foo routine
17251773 | inputs a