summaryrefslogtreecommitdiff
path: root/pp2cc.py
diff options
context:
space:
mode:
authorPeter Wu <lekensteyn@gmail.com>2011-11-28 23:16:58 +0000
committerPeter Wu <lekensteyn@gmail.com>2011-11-28 23:16:58 +0000
commit90ec610f709dc7cb25bbb3c288ec3e80ae0bc97e (patch)
tree4682c700187e043659ccd10d3b689b3a51c21917 /pp2cc.py
parentbb3c12c5c31f637642936e3177ad211fa3053e3e (diff)
downloadpp2cc-90ec610f709dc7cb25bbb3c288ec3e80ae0bc97e.tar.gz
Support for switch statement, initialize global variables by 0 by default
You shouldn't rely on the values of uninitialized variables, but it should not be initialized by 1 by default (typo: DW had to be DS)
Diffstat (limited to 'pp2cc.py')
-rwxr-xr-xpp2cc.py137
1 files changed, 133 insertions, 4 deletions
diff --git a/pp2cc.py b/pp2cc.py
index 14de70a..ecd73a8 100755
--- a/pp2cc.py
+++ b/pp2cc.py
@@ -10,7 +10,7 @@ permission. The generated output (assembly and messages) are not subject to
this license.
"""
-import sys, re, os
+import sys, re, os, operator
from pycparser import c_parser, c_ast
__author__ = "Peter Wu"
@@ -336,7 +336,7 @@ class Parse(object):
output.append("@DATA")
for varName in self.globalVars:
padding = " " * (16 - len(varName) - 1)
- output.append(self.nameToVar(varName) + padding + " DW 1")
+ output.append(self.nameToVar(varName) + padding + " DS 1")
output.append("")
output.append("@CODE")
# initialization of global variables
@@ -966,12 +966,59 @@ class Parse(object):
def parseEmptyStatement(self, linked_node):
"""Returns an empty list for an "empty statement" (duh)"""
return []
+ def parseSwitch(self, linked_node):
+ lines = []
+ lines_cases = []
+ lines_stmts = []
+ node = linked_node.node
+ stmt = node.stmt
+ cond = self.parseStatement(node.cond, linked_node)
+ switch_reg = self.registers.find_register(cond, fatal=True)
+ lines += cond
+ if not isinstance(stmt, c_ast.Compound):
+ raise RuntimeError("The switch statement is expected to have a"
+ " compound statement")
+
+ switch_end = self.uniqLbl("switchEnd")
+ linked_node.setBreak(switch_end)
+ lbl_default = None
+ for cn in stmt.block_items:
+ linked_cn = LinkedNode(cn, linked_node)
+ if linked_cn.type == "Case":
+ lbl_case = self.uniqLbl("case")
+ try:
+ case_val = self.determineConstValue(LinkedNode(cn.expr,
+ linked_cn))
+ except RuntimeError:
+ e = sys.exc_info()[1]
+ print "case label does not reduce to an integer constant"
+ print e
+ raise
+
+ lines_cases.append(self.asm.binary_op("CMP", switch_reg, case_val))
+ lines_cases.append(self.asm.branch_op("BEQ", lbl_case))
+ lines_stmts.append(self.asm.noop(lbl_case))
+ lines_stmts += self.parseStatement(cn.stmt, linked_cn)
+ elif linked_cn.type == "Default":
+ lbl_default = self.uniqLbl("default")
+ lines_stmts.append(self.asm.noop(lbl_default))
+ lines_stmts += self.parseStatement(cn.stmt, linked_cn)
+ else:
+ lines_stmts += self.parseStatement(cn, linked_cn)
+ lines += lines_cases
+ if lbl_default:
+ lines.append(self.asm.branch_op("BRA", lbl_default))
+ else:
+ lines.append(self.asm.branch_op("BRA", switch_end))
+ lines += lines_stmts
+ lines.append(self.asm.noop(switch_end))
+ return lines
def parseStatement(self, node, parent_linked_node, level_increment=False):
linked_node = LinkedNode(node, parent_linked_node, level_increment=level_increment)
self.asm.level = linked_node.level
lines = []
if linked_node.type in ("Compound", "If", "Return", "DoWhile", "While", "For",
- "Decl", "FuncDef", "Break", "Continue", "EmptyStatement"):
+ "Decl", "FuncDef", "Break", "Continue", "EmptyStatement", "Switch"):
lines += getattr(self, "parse" + linked_node.type)(linked_node)
elif linked_node.type in ("FuncDecl", "ArrayDecl", "Case", "DeclList",
"Default", "EllipsisParam",# (int a, ...)
@@ -979,7 +1026,7 @@ class Parse(object):
"Enumerator", # enum value
"EnumeratorList", # list of enum values
"FuncDecl", "Goto", "Label", "ParamList", "PtrDecl", "Struct",
- "TypeDecl", "Typedef", "Switch", "Union"):
+ "TypeDecl", "Typedef", "Union"):
raise RuntimeError("Not implemented for type " + repr(node))
else:
lines += self.parseExpression(node, parent_linked_node, level_increment)
@@ -1007,6 +1054,88 @@ class Parse(object):
else:
raise RuntimeError("Not implemented expression and unknown type: " + repr(node))
return lines
+ def determineConstValue(self, linked_node):
+ node = linked_node.node
+ value = None
+
+ # most significant bit, i.e. the bit that is one if negative
+ msb = pow(2, self.WORDSIZE - 1)
+
+ if linked_node.type == "Constant":
+ if node.type != "int":
+ raise RuntimeError("Unsupported constant type '{}'".format(node.type))
+ value = self.convertStrToInteger(self.convertInteger(node.value))
+ elif linked_node.type == "UnaryOp":
+ op = node.op
+ operators = {
+ "-": "neg",
+ "+": "pos",
+ "!": "not",
+ "~": "invert"
+ }
+ if op not in operators:
+ raise RuntimeError("Operator '{}' is not supported".format(op))
+ else:
+ operand = self.determineConstValue(node.expr)
+ if op == "-":
+ # negating a value in PP2 is flipping the WORDSIZE-th bit
+ value = operand ^ msb
+ elif op == "~":
+ # invert all bits
+ value = operand ^ (pow(2, self.WORDSIZE) - 1)
+ else:
+ value = int(getattr(operator, op)(operand))
+ elif linked_node.type == "BinaryOp":
+ op = node.op
+ operators = {
+ "*": "mul",
+ "/": "div",
+ "%": "mod",
+ "+": "add",
+ "-": "sub",
+ # invert result if the sign bits do not equal
+ "<": "lt",
+ ">": "gt",
+ "<=": "le",
+ ">=": "ge",
+ # end special treatment
+ "==": "eq",
+ "!=": "ne",
+ "&": "and_",
+ "^": "xor",
+ "|": "or_",
+ "<<": "lshift",
+ ">>": "rshift"
+ }
+ if op not in operators:
+ raise RuntimeError("Operator '{}' is not supported".format(op))
+ else:
+ operand1 = self.determineConstValue(node.left)
+ operand2 = self.determineConstValue(node.right)
+ if op in ("<", ">", "<=", ">="):
+ value = int(getattr(operator, op)(operand1, operand2))
+ # 0 < 1 and -2 < -1 holds, but -1 < 0 does not. If the sign
+ # bits are not equal, the result needs to be flipped
+ if operand2 & msb != operand2 & msb:
+ value = not value
+ elif op == "/" and operand2 & msb:
+ raise RuntimeError("Division by a negative value is"
+ "undefined", linked_node=linked_node)
+ elif op == "/" and operand2 == 0:
+ raise RuntimeError("Division by zero",
+ linked_node=linked_node)
+ else:
+ value = int(getattr(operator, op)(operand1, operand2))
+ else:
+ raise RuntimeError("Unsupported node type '{}'".format(linked_node.type))
+ # The PP2 can only count in 18-bit, therefore AND the result. Note that
+ # overflow may occur for large numbes. This is a non-issue for unary +
+ # - ~ (and ! because it results in 0 or 1). For binary comparison
+ # operators and bitwise operations, it's neither an issue because the
+ # word length does not increase. Right shift works fine, left shift may
+ # change the sign bit (as expected)
+ value &= pow(2, self.WORDSIZE) - 1
+ return value
if __name__ == "__main__":
settings = {