summaryrefslogtreecommitdiff
path: root/pp2cc.py
diff options
context:
space:
mode:
authorPeter Wu <lekensteyn@gmail.com>2011-12-01 11:48:35 +0000
committerPeter Wu <lekensteyn@gmail.com>2011-12-01 11:48:35 +0000
commite9e743876258e072ae8d07d0977157f9435a0008 (patch)
tree1c191040a9a4d19593572920484becc5f37a35be /pp2cc.py
parent9e797458dd8d144544564674a8767c077ed4bd39 (diff)
downloadpp2cc-e9e743876258e072ae8d07d0977157f9435a0008.tar.gz
Support for <<= and >>=, refactor and comments, fix math_ops corruption
Diffstat (limited to 'pp2cc.py')
-rwxr-xr-xpp2cc.py176
1 files changed, 104 insertions, 72 deletions
diff --git a/pp2cc.py b/pp2cc.py
index 6217f41..c0e763d 100755
--- a/pp2cc.py
+++ b/pp2cc.py
@@ -459,8 +459,13 @@ class Parse(object):
"==": "BEQ",
"!=": "BNE"
}
- self.binary_ops = self.math_ops
+ self.shift_ops = {
+ ">>": "DIV",
+ "<<": "MULS"
+ }
+ self.binary_ops = self.math_ops.copy()
self.binary_ops.update(self.comparison_ops)
+ self.binary_ops.update(self.shift_ops)
# local and global variable names to be defined in @DATA
self.varNames = {}
@@ -648,8 +653,6 @@ class Parse(object):
op = self.binary_ops[node.op]
elif node.op in ("&&", "||"):
return self.parseBinaryLogicalOp(linked_node)
- elif node.op in ("<<", ">>"):
- op = node.op
else:
raise RuntimeError("Binary op is not implemented yet: " + node.op)
@@ -702,75 +705,94 @@ class Parse(object):
lines.append(self.asm.branch_op("BRA", bin_end))
lines.append(self.asm.binary_op("LOAD", reg_first, 1, label=bin_true))
lines.append(self.asm.noop(bin_end, register=reg_first))
- elif op in ("<<", ">>"):
- if isinstance(node.right, c_ast.Constant):
- shift_bits = self.convertStrToInteger(operand)
+ elif node.op in ("<<", ">>"):
+ lines += self.processShift(node.op, reg_first, operand, linked_node)
+ else:
+ lines.append(self.asm.binary_op(op, reg_first, operand))
+ return lines
+ def processShift(self, operator, reg_number, operand_shift, linked_node=None):
+ """Returns lines for a shift expression
+
+ Keyword arguments:
+ operator -- Either >> for right shift or << for left shift
+ reg_number -- A register Rn containing the number to be shifted
+ operand_shift -- An operand for the shift count. If it's a register,
+ the contents of it will be used as the shift count. Otherwise, the
+ string is interpreted as a number from assembly (allowing $ and %
+ notations)
+ linked_node -- An optional LinkedNode for showing the context in
+ warning messages
+ """
+ lines = []
+ assert operator in self.shift_ops, "Invalid shift operator '{}'".format(operator)
+ binop = self.shift_ops[operator]
+ if self.registers.is_register(operand_shift):
+ # alias operand_shift for clarity below, reg_shift is a register
+ reg_shift = operand_shift
+ lbl_shift = self.uniqLbl("shift_loop")
+ lbl_shift_end = self.uniqLbl("shift_done")
+ # necessary to set the status flags for the branch conditions
+ lines.append(self.asm.binary_op("CMP", reg_shift, 0))
+ # if operand 2 is lower than 1, do not bother shifting operand
+ # 1 and jump to the end
+ lines.append(self.asm.branch_op("BLE", lbl_shift_end))
+
+ # right shift needs special treatment for the exceptional case -1
+ if operator == ">>":
+ # see below for an explanation for what's happening here
+ lines.append(self.asm.binary_op("AND", reg_number, "%10"))
+ lines.append(self.asm.binary_op("DIV", reg_number, 2))
+ mask = "%0" + (self.WORDSIZE - 1) * "1"
+ lines.append(self.asm.binary_op("AND", reg_number, mask))
+
+ # decrease shift count and finish if shift count <= 0
+ lines.append(self.asm.binary_op("SUB", reg_shift, 1))
+ lines.append(self.asm.branch_op("BLE", lbl_shift_end))
+
+ # do a single shift and decrease remaining shift count
+ lines.append(self.asm.binary_op(binop, reg_number, 2,
+ label=lbl_shift))
+ lines.append(self.asm.binary_op("SUB", reg_shift, 1))
+ # if we came from BLE, x <= 0. BGT only jumps if x > 0, so
+ # we can safe another NOOP by adding the label here
+ lines.append(self.asm.branch_op("BGT", lbl_shift,
+ label=lbl_shift_end))
+ else:
+ shift_bits = self.convertStrToInteger(operand_shift)
+ if shift_bits <= 0:
+ # optimize when shifting by 0, but warn for negative
if shift_bits < 0:
- self.logger.warning("The number of bits to be shifted are"
- "negative, no operation will be performed",
+ self.logger.warning("The shift count is negative, no shift"
+ " operation will be performed",
linked_node=linked_node)
- lines.append(self.asm.binary_op("LOAD", reg_first, reg_first))
- elif shift_bits > 0:
- # shifting only makes a difference if shifting a number
- # larger than 0 (e.g. 1 << 1)
- operand = pow(2, shift_bits)
- if op == "<<":
- lines.append(self.asm.binary_op("MULS", reg_first, operand))
- else:
- # because DIV works with signed numbers, right shift is
- # a bit harder because the sign bit does not vanish
- # Assume 4-bit numbers in the next example.
- # binary | decimal
- # 1111 | -1
- # 0111 | 7
- # The rightmost bit is insignificant in a right shift
- # but causes issues with division by two for -1 because
- # it gets rounded to 0. After removal of the rightmost
- # bit and division by two, all bits are shifted left,
- # but the result is still negative. Since we're doing a
- # zero-padded shift (unsigned), clear the leftmost bit
- # For positive numbers, this has no side effects
- lines.append(self.asm.binary_op("AND", reg_first, "%10"))
- # shift it by one to the right
- lines.append(self.asm.binary_op("DIV", reg_first, 2))
- # clear the leftmost bit by masking it with 011..11
- mask = "%0" + (self.WORDSIZE - 1) * "1"
- lines.append(self.asm.binary_op("AND", reg_first, mask))
- # decrease shift count and finish if nothing to shift
- operand /= 2
- if operand > 1:
- # process the remaining shift
- lines.append(self.asm.binary_op("DIV", reg_first, operand))
+ lines.append(self.asm.binary_op("LOAD", reg_number, reg_number))
else:
- lbl_gen = self.uniqLbl("gen2_loop")
- lbl_gen_end = self.uniqLbl("gen2_done")
-
- # necessary to set the status flags for the branch conditions
- lines.append(self.asm.binary_op("CMP", reg_second, 0))
- # if operand 2 is lower than 1, do not bother shifting operand
- # 1 and jump to the end
- lines.append(self.asm.branch_op("BLE", lbl_gen_end))
-
- if op == "<<":
- lines.append(self.asm.binary_op("MULS", reg_first, 2, label=lbl_gen))
- else:
- # see above big block of comments for explanation
- lines.append(self.asm.binary_op("AND", reg_first, "%10"))
- lines.append(self.asm.binary_op("DIV", reg_first, 2))
+ operand = pow(2, shift_bits)
+ if operator == ">>":
+ # because DIV works with signed numbers, right shift is
+ # a bit harder because the sign bit does not vanish
+ # Assume 4-bit numbers in the next example.
+ # binary | decimal
+ # 1111 | -1
+ # 0111 | 7
+ # The rightmost bit is insignificant in a right shift
+ # but causes issues with division by two for -1 because
+ # it gets rounded to 0. After removal of the rightmost
+ # bit and division by two, all bits are shifted left,
+ # but the result is still negative. Since we're doing a
+ # zero-padded shift (unsigned), clear the leftmost bit
+ # For positive numbers, this has no side effects
+ lines.append(self.asm.binary_op("AND", reg_number, "%10"))
+ # shift it by one to the right
+ lines.append(self.asm.binary_op("DIV", reg_number, 2))
+ # clear the leftmost bit by masking it with 011..11
mask = "%0" + (self.WORDSIZE - 1) * "1"
- lines.append(self.asm.binary_op("AND", reg_first, mask))
-
- # decrease shift count and finish if shift count is zero
- lines.append(self.asm.binary_op("SUB", reg_second, 1))
- lines.append(self.asm.branch_op("BEQ", lbl_gen_end))
- # continue regular shifting
- lines.append(self.asm.binary_op("DIV", reg_first, 2, label=lbl_gen))
- lines.append(self.asm.binary_op("SUB", reg_second, 1))
- # if we came from BLE, x <= 0. BGT only jumps if x > 0, so
- # we can safe another NOOP by adding the label here
- lines.append(self.asm.branch_op("BGT", lbl_gen, label=lbl_gen_end))
- else:
- lines.append(self.asm.binary_op(op, reg_first, operand))
+ lines.append(self.asm.binary_op("AND", reg_number, mask))
+ # decrease shift count and finish if nothing to shift
+ operand /= 2
+ if operand > 1:
+ # process the remaining shift
+ lines.append(self.asm.binary_op(binop, reg_number, operand))
return lines
def parseUnaryOpLValue(self, linked_node):
lines = []
@@ -1164,20 +1186,30 @@ class Parse(object):
# if not an expression, it must be a single variable name
lines += self.parseLValue(linked_lval, register=lvalue_reg)
+ math_and_shift_ops = self.math_ops.copy()
+ math_and_shift_ops.update(self.shift_ops)
if node.op == "=":
lines.append(self.asm.binary_op("STOR", result_reg,
"[" + lvalue_reg + "]"))
- elif (len(node.op) == 2 and node.op[1] == "=" and
- node.op[0] in self.math_ops):
- binop = self.math_ops[node.op[0]]
+ elif (len(node.op) >= 2 and node.op[-1] == "=" and
+ node.op[0:-1] in math_and_shift_ops):
+ # the operator, like << or +
+ operator = node.op[0:-1]
+ binop = math_and_shift_ops[operator]
# abuse lvalue_reg to save a register by storing the value of the
# lvalue. This should allow for doing x / y first before assigning
# (writing) to y
lines.append(self.asm.push(lvalue_reg))
# get the value of lvalue, e.g. the value of x
lines.append(self.asm.binary_op("LOAD", lvalue_reg, "["+lvalue_reg+"]"))
+
# perform the operation on the value
- lines.append(self.asm.binary_op(binop, lvalue_reg, result_reg))
+ if operator in self.shift_ops:
+ lines += self.processShift(operator, lvalue_reg, result_reg,
+ linked_node)
+ else:
+ lines.append(self.asm.binary_op(binop, lvalue_reg, result_reg))
+
# since lvalue_reg is now the integer result, use result_reg as the
# register for holding the address of y
lines.append(self.asm.pull(result_reg))