summaryrefslogtreecommitdiff
path: root/Registers.py
diff options
context:
space:
mode:
Diffstat (limited to 'Registers.py')
-rw-r--r--Registers.py154
1 files changed, 154 insertions, 0 deletions
diff --git a/Registers.py b/Registers.py
new file mode 100644
index 0000000..2a070d4
--- /dev/null
+++ b/Registers.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+"""Compiles C into assembly for the practicum processor (PP2)
+
+All rights reserved, you may not redistribute or use this program without prior
+permission from Peter Wu or Xander Houtman. Use of this program is entirely
+your own risk. In no circumstances can the authors of this program be held
+responsible for any damage including, but not limited to, financial damage or
+data loss. Modification of this program is not allowed without prior
+permission. The generated output (assembly and messages) are not subject to
+this license.
+"""
+
+import re
+
+__author__ = "Peter Wu"
+__copyright__ = "Copyright 2011, Peter Wu"
+__credits__ = ["Peter Wu"]
+__license__ = "Proprietary"
+__version__ = "1.0"
+__maintainer__ = "Peter Wu"
+__email__ = "uwretep@gmail.com"
+
+class Registers(object):
+ """Register related functions
+
+ Some functions were supposed to optimize things (alloc and free), but it's
+ not implemented
+ """
+ def __init__(self):
+ # which registers are in use?
+ self.registers = {}
+ for reg in range(0, 8):
+ self.registers[str(reg)] = False
+ def alloc(self, register=None):
+ """Retrieves a register which is marked unused, marks it as in use and
+ return it"""
+ # if a register was explicitly requested
+ if register:
+ if not self.is_register(register):
+ raise RuntimeError("'{}' is not a register".format(register))
+ register = register[1]
+ if self.registers[register]:
+ raise RuntimeError("Register 'R{}' is already in use".format(register))
+ else:
+ for register in range(0, 6):
+ register = str(register)
+ # find a free register
+ if not self.registers[register]:
+ break
+ else:
+ raise RuntimeError("No free registers are available")
+ self.registers[register] = True
+ return "R" + register
+ def free(self, register):
+ """Marks a register as unused
+
+ Keyword arguments:
+ register -- a register in the format Rn where 0 <= n < 8
+
+ """
+ # remove leading R
+ register = register[1:]
+ if register in self.registers:
+ if self.registers[register]:
+ self.registers[register] = False;
+ else:
+ raise RuntimeError("free() of unused register")
+ else:
+ raise RuntimeError("free() of invalid register")
+ def get_register(self, text):
+ """Retrieve the first register from a binary instruction"""
+ text = text.strip()
+ # ignore commented lines
+ if text.startswith(";"):
+ return None
+ # skip labels if any
+ text = text.upper().split(":", 1)[-1]
+ # exclude the first operand, e.g. LOAD
+ text = text.lstrip(" ").split(" ", 1)[-1]
+ # retieve the first element before the next space, e.g. R0
+ text = text.lstrip(" ").split(" ", 1)[0]
+ # is it a register?
+ if self.is_register(text):
+ return text
+ # out of luck
+ return None
+ def is_register(self, text):
+ """Returns True if text is a register, false otherwise"""
+ return len(text) == 2 and text[0] == "R" and text[1] in self.registers
+ def get_instruction(self, text):
+ """Retrieve the instruction from text skipping labels and comments"""
+ text = text.strip()
+ # ignore commented lines
+ if text.startswith(";"):
+ return None
+ # skip labels if any
+ text = text.upper().split(":", 1)[-1]
+ # the first element is assumed to be an instruction
+ text = text.lstrip(" ").split(" ", 1)[0]
+ # instructions have a length between 2 (OR) and 4 (LOAD)
+ if len(text) >= 2 and len(text) <= 4:
+ return text
+ return None
+ def find_register(self, instructions_list, fatal=False):
+ """Finds the last modified register in a list of instructions"""
+ for line in reversed(instructions_list):
+ reg = self.get_register(line)
+ if reg:
+ return reg
+ else:
+ instruction = self.get_instruction(line)
+ # convention: non-void functions store their result value in R0
+ if instruction == "BRS":
+ return "R0"
+ if fatal:
+ raise RuntimeError("No register found in instructions list")
+ return None
+ def is_register_changed(self, line, register):
+ """Returns True if the register is possibly modified in the line
+
+ Keyword arguments:
+ line -- The instruction line to be analyzed
+ register -- The register to be looked for in the line
+ """
+ line = line.split(";", 1)[0].strip().upper()
+ register = register.upper()
+ if not self.is_register(register):
+ raise RuntimeError("Invalid register argument")
+ # split into at most 3 elements (instruction, register, operand)
+ matches = re.split("\s+", line, 2)
+ if len(matches) == 2:
+ instruction, reg = matches
+ if (instruction == "PULL" and
+ self.is_register(reg) and reg == register):
+ return True
+ # Assume function calls to be malicious
+ if instruction == "BRS":
+ return True
+ elif len(matches) == 3:
+ instruction, reg, operand = matches
+ # remove whitespace from the operand. LF and CR do not occur
+ operand = operand.translate(None, "\t ")
+ if (operand.startswith("[--" + register)
+ or operand.endswith(register + "++]")):
+ return True
+ if instruction == "STOR" and operand == register:
+ return True
+ # MULL and DVMD modify two registers
+ if (instruction in ("MULL", "DVMD") and self.is_register(reg) and
+ int(reg[1]) + 1 == int(register[1])):
+ return True
+ if instruction not in ("CMP", "CHCK") and reg == register:
+ return True
+ return False