#!/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. """ __author__ = "Peter Wu" __copyright__ = "Copyright 2011, Peter Wu" __credits__ = ["Peter Wu"] __license__ = "Proprietary" __version__ = "1.0" __maintainer__ = "Peter Wu" __email__ = "uwretep@gmail.com" class Variables(object): def __init__(self, parent_variables): """A scope for holding variable names Keywords arguments: parent_variables -- the parent Variables object. If None, it's a global variable scope """ self.parent_variables = parent_variables self.function = None if self.parent_variables and hasattr(self.parent_variables, "function"): self.function = self.parent_variables.function # key: name, value: address n relative to BP (n >= 1) self.local_vars = {} self.param_vars = [] def setFunction(self, function): """Sets the function object containing the stack allocation function""" self.function = function def getAddress(self, name): """Gets the address for a variable as a tuple containing a register and displacement To get the address of the variable, add the register value and displacement """ # first try the local scope if name in self.local_vars: # XXX don't hardcode R5 return ("R5", str(-self.local_vars[name])) try: # 1 for the return address, 1 for the stored BP return ("R5", str(2 + self.param_vars.index(name))) except ValueError: pass # lookup in the parent if self.parent_variables: return self.parent_variables.getAddress(name) raise RuntimeError("Use of undefined variable '{}'".format(name)) def declName(self, name, size=1, is_param=False, is_static=False): """Declares a variable in the nearest scope Keyword arguments: name -- The symbolic name of the variable size -- The size of the memory to be allocated in words (default 1) is_param -- Whether the name is a function parameter or not is_static -- Whether the variable should be static or not. Values of static variables are retained between function calls """ if name in self.local_vars or name in self.param_vars: raise RuntimeError("Redeclaration of variable '{}'".format(name)) # parameters do not need a special allocation because the callee # pushes it into the stack if is_param: self.param_vars.append(name) else: self.local_vars[name] = self.function.allocStack(size) class GlobalVariables(Variables): def __init__(self, defined_names): """A scope for holding variable names Keywords arguments: defined_names -- A dictionary holding identifiers which are already defined in assembly """ self.defined_names = defined_names # key: variable name, value: name as it appears in assembly self.global_vars = {} # a list of variable names which are declared and defined self.global_vars_defined = [] def _uniqName(self, name): """Returns an unique global variable name for assembly""" uniq_name = name i = 0 while uniq_name in self.defined_names: uniq_name = name + "_" + str(i) i += 1 return uniq_name def isDeclared(self, name): """Returns True if the global variable is declared, False otherwise""" return name in self.global_vars def isDefined(self, name): """Returns True if the global variable is defined, False otherwise""" return name in self.global_vars_defined def getAddress(self, name): """Gets the address for a variable as a tuple containing a register and displacement To get the address of the variable, add the register value and displacement """ if self.isDeclared(name): return ("GB", self.global_vars[name]) raise RuntimeError("Use of undefined variable '{}'".format(name)) def declName(self, name, size=1, is_param=False, is_static=False): """Declares a variable in the nearest scope Keyword arguments: name -- The symbolic name of the variable size -- The size of the memory to be allocated in words (default 1) is_param -- Whether the name is a function parameter or not is_static -- Whether the variable should be static or not. Static variables in this global scope are only visible in the file itself """ if name in self.global_vars: raise RuntimeError("Redeclaration of variable '{}'".format(name)) if is_param: raise RuntimeError("Parameter '{}' declared in global context".format(name)) if is_static: # static globals are prefixed with "svar_" and not shared between # files var_name = self._uniqName("svar_" + name) else: # global variables are prefixed "var_". Don't use _uniqName because # globals may be declared multiple times and are still shared var_name = "var_" + name self.global_vars[name] = var_name if var_name in self.defined_names: old_size = len(self.defined_names[var_name]) if size != old_size: raise RuntimeError("Size {} of global '{}' does not equal" " an earlier definition {}" .format(size, name, old_size)) else: # insert size items, initialized with 0 self.defined_names[var_name] = ["0"] * size def defName(self, name): """Marks a variable name as defined Variables may be declared multiple times in the globa scope, but not defined multiple times """ assert self.isDeclared(name), "Definition of undeclared '{}'".format(name) if self.isDefined(name): raise RuntimeError("Redefinition of '{}'".format(name)) self.global_vars_defined.append(name)