Ophis/src/Ophis/IR.py
Michael C. Martin f8bc917601 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.
2012-05-27 15:57:23 -07:00

161 lines
5.4 KiB
Python

"""Ophis Intermediate Representation
Classes for representing the Intermediate nodes upon which the
assembler passes operate."""
# Copyright 2002-2012 Michael C. Martin and additional contributors.
# You may use, modify, and distribute this file under the MIT
# license: See README for details.
import Ophis.Errors as Err
class Node:
"""The default IR Node
Instances of Node always have the three fields ppt(Program Point),
nodetype(a string), and data (a list)."""
def __init__(self, ppt, nodetype, *data):
self.ppt = ppt
self.nodetype = nodetype
self.data = list(data)
def accept(self, asmpass, env=None):
"""Implements the Visitor pattern for an assembler pass.
Calls the routine 'asmpass.visitTYPE(self, env)' where
TYPE is the value of self.nodetype."""
Err.currentpoint = self.ppt
routine = getattr(asmpass, "visit"+self.nodetype, asmpass.visitUnknown)
routine(self, env)
def __str__(self):
if self.nodetype != "SEQUENCE":
return str(self.ppt)+": "+self.nodetype+" - "+" ".join(map(str, self.data))
else:
return "\n".join(map(str, self.data))
def __repr__(self):
args = [self.ppt, self.nodetype] + self.data
return "Node(" + ", ".join(map(repr, args)) + ")"
NullNode = Node("<none>", "None")
def SequenceNode(ppt, nodelist):
return Node(ppt, "SEQUENCE", *nodelist)
class Expr:
"""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."""
def __init__(self, data):
self.data = data
self.hardcoded = 0
def __str__(self):
return "<UNKNOWN: "+`self.data`+">"
def valid(self, env=None, PCvalid=0):
"""Returns true if the the expression can be successfully
evaluated in the specified environment."""
return 0
def value(self, env=None):
"Evaluates this expression in the given environment."
return None
class ConstantExpr(Expr):
"Represents a numeric constant"
def __init__(self, data):
self.data = data
self.hardcoded = 1
def __str__(self):
return str(self.data)
def valid(self, env=None, PCvalid=0):
return 1
def value(self, env=None):
return self.data
class LabelExpr(Expr):
"Represents a symbolic constant"
def __init__(self, data):
self.data = data
self.hardcoded = 0
def __str__(self):
return self.data
def valid(self, env=None, PCvalid=0):
return (env is not None) and self.data in env
def value(self, env=None):
return env[self.data]
class PCExpr(Expr):
"Represents the current program counter: ^"
def __init__(self):
self.hardcoded = 0
def __str__(self):
return "^"
def valid(self, env=None, PCvalid=0):
return env is not None and PCvalid
def value(self, env=None):
return env.getPC()
class HighByteExpr(Expr):
"Represents the expression >{data}"
def __init__(self, data):
self.data = data
self.hardcoded = data.hardcoded
def __str__(self):
return ">"+str(self.data)
def valid(self, env=None, PCvalid=0):
return self.data.valid(env, PCvalid)
def value(self, env=None):
val = self.data.value(env)
return (val >> 8) & 0xff
class LowByteExpr(Expr):
"Represents the expression <{data}"
def __init__(self, data):
self.data = data
self.hardcoded = data.hardcoded
def __str__(self):
return "<"+str(self.data)
def valid(self, env=None, PCvalid=0):
return self.data.valid(env, PCvalid)
def value(self, env=None):
val = self.data.value(env)
return val & 0xff
class SequenceExpr(Expr):
"""Represents an interleaving of operands (of type Expr) and
operators (of type String). Subclasses must provide a routine
operate(self, firstarg, op, secondarg) that evaluates the
operator."""
def __init__(self, data):
"""Constructor for Sequence Expressions. Results will be
screwy if the data inpot isn't a list with types
[Expr, str, Expr, str, Expr, str, ... Expr, str, Expr]."""
self.data = data
self.operands = [x for x in data if isinstance(x, Expr)]
self.operators = [x for x in data if type(x)==str]
for i in self.operands:
if not i.hardcoded:
self.hardcoded = 0
break
else:
self.hardcoded = 1
def __str__(self):
return "["+" ".join(map(str, self.data))+"]"
def valid(self, env=None, PCvalid=0):
for i in self.operands:
if not i.valid(env, PCvalid):
return 0
return 1
def value(self, env=None):
subs = map((lambda x: x.value(env)), self.operands)
result = subs[0]
index = 1
for op in self.operators:
result = self.operate(result, op, subs[index])
index += 1
return result
def operate(self, start, op, other):
if op=="*": return start * other
if op=="/": return start // other
if op=="+": return start + other
if op=="-": return start - other
if op=="&": return start & other
if op=="|": return start | other
if op=="^": return start ^ other