#!/usr/bin/env python """Compiles C into assembly for the practicum processor (PP2) Copyright (C) 2011-2014 Peter Wu Licensed under the MIT license . """ __author__ = "Peter Wu" __copyright__ = "Copyright (C) 2011-2014 Peter Wu" __credits__ = ["Peter Wu"] __license__ = "MIT" __version__ = "1.0" __maintainer__ = "Peter Wu" __email__ = "lekensteyn@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)