From 8247c54b7bf96d467a28774a97c963534d140a69 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Thu, 1 Dec 2011 15:47:34 +0000 Subject: Split classes in separate files --- Registers.py | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 Registers.py (limited to 'Registers.py') 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 -- cgit v1.2.1