Source code for Lib.Operands

"""
This file defines the base class :py:class:`Operand`
and its subclasses for different operands: :py:class:`Condition`,
:py:class:`DataLocation` and :py:class:`Function`.

The class :py:class:`DataLocation` itself has subclasses:
:py:class:`Register`, :py:class:`Offset` for address in memory,
:py:class:`Immediate` for constants and :py:class:`Temporary`
for location not yet allocated.

This file also define shortcuts for registers in RISCV.
"""

from typing import Dict, List
from MiniCParser import MiniCParser
from Lib.Errors import MiniCInternalError


[docs]class Operand(): pass
# signed version for riscv all_ops = ['blt', 'bgt', 'beq', 'bne', 'ble', 'bge', 'beqz', 'bnez'] opdict = {MiniCParser.LT: 'blt', MiniCParser.GT: 'bgt', MiniCParser.LTEQ: 'ble', MiniCParser.GTEQ: 'bge', MiniCParser.NEQ: 'bne', MiniCParser.EQ: 'beq'} opnot_dict = {'bgt': 'ble', 'bge': 'blt', 'blt': 'bge', 'ble': 'bgt', 'beq': 'bne', 'bne': 'beq', 'beqz': 'bnez', 'bnez': 'beqz'}
[docs]class Condition(Operand): """Condition, i.e. comparison operand for a CondJump. Example usage : - Condition('beq') = branch if equal. - Condition(MiniCParser.LT) = branch if lower than. - ... The constructor's argument shall be a string in the list all_ops, or a comparison operator in MiniCParser.LT, MiniCParser.GT, ... (one of the keys in opdict). A 'negate' method allows getting the negation of this condition. """ _op: str def __init__(self, optype): if optype in opdict: self._op = opdict[optype] elif str(optype) in all_ops: self._op = str(optype) else: raise MiniCInternalError(f"Unsupported comparison operator {optype}")
[docs] def negate(self) -> 'Condition': """Return the opposite condition.""" return Condition(opnot_dict[self._op])
def __str__(self): return self._op
[docs]class Function(Operand): """Operand for build-in function call.""" _name: str def __init__(self, name: str): self._name = name def __str__(self): return self._name
[docs]class DataLocation(Operand): """ A Data Location is either a register, a temporary or a place in memory (offset). """ pass
# map for register shortcuts reg_map = dict([(0, 'zero'), (1, 'ra'), (2, 'sp')] + # no (3, 'gp') nor (4, 'tp') [(i+5, 't'+str(i)) for i in range(3)] + [(8, 'fp'), (9, 's1')] + [(i+10, 'a'+str(i)) for i in range(8)] + [(i+18, 's'+str(i+2)) for i in range(10)] + [(i+28, 't'+str(i+3)) for i in range(4)])
[docs]class Register(DataLocation): """ A (physical) register.""" _number: int def __init__(self, number: int): self._number = number def __repr__(self): if self._number not in reg_map: raise MiniCInternalError(f"Register number {self._number} should not be used") else: return ("{}".format(reg_map[self._number])) def __eq__(self, other): return isinstance(other, Register) and self._number == other._number def __hash__(self): return self._number
# Shortcuts for registers in RISCV # Only integer registers #: Zero register ZERO = Register(0) #: RA = Register(1) #: SP = Register(2) #: Register not used for this course GP = Register(3) #: Register not used for this course TP = Register(4) #: A = tuple(Register(i + 10) for i in range(8)) #: S = tuple(Register(i + 8) for i in range(2)) + tuple(Register(i + 18) for i in range(10)) #: T = tuple(Register(i + 5) for i in range(3)) + tuple(Register(i + 28) for i in range(4)) #: A0 = A[0] # function args/return Values: A0, A1 #: A1 = A[1] #: Frame Pointer = Saved register 0 FP = S[0] #: General purpose registers, usable for the allocator GP_REGS = S[4:] + T # s0, s1, s2 and s3 are special
[docs]class Offset(DataLocation): """ Offset = address in memory computed with base + offset.""" _basereg: Register _offset: int def __init__(self, basereg: Register, offset: int): self._basereg = basereg self._offset = offset def __repr__(self): return ("{}({})".format(self._offset, self._basereg))
[docs] def get_offset(self) -> int: """Return the value of the offset.""" return self._offset
[docs]class Immediate(DataLocation): """Immediate operand (integer).""" _val: int def __init__(self, val): self._val = val def __str__(self): return str(self._val)
[docs]class Temporary(DataLocation): """Temporary, a location that has not been allocated yet. It will later be mapped to a physical register (Register) or to a memory location (Offset). """ _number: int _pool: 'TemporaryPool' def __init__(self, number: int, pool: 'TemporaryPool'): self._number = number self._pool = pool def __repr__(self): return ("temp_{}".format(str(self._number)))
[docs] def get_alloced_loc(self) -> DataLocation: """Return the DataLocation allocated to this Temporary.""" return self._pool.get_alloced_loc(self)
[docs]class TemporaryPool: """Manage a pool of temporaries.""" _all_temps: List[Temporary] _current_num: int _allocation: Dict[Temporary, DataLocation] def __init__(self): self._all_temps = [] self._current_num = 0 self._allocation = dict()
[docs] def get_all_temps(self) -> List[Temporary]: """Return all the temporaries of the pool.""" return self._all_temps
[docs] def get_alloced_loc(self, t: Temporary) -> DataLocation: """Get the actual DataLocation allocated for the temporary t.""" return self._allocation[t]
[docs] def add_tmp(self, t: Temporary): """Add a temporary to the pool.""" self._all_temps.append(t) self._allocation[t] = t # While no allocation, return the temporary itself
[docs] def set_temp_allocation(self, allocation: Dict[Temporary, DataLocation]) -> None: """Give a mapping from temporaries to actual registers. The argument allocation must be a dict from Temporary to DataLocation other than Temporary (typically Register or Offset). Typing enforces that keys are Temporary and values are Datalocation. We check the values are indeed not Temporary. """ for v in allocation.values(): assert not isinstance(v, Temporary), ( "Incorrect allocation scheme: value " + str(v) + " is a Temporary.") self._allocation = allocation
[docs] def fresh_tmp(self) -> Temporary: """Give a new fresh Temporary and add it to the pool.""" t = Temporary(self._current_num, self) self._current_num += 1 self.add_tmp(t) return t
[docs]class Renamer: """Manage a renaming of temporaries.""" _pool: TemporaryPool _env: Dict[Temporary, Temporary] def __init__(self, pool: TemporaryPool): self._pool = pool self._env = dict()
[docs] def fresh(self, t: Temporary) -> Temporary: """Give a fresh rename for a Temporary.""" new_t = self._pool.fresh_tmp() self._env[t] = new_t return new_t
[docs] def replace(self, t: Temporary) -> Temporary: """Give the rename for a Temporary (which is itself if it is not renamed).""" return self._env.get(t, t)
[docs] def defined(self, t: Temporary) -> bool: """True if the Temporary is renamed.""" return t in self._env
[docs] def copy(self): """Give a copy of the Renamer.""" r = Renamer(self._pool) r._env = self._env.copy() return r