1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
#!/usr/bin/env python
"""Compiles C into assembly for the practicum processor (PP2)
Copyright (C) 2011-2014 Peter Wu <lekensteyn@gmail.com>
Licensed under the MIT license <http://opensource.org/licenses/MIT>.
"""
__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)
|