mirror of
https://github.com/catseye/SixtyPical.git
synced 2025-02-23 01:28:57 +00:00
Better generation of code at tail of routines.
This commit is contained in:
parent
b9df1482c6
commit
44d97c33a2
@ -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`.
|
||||
|
||||
|
5
TODO.md
5
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
|
||||
--------------
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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`.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user