From 44d97c33a2145d4b02ee5f17077fcb1e650a90bd Mon Sep 17 00:00:00 2001 From: Chris Pressey Date: Tue, 22 Oct 2019 11:37:00 +0100 Subject: [PATCH] Better generation of code at tail of routines. --- HISTORY.md | 3 ++ TODO.md | 5 --- src/sixtypical/compiler.py | 61 ++++++++++++++++++++------------- src/sixtypical/emitter.py | 4 +++ tests/SixtyPical Compilation.md | 2 -- 5 files changed, 45 insertions(+), 30 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 75e8d68..64fa049 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -14,6 +14,9 @@ History of SixtyPical * Added `--prune-unreachable-routines` option, which causes the compiler to in fact omit routines determined to be unreachable as described above. +* Code generation now performs modest peephole optimization, + generating better code for `goto`s and `if` blocks at the + end of a routine. * The `dcc6502-adapter` test adapter was updated to conform to the output of the latest version of `dcc6502`. diff --git a/TODO.md b/TODO.md index 95967d4..861f8fe 100644 --- a/TODO.md +++ b/TODO.md @@ -90,11 +90,6 @@ if the block is in tail position. The constraints should iron out the same both As long as the routine has consistent type context every place it exits, that should be fine. -### Branch optimization in `if` - -Currently the `if` generator is not smart enough to avoid generating silly -jump instructions. (See the Fallthru tests.) Improve it. - Implementation -------------- diff --git a/src/sixtypical/compiler.py b/src/sixtypical/compiler.py index 4cc4dd3..23b1f0c 100644 --- a/src/sixtypical/compiler.py +++ b/src/sixtypical/compiler.py @@ -9,7 +9,7 @@ from sixtypical.model import ( TableType, PointerType, RoutineType, VectorType, REG_A, REG_X, REG_Y, FLAG_C ) -from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte +from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte, Emitter from sixtypical.gen6502 import ( Immediate, Absolute, AbsoluteX, AbsoluteY, ZeroPage, Indirect, IndirectY, Relative, LDA, LDX, LDY, STA, STX, STY, @@ -157,17 +157,31 @@ class Compiler(object): self.emitter.resolve_bss_label(label) def compile_routine(self, routine, next_routine=None): - self.current_routine = routine - self.skip_final_goto = (next_routine is not None) - self.final_goto_seen = False assert isinstance(routine, Routine) + + self.current_routine = routine + saved_emitter = self.emitter + self.emitter = Emitter(saved_emitter.addr) if routine.block: self.emitter.resolve_label(self.get_label(routine.name)) self.compile_block(routine.block) - if not self.final_goto_seen: + + needs_rts = True + if self.emitter.accum: + last_op = self.emitter.accum[-1] + if isinstance(last_op, JMP): + needs_rts = False + if isinstance(last_op.operand, Absolute): + if isinstance(last_op.operand.value, Label): + if next_routine and last_op.operand.value.name == next_routine.name: + self.emitter.retract() + + if needs_rts: self.emitter.emit(RTS()) + + saved_emitter.emit(*self.emitter.accum) + self.emitter = saved_emitter self.current_routine = None - self.skip_final_goto = False def compile_block(self, block): assert isinstance(block, Block) @@ -441,19 +455,15 @@ class Compiler(object): raise NotImplementedError(location_type) def compile_goto(self, instr): - self.final_goto_seen = True - if self.skip_final_goto: - pass + location = instr.location + label = self.get_label(instr.location.name) + location_type = self.get_type(location) + if isinstance(location_type, RoutineType): + self.emitter.emit(JMP(Absolute(label))) + elif isinstance(location_type, VectorType): + self.emitter.emit(JMP(Indirect(label))) else: - location = instr.location - label = self.get_label(instr.location.name) - location_type = self.get_type(location) - if isinstance(location_type, RoutineType): - self.emitter.emit(JMP(Absolute(label))) - elif isinstance(location_type, VectorType): - self.emitter.emit(JMP(Indirect(label))) - else: - raise NotImplementedError(location_type) + raise NotImplementedError(location_type) def compile_cmp(self, instr, src, dest): """`instr` is only for reporting purposes""" @@ -666,12 +676,17 @@ class Compiler(object): else_label = Label('else_label') self.emitter.emit(cls(Relative(else_label))) self.compile_block(instr.block1) + if instr.block2 is not None: - end_label = Label('end_label') - self.emitter.emit(JMP(Absolute(end_label))) - self.emitter.resolve_label(else_label) - self.compile_block(instr.block2) - self.emitter.resolve_label(end_label) + if instr.block1.shallow_contains_goto: + self.emitter.resolve_label(else_label) + self.compile_block(instr.block2) + else: + end_label = Label('end_label') + self.emitter.emit(JMP(Absolute(end_label))) + self.emitter.resolve_label(else_label) + self.compile_block(instr.block2) + self.emitter.resolve_label(end_label) else: self.emitter.resolve_label(else_label) diff --git a/src/sixtypical/emitter.py b/src/sixtypical/emitter.py index e574640..31ce731 100644 --- a/src/sixtypical/emitter.py +++ b/src/sixtypical/emitter.py @@ -171,6 +171,10 @@ class Emitter(object): self.accum.append(thing) self.addr += thing.size() + def retract(self): + thing = self.accum.pop() + self.addr -= thing.size() + def serialize_to(self, stream): """`stream` should be a file opened in binary mode.""" addr = self.start_addr diff --git a/tests/SixtyPical Compilation.md b/tests/SixtyPical Compilation.md index 687d57e..1f71955 100644 --- a/tests/SixtyPical Compilation.md +++ b/tests/SixtyPical Compilation.md @@ -610,7 +610,6 @@ Compiling `repeat forever`. = $080D LDY #$41 = $080F INY = $0810 JMP $080F - = $0813 RTS The body of `repeat forever` can be empty. @@ -620,7 +619,6 @@ The body of `repeat forever` can be empty. | } forever | } = $080D JMP $080D - = $0810 RTS Compiling `for ... up to`.