Better generation of code at tail of routines.

This commit is contained in:
Chris Pressey 2019-10-22 11:37:00 +01:00
parent b9df1482c6
commit 44d97c33a2
5 changed files with 45 additions and 30 deletions

View File

@ -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`.

View File

@ -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
--------------

View File

@ -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)

View File

@ -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

View File

@ -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`.