mirror of
https://github.com/catseye/SixtyPical.git
synced 2024-06-07 22:29:27 +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
|
* Added `--prune-unreachable-routines` option, which causes
|
||||||
the compiler to in fact omit routines determined to be
|
the compiler to in fact omit routines determined to be
|
||||||
unreachable as described above.
|
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
|
* The `dcc6502-adapter` test adapter was updated to conform
|
||||||
to the output of the latest version of `dcc6502`.
|
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.
|
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
|
Implementation
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ from sixtypical.model import (
|
||||||
TableType, PointerType, RoutineType, VectorType,
|
TableType, PointerType, RoutineType, VectorType,
|
||||||
REG_A, REG_X, REG_Y, FLAG_C
|
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 (
|
from sixtypical.gen6502 import (
|
||||||
Immediate, Absolute, AbsoluteX, AbsoluteY, ZeroPage, Indirect, IndirectY, Relative,
|
Immediate, Absolute, AbsoluteX, AbsoluteY, ZeroPage, Indirect, IndirectY, Relative,
|
||||||
LDA, LDX, LDY, STA, STX, STY,
|
LDA, LDX, LDY, STA, STX, STY,
|
||||||
|
@ -157,17 +157,31 @@ class Compiler(object):
|
||||||
self.emitter.resolve_bss_label(label)
|
self.emitter.resolve_bss_label(label)
|
||||||
|
|
||||||
def compile_routine(self, routine, next_routine=None):
|
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)
|
assert isinstance(routine, Routine)
|
||||||
|
|
||||||
|
self.current_routine = routine
|
||||||
|
saved_emitter = self.emitter
|
||||||
|
self.emitter = Emitter(saved_emitter.addr)
|
||||||
if routine.block:
|
if routine.block:
|
||||||
self.emitter.resolve_label(self.get_label(routine.name))
|
self.emitter.resolve_label(self.get_label(routine.name))
|
||||||
self.compile_block(routine.block)
|
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())
|
self.emitter.emit(RTS())
|
||||||
|
|
||||||
|
saved_emitter.emit(*self.emitter.accum)
|
||||||
|
self.emitter = saved_emitter
|
||||||
self.current_routine = None
|
self.current_routine = None
|
||||||
self.skip_final_goto = False
|
|
||||||
|
|
||||||
def compile_block(self, block):
|
def compile_block(self, block):
|
||||||
assert isinstance(block, Block)
|
assert isinstance(block, Block)
|
||||||
|
@ -441,19 +455,15 @@ class Compiler(object):
|
||||||
raise NotImplementedError(location_type)
|
raise NotImplementedError(location_type)
|
||||||
|
|
||||||
def compile_goto(self, instr):
|
def compile_goto(self, instr):
|
||||||
self.final_goto_seen = True
|
location = instr.location
|
||||||
if self.skip_final_goto:
|
label = self.get_label(instr.location.name)
|
||||||
pass
|
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:
|
else:
|
||||||
location = instr.location
|
raise NotImplementedError(location_type)
|
||||||
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)
|
|
||||||
|
|
||||||
def compile_cmp(self, instr, src, dest):
|
def compile_cmp(self, instr, src, dest):
|
||||||
"""`instr` is only for reporting purposes"""
|
"""`instr` is only for reporting purposes"""
|
||||||
|
@ -666,12 +676,17 @@ class Compiler(object):
|
||||||
else_label = Label('else_label')
|
else_label = Label('else_label')
|
||||||
self.emitter.emit(cls(Relative(else_label)))
|
self.emitter.emit(cls(Relative(else_label)))
|
||||||
self.compile_block(instr.block1)
|
self.compile_block(instr.block1)
|
||||||
|
|
||||||
if instr.block2 is not None:
|
if instr.block2 is not None:
|
||||||
end_label = Label('end_label')
|
if instr.block1.shallow_contains_goto:
|
||||||
self.emitter.emit(JMP(Absolute(end_label)))
|
self.emitter.resolve_label(else_label)
|
||||||
self.emitter.resolve_label(else_label)
|
self.compile_block(instr.block2)
|
||||||
self.compile_block(instr.block2)
|
else:
|
||||||
self.emitter.resolve_label(end_label)
|
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:
|
else:
|
||||||
self.emitter.resolve_label(else_label)
|
self.emitter.resolve_label(else_label)
|
||||||
|
|
||||||
|
|
|
@ -171,6 +171,10 @@ class Emitter(object):
|
||||||
self.accum.append(thing)
|
self.accum.append(thing)
|
||||||
self.addr += thing.size()
|
self.addr += thing.size()
|
||||||
|
|
||||||
|
def retract(self):
|
||||||
|
thing = self.accum.pop()
|
||||||
|
self.addr -= thing.size()
|
||||||
|
|
||||||
def serialize_to(self, stream):
|
def serialize_to(self, stream):
|
||||||
"""`stream` should be a file opened in binary mode."""
|
"""`stream` should be a file opened in binary mode."""
|
||||||
addr = self.start_addr
|
addr = self.start_addr
|
||||||
|
|
|
@ -610,7 +610,6 @@ Compiling `repeat forever`.
|
||||||
= $080D LDY #$41
|
= $080D LDY #$41
|
||||||
= $080F INY
|
= $080F INY
|
||||||
= $0810 JMP $080F
|
= $0810 JMP $080F
|
||||||
= $0813 RTS
|
|
||||||
|
|
||||||
The body of `repeat forever` can be empty.
|
The body of `repeat forever` can be empty.
|
||||||
|
|
||||||
|
@ -620,7 +619,6 @@ The body of `repeat forever` can be empty.
|
||||||
| } forever
|
| } forever
|
||||||
| }
|
| }
|
||||||
= $080D JMP $080D
|
= $080D JMP $080D
|
||||||
= $0810 RTS
|
|
||||||
|
|
||||||
Compiling `for ... up to`.
|
Compiling `for ... up to`.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user