git @ Cat's Eye Technologies SixtyPical / 44d97c3
Better generation of code at tail of routines. Chris Pressey 5 years ago
5 changed file(s) with 46 addition(s) and 31 deletion(s). Raw diff Collapse all Expand all
1313 * Added `--prune-unreachable-routines` option, which causes
1414 the compiler to in fact omit routines determined to be
1515 unreachable as described above.
16 * Code generation now performs modest peephole optimization,
17 generating better code for `goto`s and `if` blocks at the
18 end of a routine.
1619 * The `dcc6502-adapter` test adapter was updated to conform
1720 to the output of the latest version of `dcc6502`.
1821
8989
9090 As long as the routine has consistent type context every place it exits, that should be fine.
9191
92 ### Branch optimization in `if`
93
94 Currently the `if` generator is not smart enough to avoid generating silly
95 jump instructions. (See the Fallthru tests.) Improve it.
96
9792 Implementation
9893 --------------
9994
88 TableType, PointerType, RoutineType, VectorType,
99 REG_A, REG_X, REG_Y, FLAG_C
1010 )
11 from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte
11 from sixtypical.emitter import Byte, Word, Table, Label, Offset, LowAddressByte, HighAddressByte, Emitter
1212 from sixtypical.gen6502 import (
1313 Immediate, Absolute, AbsoluteX, AbsoluteY, ZeroPage, Indirect, IndirectY, Relative,
1414 LDA, LDX, LDY, STA, STX, STY,
156156 self.emitter.resolve_bss_label(label)
157157
158158 def compile_routine(self, routine, next_routine=None):
159 assert isinstance(routine, Routine)
160
159161 self.current_routine = routine
160 self.skip_final_goto = (next_routine is not None)
161 self.final_goto_seen = False
162 assert isinstance(routine, Routine)
162 saved_emitter = self.emitter
163 self.emitter = Emitter(saved_emitter.addr)
163164 if routine.block:
164165 self.emitter.resolve_label(self.get_label(routine.name))
165166 self.compile_block(routine.block)
166 if not self.final_goto_seen:
167
168 needs_rts = True
169 if self.emitter.accum:
170 last_op = self.emitter.accum[-1]
171 if isinstance(last_op, JMP):
172 needs_rts = False
173 if isinstance(last_op.operand, Absolute):
174 if isinstance(last_op.operand.value, Label):
175 if next_routine and last_op.operand.value.name == next_routine.name:
176 self.emitter.retract()
177
178 if needs_rts:
167179 self.emitter.emit(RTS())
180
181 saved_emitter.emit(*self.emitter.accum)
182 self.emitter = saved_emitter
168183 self.current_routine = None
169 self.skip_final_goto = False
170184
171185 def compile_block(self, block):
172186 assert isinstance(block, Block)
440454 raise NotImplementedError(location_type)
441455
442456 def compile_goto(self, instr):
443 self.final_goto_seen = True
444 if self.skip_final_goto:
445 pass
446 else:
447 location = instr.location
448 label = self.get_label(instr.location.name)
449 location_type = self.get_type(location)
450 if isinstance(location_type, RoutineType):
451 self.emitter.emit(JMP(Absolute(label)))
452 elif isinstance(location_type, VectorType):
453 self.emitter.emit(JMP(Indirect(label)))
454 else:
455 raise NotImplementedError(location_type)
457 location = instr.location
458 label = self.get_label(instr.location.name)
459 location_type = self.get_type(location)
460 if isinstance(location_type, RoutineType):
461 self.emitter.emit(JMP(Absolute(label)))
462 elif isinstance(location_type, VectorType):
463 self.emitter.emit(JMP(Indirect(label)))
464 else:
465 raise NotImplementedError(location_type)
456466
457467 def compile_cmp(self, instr, src, dest):
458468 """`instr` is only for reporting purposes"""
665675 else_label = Label('else_label')
666676 self.emitter.emit(cls(Relative(else_label)))
667677 self.compile_block(instr.block1)
678
668679 if instr.block2 is not None:
669 end_label = Label('end_label')
670 self.emitter.emit(JMP(Absolute(end_label)))
671 self.emitter.resolve_label(else_label)
672 self.compile_block(instr.block2)
673 self.emitter.resolve_label(end_label)
680 if instr.block1.shallow_contains_goto:
681 self.emitter.resolve_label(else_label)
682 self.compile_block(instr.block2)
683 else:
684 end_label = Label('end_label')
685 self.emitter.emit(JMP(Absolute(end_label)))
686 self.emitter.resolve_label(else_label)
687 self.compile_block(instr.block2)
688 self.emitter.resolve_label(end_label)
674689 else:
675690 self.emitter.resolve_label(else_label)
676691
170170 self.accum.append(thing)
171171 self.addr += thing.size()
172172
173 def retract(self):
174 thing = self.accum.pop()
175 self.addr -= thing.size()
176
173177 def serialize_to(self, stream):
174178 """`stream` should be a file opened in binary mode."""
175179 addr = self.start_addr
609609 = $080D LDY #$41
610610 = $080F INY
611611 = $0810 JMP $080F
612 = $0813 RTS
613612
614613 The body of `repeat forever` can be empty.
615614
619618 | } forever
620619 | }
621620 = $080D JMP $080D
622 = $0810 RTS
623621
624622 Compiling `for ... up to`.
625623