mirror of
https://github.com/michaelcmartin/Ophis.git
synced 2024-12-10 16:53:04 +00:00
A new 'correctness optimization': ExtendBranches.
This pass actually isn't an optimizer in that it produces larger binaries when it triggers. However, the larger binaries created will actually assemble properly. The ExtendBranches pass detects Relative instructions (that is, branches) that extend past the signed-8-bit range Relative instructions permit, and replaces them with a branch-jump combination with identical semantics. Since this may be evidence of a program bug, Ophis will warn when the optimization is triggered. Due to similarities between this pass and UpdateLabels, both passes have been refactored in passing.
This commit is contained in:
parent
3184b22e41
commit
f8bc917601
@ -307,7 +307,7 @@ def parse_line(ppt, lexemelist):
|
||||
return IR.SequenceNode(ppt, result)
|
||||
|
||||
def parse_file(ppt, filename):
|
||||
"Loads a .P65 source file, and returns an IR list."
|
||||
"Loads an Ophis source file, and returns an IR list."
|
||||
Err.currentpoint = ppt
|
||||
if Cmd.verbose > 0: print "Loading "+filename
|
||||
try:
|
||||
|
@ -39,7 +39,7 @@ def SequenceNode(ppt, nodelist):
|
||||
return Node(ppt, "SEQUENCE", *nodelist)
|
||||
|
||||
class Expr:
|
||||
"""Base class for P65 expressions
|
||||
"""Base class for Ophis expressions
|
||||
All expressions have a field called "data" and a boolean field
|
||||
called "hardcoded". An expression is hardcoded if it has no
|
||||
symbolic values in it."""
|
||||
|
@ -40,6 +40,7 @@ def run_all(infile, outfile):
|
||||
l_basic = Ophis.Passes.UpdateLabels()
|
||||
l = Ophis.Passes.FixPoint("label update", [l_basic], lambda: l_basic.changed == 0)
|
||||
c = Ophis.Passes.Collapse()
|
||||
b = Ophis.Passes.ExtendBranches()
|
||||
a = Ophis.Passes.Assembler()
|
||||
|
||||
passes = []
|
||||
@ -47,7 +48,7 @@ def run_all(infile, outfile):
|
||||
passes.append(Ophis.Passes.FixPoint("macro expansion", [m], lambda: m.changed == 0))
|
||||
passes.append(Ophis.Passes.FixPoint("label initialization", [i], lambda: i.changed == 0))
|
||||
passes.extend([Ophis.Passes.CircularityCheck(), Ophis.Passes.CheckExprs(), Ophis.Passes.EasyModes()])
|
||||
passes.append(Ophis.Passes.FixPoint("instruction selection", [l, c], lambda: c.collapsed == 0))
|
||||
passes.append(Ophis.Passes.FixPoint("instruction selection", [l, c, b], lambda: c.collapsed == 0 and b.expanded == 0))
|
||||
passes.extend([Ophis.Passes.NormalizeModes(), Ophis.Passes.UpdateLabels(), a])
|
||||
|
||||
for p in passes: p.go(z, env)
|
||||
|
@ -9,20 +9,20 @@
|
||||
|
||||
# Names of addressing modes
|
||||
modes = ["Implied", # 0
|
||||
"Immediate", # 1
|
||||
"Zero Page", # 2
|
||||
"Zero Page, X", # 3
|
||||
"Zero Page, Y", # 4
|
||||
"Absolute", # 5
|
||||
"Absolute, X", # 6
|
||||
"Absolute, Y", # 7
|
||||
"(Absolute)", # 8
|
||||
"Immediate", # 1
|
||||
"Zero Page", # 2
|
||||
"Zero Page, X", # 3
|
||||
"Zero Page, Y", # 4
|
||||
"Absolute", # 5
|
||||
"Absolute, X", # 6
|
||||
"Absolute, Y", # 7
|
||||
"(Absolute)", # 8
|
||||
"(Absolute, X)", # 9
|
||||
"(Absolute), Y", # 10
|
||||
"(Zero Page)", # 11
|
||||
"(Zero Page, X)", # 12
|
||||
"(Zero Page), Y", # 13
|
||||
"Relative"] # 14
|
||||
"(Zero Page, X)", # 12
|
||||
"(Zero Page), Y", # 13
|
||||
"Relative"] # 14
|
||||
|
||||
# Lengths of the argument
|
||||
lengths = [0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1]
|
||||
|
@ -227,11 +227,9 @@ class EasyModes(Pass):
|
||||
def visitUnknown(self, node, env):
|
||||
pass
|
||||
|
||||
class UpdateLabels(Pass):
|
||||
"Computes the new values for all entries in the symbol table"
|
||||
name = "Label Update Pass"
|
||||
def prePass(self):
|
||||
self.changed = 0
|
||||
class PCTracker(Pass):
|
||||
"Superclass for passes that need an accurate program counter."
|
||||
name = "**BUG** PC Tracker Superpass used directly"
|
||||
def visitSetPC(self, node, env): env.setPC(node.data[0].value(env))
|
||||
def visitAdvance(self, node, env): env.setPC(node.data[0].value(env))
|
||||
def visitImplied(self, node, env): env.incPC(1)
|
||||
@ -256,17 +254,24 @@ class UpdateLabels(Pass):
|
||||
def visitPointerX(self, node, env): env.incPC(3)
|
||||
def visitPointerY(self, node, env): env.incPC(3)
|
||||
def visitCheckPC(self, node, env): pass
|
||||
def visitLabel(self, node, env): pass
|
||||
def visitByte(self, node, env): env.incPC(len(node.data))
|
||||
def visitWord(self, node, env): env.incPC(len(node.data)*2)
|
||||
def visitDword(self, node, env): env.incPC(len(node.data)*4)
|
||||
def visitWordBE(self, node, env): env.incPC(len(node.data)*2)
|
||||
def visitDwordBE(self, node, env): env.incPC(len(node.data)*4)
|
||||
|
||||
class UpdateLabels(PCTracker):
|
||||
"Computes the new values for all entries in the symbol table"
|
||||
name = "Label Update Pass"
|
||||
def prePass(self):
|
||||
self.changed = 0
|
||||
def visitLabel(self, node, env):
|
||||
(label, val) = node.data
|
||||
old = env[label]
|
||||
env[label] = val.value(env)
|
||||
if old != env[label]:
|
||||
self.changed = 1
|
||||
def visitByte(self, node, env): env.incPC(len(node.data))
|
||||
def visitWord(self, node, env): env.incPC(len(node.data)*2)
|
||||
def visitDword(self, node, env): env.incPC(len(node.data)*4)
|
||||
def visitWordBE(self, node, env): env.incPC(len(node.data)*2)
|
||||
def visitDwordBE(self, node, env): env.incPC(len(node.data)*4)
|
||||
|
||||
class Collapse(Pass):
|
||||
"""Selects as many zero-page instructions to convert as
|
||||
@ -344,6 +349,65 @@ def collapse_y_ind(node, env):
|
||||
else:
|
||||
return 0
|
||||
|
||||
class ExtendBranches(PCTracker):
|
||||
"""Eliminates any branch instructions that would end up going past
|
||||
the 128-byte range, and replaces them with a branch-jump
|
||||
pair. Also tracks how many elements where changed this pass."""
|
||||
name = "Branch Expansion Pass"
|
||||
reversed = { 'bcc': 'bcs',
|
||||
'bcs': 'bcc',
|
||||
'beq': 'bne',
|
||||
'bmi': 'bpl',
|
||||
'bne': 'beq',
|
||||
'bpl': 'bmi',
|
||||
'bvc': 'bvs',
|
||||
'bvs': 'bvc',
|
||||
# 65c02 ones. 'bra' is special, though, having no inverse
|
||||
'bbr0': 'bbs0',
|
||||
'bbs0': 'bbr0',
|
||||
'bbr1': 'bbs1',
|
||||
'bbs1': 'bbr1',
|
||||
'bbr2': 'bbs2',
|
||||
'bbs2': 'bbr2',
|
||||
'bbr3': 'bbs3',
|
||||
'bbs3': 'bbr3',
|
||||
'bbr4': 'bbs4',
|
||||
'bbs4': 'bbr4',
|
||||
'bbr5': 'bbs5',
|
||||
'bbs5': 'bbr5',
|
||||
'bbr6': 'bbs6',
|
||||
'bbs6': 'bbr6',
|
||||
'bbr7': 'bbs7',
|
||||
'bbs7': 'bbr7'
|
||||
}
|
||||
def prePass(self):
|
||||
self.expanded = 0
|
||||
def visitRelative(self, node, env):
|
||||
(opcode, expr) = node.data
|
||||
arg = expr.value(env)
|
||||
arg = arg-(env.getPC()+2)
|
||||
if arg < -128 or arg > 127:
|
||||
if opcode == 'bra':
|
||||
# If BRA - BRanch Always - is out of range, it's a JMP.
|
||||
node.data = ('jmp', expr)
|
||||
node.nodetype = "Absolute"
|
||||
if Cmd.verbose > 0:
|
||||
print str(node.ppt) + ": WARNING: bra out of range, replacing with jmp"
|
||||
else:
|
||||
# Otherwise, we replace it with a 'macro' of sorts by hand:
|
||||
# $branch LOC -> $reversed_branch ^+5; JMP LOC
|
||||
# We don't use temp labels here because labels need to have been fixed
|
||||
# in place by this point, and JMP is always 3 bytes long.
|
||||
expansion = [IR.Node(node.ppt, "Relative", ExtendBranches.reversed[opcode], IR.SequenceExpr([IR.PCExpr(), "+", IR.ConstantExpr(5)])),
|
||||
IR.Node(node.ppt, "Absolute", 'jmp', expr)]
|
||||
node.nodetype='SEQUENCE'
|
||||
node.data = expansion
|
||||
if Cmd.verbose > 0:
|
||||
print str(node.ppt) + ": WARNING: "+opcode+" out of range, replacing with "+ExtendBranches.reversed[opcode] +"/jmp combo"
|
||||
self.expanded += 1
|
||||
node.accept(self, env)
|
||||
else:
|
||||
env.incPC(2)
|
||||
|
||||
class NormalizeModes(Pass):
|
||||
"""Eliminates the intermediate "Memory" and "Pointer" nodes,
|
||||
|
BIN
tests/branch_c02.bin
Normal file
BIN
tests/branch_c02.bin
Normal file
Binary file not shown.
74
tests/branch_c02.oph
Normal file
74
tests/branch_c02.oph
Normal file
@ -0,0 +1,74 @@
|
||||
.text
|
||||
.org $0800
|
||||
early:
|
||||
bbr0 late
|
||||
bbr1 late
|
||||
bbr2 late
|
||||
bbr3 late
|
||||
bbr4 late
|
||||
bbr5 late
|
||||
bbr6 late
|
||||
bbr7 late
|
||||
bra late
|
||||
bbs0 late
|
||||
bbs1 late
|
||||
bbs2 late
|
||||
bbs3 late
|
||||
bbs4 late
|
||||
bbs5 late
|
||||
bbs6 late
|
||||
bbs7 late
|
||||
bbr0 early
|
||||
bbr1 early
|
||||
bbr2 early
|
||||
bbr3 early
|
||||
bbr4 early
|
||||
bbr5 early
|
||||
bbr6 early
|
||||
bbr7 early
|
||||
bra early
|
||||
bbs0 early
|
||||
bbs1 early
|
||||
bbs2 early
|
||||
bbs3 early
|
||||
bbs4 early
|
||||
bbs5 early
|
||||
bbs6 early
|
||||
bbs7 early
|
||||
.advance ^+256
|
||||
late:
|
||||
bbr0 late
|
||||
bbr1 late
|
||||
bbr2 late
|
||||
bbr3 late
|
||||
bbr4 late
|
||||
bbr5 late
|
||||
bbr6 late
|
||||
bbr7 late
|
||||
bra late
|
||||
bbs0 late
|
||||
bbs1 late
|
||||
bbs2 late
|
||||
bbs3 late
|
||||
bbs4 late
|
||||
bbs5 late
|
||||
bbs6 late
|
||||
bbs7 late
|
||||
bbr0 early
|
||||
bbr1 early
|
||||
bbr2 early
|
||||
bbr3 early
|
||||
bbr4 early
|
||||
bbr5 early
|
||||
bbr6 early
|
||||
bbr7 early
|
||||
bra early
|
||||
bbs0 early
|
||||
bbs1 early
|
||||
bbs2 early
|
||||
bbs3 early
|
||||
bbs4 early
|
||||
bbs5 early
|
||||
bbs6 early
|
||||
bbs7 early
|
||||
|
106
tests/branch_c02_ref.oph
Normal file
106
tests/branch_c02_ref.oph
Normal file
@ -0,0 +1,106 @@
|
||||
.text
|
||||
.org $0800
|
||||
early:
|
||||
bbs0 +
|
||||
jmp late
|
||||
* bbs1 +
|
||||
jmp late
|
||||
* bbs2 +
|
||||
jmp late
|
||||
* bbs3 +
|
||||
jmp late
|
||||
* bbs4 +
|
||||
jmp late
|
||||
* bbs5 +
|
||||
jmp late
|
||||
* bbs6 +
|
||||
jmp late
|
||||
* bbs7 +
|
||||
jmp late
|
||||
* jmp late
|
||||
* bbr0 +
|
||||
jmp late
|
||||
* bbr1 +
|
||||
jmp late
|
||||
* bbr2 +
|
||||
jmp late
|
||||
* bbr3 +
|
||||
jmp late
|
||||
* bbr4 +
|
||||
jmp late
|
||||
* bbr5 +
|
||||
jmp late
|
||||
* bbr6 +
|
||||
jmp late
|
||||
* bbr7 +
|
||||
jmp late
|
||||
* bbr0 early
|
||||
bbr1 early
|
||||
bbr2 early
|
||||
bbr3 early
|
||||
bbr4 early
|
||||
bbr5 early
|
||||
bbr6 early
|
||||
bbr7 early
|
||||
bra early
|
||||
bbs0 early
|
||||
bbs1 early
|
||||
bbs2 early
|
||||
bbs3 early
|
||||
bbs4 early
|
||||
bbs5 early
|
||||
bbs6 early
|
||||
bbs7 early
|
||||
.advance ^+256
|
||||
late:
|
||||
bbr0 late
|
||||
bbr1 late
|
||||
bbr2 late
|
||||
bbr3 late
|
||||
bbr4 late
|
||||
bbr5 late
|
||||
bbr6 late
|
||||
bbr7 late
|
||||
bra late
|
||||
bbs0 late
|
||||
bbs1 late
|
||||
bbs2 late
|
||||
bbs3 late
|
||||
bbs4 late
|
||||
bbs5 late
|
||||
bbs6 late
|
||||
bbs7 late
|
||||
bbs0 +
|
||||
jmp early
|
||||
* bbs1 +
|
||||
jmp early
|
||||
* bbs2 +
|
||||
jmp early
|
||||
* bbs3 +
|
||||
jmp early
|
||||
* bbs4 +
|
||||
jmp early
|
||||
* bbs5 +
|
||||
jmp early
|
||||
* bbs6 +
|
||||
jmp early
|
||||
* bbs7 +
|
||||
jmp early
|
||||
* jmp early
|
||||
bbr0 +
|
||||
jmp early
|
||||
* bbr1 +
|
||||
jmp early
|
||||
* bbr2 +
|
||||
jmp early
|
||||
* bbr3 +
|
||||
jmp early
|
||||
* bbr4 +
|
||||
jmp early
|
||||
* bbr5 +
|
||||
jmp early
|
||||
* bbr6 +
|
||||
jmp early
|
||||
* bbr7 +
|
||||
jmp early
|
||||
*
|
BIN
tests/longbranch.bin
Normal file
BIN
tests/longbranch.bin
Normal file
Binary file not shown.
37
tests/longbranch.oph
Normal file
37
tests/longbranch.oph
Normal file
@ -0,0 +1,37 @@
|
||||
.text
|
||||
.org $0800
|
||||
early:
|
||||
bpl late
|
||||
bmi late
|
||||
bvc late
|
||||
bvs late
|
||||
bcc late
|
||||
bcs late
|
||||
bne late
|
||||
beq late
|
||||
bpl early
|
||||
bmi early
|
||||
bvc early
|
||||
bvs early
|
||||
bcc early
|
||||
bcs early
|
||||
bne early
|
||||
beq early
|
||||
.advance $0900
|
||||
late:
|
||||
bpl late
|
||||
bmi late
|
||||
bvc late
|
||||
bvs late
|
||||
bcc late
|
||||
bcs late
|
||||
bne late
|
||||
beq late
|
||||
bpl early
|
||||
bmi early
|
||||
bvc early
|
||||
bvs early
|
||||
bcc early
|
||||
bcs early
|
||||
bne early
|
||||
beq early
|
54
tests/longbranch_ref.oph
Normal file
54
tests/longbranch_ref.oph
Normal file
@ -0,0 +1,54 @@
|
||||
.text
|
||||
.org $0800
|
||||
early:
|
||||
bmi +
|
||||
jmp late
|
||||
* bpl +
|
||||
jmp late
|
||||
* bvs +
|
||||
jmp late
|
||||
* bvc +
|
||||
jmp late
|
||||
* bcs +
|
||||
jmp late
|
||||
* bcc +
|
||||
jmp late
|
||||
* beq +
|
||||
jmp late
|
||||
* bne +
|
||||
jmp late
|
||||
* bpl early
|
||||
bmi early
|
||||
bvc early
|
||||
bvs early
|
||||
bcc early
|
||||
bcs early
|
||||
bne early
|
||||
beq early
|
||||
.advance $0900
|
||||
late:
|
||||
bpl late
|
||||
bmi late
|
||||
bvc late
|
||||
bvs late
|
||||
bcc late
|
||||
bcs late
|
||||
bne late
|
||||
beq late
|
||||
bmi +
|
||||
jmp early
|
||||
* bpl +
|
||||
jmp early
|
||||
* bvs +
|
||||
jmp early
|
||||
* bvc +
|
||||
jmp early
|
||||
* bcs +
|
||||
jmp early
|
||||
* bcc +
|
||||
jmp early
|
||||
* beq +
|
||||
jmp early
|
||||
* bne +
|
||||
jmp early
|
||||
*
|
Loading…
Reference in New Issue
Block a user