git @ Cat's Eye Technologies SixtyPical / 27df0d2
Optimize away `RTS` and `JMP` when possible. Fallthru stuff done. Chris Pressey 3 years ago
5 changed file(s) with 36 addition(s) and 32 deletion(s). Raw diff Collapse all Expand all
99 * Accessing zero-page with `ld` and `st` generates zero-page opcodes.
1010 * A `byte` or `word` table can be initialized with a list of constants.
1111 * Branching and repeating on the `n` flag is now supported.
12 * The `--optimize-fallthru` option causes the program to be analyzed
13 for fallthru optimizations; `--dump-fallthru-info` option outputs the
14 information from this analysis phase, in JSON format, to stdout.
12 * The `--optimize-fallthru` option causes the routines of the program
13 to be re-ordered to maximize the number of cases where a `goto`'ed
14 routine can be simply "falled through" to instead of `JMP`ed to.
15 * `--dump-fallthru-info` option outputs the information from the
16 fallthru analysis phase, in JSON format, to stdout.
17 * Even without fallthru optimization, `RTS` is no longer emitted after
18 the `JMP` from compiling a final `goto`.
1519 * Specifying multiple SixtyPical source files will produce a single
1620 compiled result from their combination.
1721 * Rudimentary support for Atari 2600 prelude in a 4K cartridge image,
6262 * [Literate test suite for SixtyPical syntax](tests/SixtyPical%20Syntax.md)
6363 * [Literate test suite for SixtyPical analysis](tests/SixtyPical%20Analysis.md)
6464 * [Literate test suite for SixtyPical compilation](tests/SixtyPical%20Compilation.md)
65 * [Literate test suite for SixtyPical fallthru optimization](tests/SixtyPical%20Fallthru.md)
6566 * [6502 Opcodes used/not used in SixtyPical](doc/6502%20Opcodes.md)
6667
6768 TODO
7172
7273 This preserves them, so that, semantically, they can be used later even though they
7374 are trashed inside the block.
74
75 ### Re-order routines and optimize tail-calls to fallthroughs
76
77 Not because it saves 3 bytes, but because it's a neat trick. Doing it optimally
78 is probably NP-complete. But doing it adequately is probably not that hard.
7975
8076 ### And at some point...
8177
106106
107107 for roster_row in compilation_roster:
108108 for routine_name in roster_row[0:-1]:
109 self.compile_routine(self.routines[routine_name])
109 self.compile_routine(self.routines[routine_name], skip_final_goto=True)
110110 routine_name = roster_row[-1]
111111 self.compile_routine(self.routines[routine_name])
112112
139139 if defn.initial is None and defn.addr is None:
140140 self.emitter.resolve_bss_label(label)
141141
142 def compile_routine(self, routine):
142 def compile_routine(self, routine, skip_final_goto=False):
143143 self.current_routine = routine
144 self.skip_final_goto = skip_final_goto
145 self.final_goto_seen = False
144146 assert isinstance(routine, Routine)
145147 if routine.block:
146148 self.emitter.resolve_label(self.get_label(routine.name))
147149 self.compile_block(routine.block)
148 self.emitter.emit(RTS())
150 if not self.final_goto_seen:
151 self.emitter.emit(RTS())
149152 self.current_routine = None
153 self.skip_final_goto = False
150154
151155 def compile_block(self, block):
152156 assert isinstance(block, Block)
352356 else:
353357 raise NotImplementedError
354358 elif opcode == 'goto':
355 location = instr.location
356 label = self.get_label(instr.location.name)
357 if isinstance(location.type, RoutineType):
358 self.emitter.emit(JMP(Absolute(label)))
359 elif isinstance(location.type, VectorType):
360 self.emitter.emit(JMP(Indirect(label)))
361 else:
362 raise NotImplementedError
359 self.final_goto_seen = True
360 if self.skip_final_goto:
361 pass
362 else:
363 location = instr.location
364 label = self.get_label(instr.location.name)
365 if isinstance(location.type, RoutineType):
366 self.emitter.emit(JMP(Absolute(label)))
367 elif isinstance(location.type, VectorType):
368 self.emitter.emit(JMP(Indirect(label)))
369 else:
370 raise NotImplementedError
363371 elif opcode == 'copy':
364372 self.compile_copy(instr, instr.src, instr.dest)
365373 elif opcode == 'trash':
775775 = $081E JMP ($0822)
776776 = $0821 RTS
777777
778 goto.
778 Compiling `goto`. Note that no `RTS` is emitted after the `JMP`.
779779
780780 | routine bar
781781 | inputs y
793793 | goto bar
794794 | }
795795 = $080D LDY #$C8
796 = $080F JMP $0813
797 = $0812 RTS
798 = $0813 LDX #$C8
799 = $0815 RTS
796 = $080F JMP $0812
797 = $0812 LDX #$C8
798 = $0814 RTS
800799
801800 ### Vector tables
802801
2424
2525 More formally, we can say
2626
27 fall : R → R ∪ {nil}, fall(r) ≠ r
27 > fall : R → R ∪ {nil}, fall(r) ≠ r
2828
2929 where `nil` is an atom that represents no routine.
3030
246246 | {
247247 | goto foo
248248 | }
249 = $080D JMP $0811
250 = $0810 RTS
251 = $0811 LDA #$00
252 = $0813 RTS
253 = $0814 LDA #$FF
254 = $0816 JMP $0811
255 = $0819 RTS
249 = $080D LDA #$00
250 = $080F RTS
251 = $0810 LDA #$FF
252 = $0812 JMP $080D