Module src.backend.stack
This module controls the virtual stack necessary for keeping track of base pointer, stack pointer, and anything else that is pushed/popped onto the virtual stack. By using a virtual stack we can correctly determine offsets of values stored in memory that gets referenced.
Expand source code
"""
This module controls the virtual stack necessary for keeping track of base pointer, stack pointer, and anything else that is pushed/popped onto the virtual stack. By using a virtual stack we can correctly determine offsets of values stored in memory that gets referenced.
"""
class stackObj():
"""
The object that is put on the stack, contains the type of the object on the stack (register, base pointer, etc) and the name of the object
"""
def __init__(self, **kwarg):
"""
Initializes the object that goes on the stack
Args:
kwarg: A dictionary of various objects
"""
# Scenarions:
# Its a known variable being stored
# Its a register with an unknown value being stored.
# RBP being pushed
if "Type" not in kwarg:
print ("Invalid object pushed to stack")
raise BaseException
self.type = kwarg["Type"]
self.Name = kwarg["Name"]
def __str__(self):
return f"{self.type} {self.Name}"
class Stack():
"""
Emulates the stack in order to store the locations of variables not in a register.
The base pointer is assumed to come before the start of the list. Similarly the stack pointer points exists after the end of the list.
"""
def __init__(self):
"""
Initializes the stack
"""
self.stk = []
self.lbp = -1
def push(self, objType, name=""):
"""
Takes in a list of asm strings using variable names, converts these to use proper register allocation
Args:
objType: The object type, essentially is it a base pointer, etc.
name: The name being stored in the stack object
"""
obj = stackObj(Name=name, Type=objType)
if objType == "BpMov":
self.lbp = 0
else:
self.lbp += 1
self.stk.append(obj)
pass
def pop(self):
"""
Pops the top object off of the stack
Returns:
the popped stack object
"""
self.lbp -= 1
return self.stk.pop()
pass
def peek(self):
"""
Returns the top item on the stack without popping it
Returns:
The top object on the stack
"""
return self.stk[-1]
def find_offset(self, var, bpSkipsAllowed=0):
"""
Finds the offset if the variable has an allocated slot on the stack. Otherwise returns None.
Args:
var: The variable name that is being checked for
bpSkipsAllowed: The number of basepointers that can be moved past, used to determine if something has a variable within scope
Returns:
Either none or the newly calulated offset
"""
skipCnt = -1
voi = -1
for i, e in enumerate(reversed(self.stk)):
if e.type == "BpMov":
skipCnt += 1
if skipCnt >= bpSkipsAllowed:
return None
elif e.Name == var:
voi = i
if voi != -1:
return (voi-self.lbp + 1)*8
# Variable cant be found on the stack
return None
def dist_from_base(self):
"""
Finds the number of bytes between the base pointer and stack pointer
Returns:
The number of bytes between the base pointer and the stack pointer
"""
return self.lbp * 8
def clear(self):
"""
Clears the stack for when it is needed
"""
self.stk = []
self.lbp = -1
def __str__(self):
return str([str(x) for x in self.stk])
Classes
class Stack
-
Emulates the stack in order to store the locations of variables not in a register. The base pointer is assumed to come before the start of the list. Similarly the stack pointer points exists after the end of the list.
Initializes the stack
Expand source code
class Stack(): """ Emulates the stack in order to store the locations of variables not in a register. The base pointer is assumed to come before the start of the list. Similarly the stack pointer points exists after the end of the list. """ def __init__(self): """ Initializes the stack """ self.stk = [] self.lbp = -1 def push(self, objType, name=""): """ Takes in a list of asm strings using variable names, converts these to use proper register allocation Args: objType: The object type, essentially is it a base pointer, etc. name: The name being stored in the stack object """ obj = stackObj(Name=name, Type=objType) if objType == "BpMov": self.lbp = 0 else: self.lbp += 1 self.stk.append(obj) pass def pop(self): """ Pops the top object off of the stack Returns: the popped stack object """ self.lbp -= 1 return self.stk.pop() pass def peek(self): """ Returns the top item on the stack without popping it Returns: The top object on the stack """ return self.stk[-1] def find_offset(self, var, bpSkipsAllowed=0): """ Finds the offset if the variable has an allocated slot on the stack. Otherwise returns None. Args: var: The variable name that is being checked for bpSkipsAllowed: The number of basepointers that can be moved past, used to determine if something has a variable within scope Returns: Either none or the newly calulated offset """ skipCnt = -1 voi = -1 for i, e in enumerate(reversed(self.stk)): if e.type == "BpMov": skipCnt += 1 if skipCnt >= bpSkipsAllowed: return None elif e.Name == var: voi = i if voi != -1: return (voi-self.lbp + 1)*8 # Variable cant be found on the stack return None def dist_from_base(self): """ Finds the number of bytes between the base pointer and stack pointer Returns: The number of bytes between the base pointer and the stack pointer """ return self.lbp * 8 def clear(self): """ Clears the stack for when it is needed """ self.stk = [] self.lbp = -1 def __str__(self): return str([str(x) for x in self.stk])
Methods
def clear(self)
-
Clears the stack for when it is needed
Expand source code
def clear(self): """ Clears the stack for when it is needed """ self.stk = [] self.lbp = -1
def dist_from_base(self)
-
Finds the number of bytes between the base pointer and stack pointer
Returns
The
number
ofbytes
between
the
base
pointer
and
the
stack
pointer
Expand source code
def dist_from_base(self): """ Finds the number of bytes between the base pointer and stack pointer Returns: The number of bytes between the base pointer and the stack pointer """ return self.lbp * 8
def find_offset(self, var, bpSkipsAllowed=0)
-
Finds the offset if the variable has an allocated slot on the stack. Otherwise returns None.
Args
var
- The variable name that is being checked for
bpSkipsAllowed
- The number of basepointers that can be moved past, used to determine if something has a variable within scope
Returns
Either
none
orthe
newly
calulated
offset
Expand source code
def find_offset(self, var, bpSkipsAllowed=0): """ Finds the offset if the variable has an allocated slot on the stack. Otherwise returns None. Args: var: The variable name that is being checked for bpSkipsAllowed: The number of basepointers that can be moved past, used to determine if something has a variable within scope Returns: Either none or the newly calulated offset """ skipCnt = -1 voi = -1 for i, e in enumerate(reversed(self.stk)): if e.type == "BpMov": skipCnt += 1 if skipCnt >= bpSkipsAllowed: return None elif e.Name == var: voi = i if voi != -1: return (voi-self.lbp + 1)*8 # Variable cant be found on the stack return None
def peek(self)
-
Returns the top item on the stack without popping it
Returns
The
top
object
on
the
stack
Expand source code
def peek(self): """ Returns the top item on the stack without popping it Returns: The top object on the stack """ return self.stk[-1]
def pop(self)
-
Pops the top object off of the stack
Returns
the
popped
stack
object
Expand source code
def pop(self): """ Pops the top object off of the stack Returns: the popped stack object """ self.lbp -= 1 return self.stk.pop() pass
def push(self, objType, name='')
-
Takes in a list of asm strings using variable names, converts these to use proper register allocation
Args
objType
- The object type, essentially is it a base pointer, etc.
name
- The name being stored in the stack object
Expand source code
def push(self, objType, name=""): """ Takes in a list of asm strings using variable names, converts these to use proper register allocation Args: objType: The object type, essentially is it a base pointer, etc. name: The name being stored in the stack object """ obj = stackObj(Name=name, Type=objType) if objType == "BpMov": self.lbp = 0 else: self.lbp += 1 self.stk.append(obj) pass
class stackObj (**kwarg)
-
The object that is put on the stack, contains the type of the object on the stack (register, base pointer, etc) and the name of the object
Initializes the object that goes on the stack
Args
kwarg
- A dictionary of various objects
Expand source code
class stackObj(): """ The object that is put on the stack, contains the type of the object on the stack (register, base pointer, etc) and the name of the object """ def __init__(self, **kwarg): """ Initializes the object that goes on the stack Args: kwarg: A dictionary of various objects """ # Scenarions: # Its a known variable being stored # Its a register with an unknown value being stored. # RBP being pushed if "Type" not in kwarg: print ("Invalid object pushed to stack") raise BaseException self.type = kwarg["Type"] self.Name = kwarg["Name"] def __str__(self): return f"{self.type} {self.Name}"