From e9e743876258e072ae8d07d0977157f9435a0008 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 1 Dec 2011 11:48:35 +0000 Subject: Support for <<= and >>=, refactor and comments, fix math_ops corruption --- README | 2 +- TODO | 3 -- pp2cc.py | 176 +++++++++++++++++++++++++++++++++++++-------------------------- 3 files changed, 105 insertions(+), 76 deletions(-) diff --git a/README b/README index 1e45505..1770a4f 100644 --- a/README +++ b/README @@ -63,7 +63,7 @@ Example in bits assuming 4-bit words: 1000 (-8) >> 2 becomes 0010 (2), 0110 (6) << 1 becomes 1100 (-4) A7.14-A7.15 Logical AND && and OR || are supported. Result is indeed 0 or 1 A7.16 Conditional operator ? : - supported -A7.17 Assignment - supported: = *= /= %= += -= &= ^= |= Unsupported: <<= >>= +A7.17 Assignment - supported: = *= /= %= += -= &= ^= |= <<= >>= Supported for variable names only, pointers and array references A7.18 Comma - supported A7.19 Constant expressions - not checked diff --git a/TODO b/TODO index ba21e8c..23d2c62 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,3 @@ -Support for other assignment operators: -<<= >>= - Post decrement/increment operators: p-- p++ 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)) -- cgit v1.2.1