Source code for Lib.FunctionData

"""
This file defines the base class :py:class:`FunctionData`,
containing metadata on a RiscV function, as well as utility
functions common to the different intermediate representations.
"""

from typing import (List, Callable, TypeVar)
from Lib.Errors import AllocationError
from Lib.Operands import (
    Offset, Temporary, TemporaryPool,
    S, T, FP)
from Lib.Statement import (Statement, Instruction, Label, Comment)


[docs]class FunctionData: """ Stores some metadata on a RiscV function: name of the function, label names, temporary variables (using :py:class:`Lib.Operands.TemporaryPool`), and div_by_zero label. This class is usually used indirectly through the different intermediate representations we work with, such as :py:attr:`Lib.LinearCode.LinearCode.fdata`. """ _nblabel: int _dec: int _pool: TemporaryPool _name: str _label_div_by_zero: Label def __init__(self, name: str): self._nblabel = -1 self._dec = 0 self._pool = TemporaryPool() self._name = name self._label_div_by_zero = self.fresh_label("div_by_zero")
[docs] def get_name(self) -> str: """Return the name of the function.""" return self._name
[docs] def fresh_tmp(self) -> Temporary: """ Return a new fresh Temporary, which is added to the pool. """ return self._pool.fresh_tmp()
[docs] def fresh_offset(self) -> Offset: """ Return a new offset in the memory stack. Offsets are decreasing relative to FP. """ self._dec = self._dec + 1 # For ld or sd, an offset on 12 signed bits is expected # Raise an error if the offset is too big if -8 * self._dec < - 2 ** 11: raise AllocationError( "Offset given by the allocation too big to be manipulated ({}), sorry." .format(self._dec)) return Offset(FP, -8 * self._dec)
[docs] def get_offset(self) -> int: """ Return the current offset in the memory stack. """ return self._dec
def _fresh_label_name(self, name) -> str: """ Return a new unique label name based on the given string. """ self._nblabel = self._nblabel + 1 return name + "_" + str(self._nblabel) + "_" + self._name
[docs] def fresh_label(self, name) -> Label: """ Return a new label, with a unique name based on the given string. """ return Label(self._fresh_label_name(name))
[docs] def get_label_div_by_zero(self) -> Label: return self._label_div_by_zero
_T = TypeVar("_T", bound=Statement) def _iter_statements( listIns: List[_T], f: Callable[[_T], List[_T]]) -> List[_T | Comment]: """Iterate over instructions. For each real instruction i (not label or comment), replace it with the list of instructions given by f(i). """ newListIns: List[_T | Comment] = [] for old_i in listIns: # Do nothing for label or comment if not isinstance(old_i, Instruction): newListIns.append(old_i) continue new_i_list = f(old_i) # Otherwise, replace the instruction by the list # returned by f, with comments giving the replacement newListIns.append(Comment("Replaced " + str(old_i))) newListIns.extend(new_i_list) return newListIns def _print_code(listIns: List, fdata: FunctionData, output, init_label=None, fin_label=None, fin_div0=False, comment=None) -> None: """ Please use print_code from LinearCode or CFG, not directly this one. Print the instructions from listIns, forming fdata, on output. If init_label is given, add an initial jump to it before the generated code. If fin_label is given, add it after the generated code. If fin_div0 is given equal to true, add the code for returning an error when dividing by 0, at the very end. """ # compute size for the local stack - do not forget to align by 16 fo = fdata.get_offset() # allocate enough memory for stack # Room for S_i (except S_0 which is fp) and T_i backup fo += len(S[1:]) + len(T) cardoffset = 8 * (fo + (0 if fo % 2 == 0 else 1)) + 16 output.write( "##Automatically generated RISCV code, MIF08 & CAP\n") if comment is not None: output.write("##{} version\n".format(comment)) output.write("\n\n##prelude\n") # We put an li t0, cardoffset in case it is greater than 2**11 # We use t0 because it is caller-saved output.write(""" .text .globl {0} {0}: li t0, {1} sub sp, sp, t0 sd ra, 0(sp) sd fp, 8(sp) add fp, sp, t0 """.format(fdata.get_name(), cardoffset)) # Stack in RiscV is managed with SP if init_label is not None: # Add a jump to init_label before the generated code. output.write(""" j {0} """.format(init_label)) output.write("\n\n##Generated Code\n") # Generated code for i in listIns: i.printIns(output) output.write("\n\n##postlude\n") if fin_label is not None: # Add fin_label after the generated code. output.write(""" {0}: """.format(fin_label)) # We put an li t0, cardoffset in case it is greater than 2**11 # We use t0 because it is caller-saved output.write(""" ld ra, 0(sp) ld fp, 8(sp) li t0, {0} add sp, sp, t0 ret """.format(cardoffset)) if fin_div0: # Add code for division by 0 at the end. output.write(""" {0}: la a0, {0}_msg call println_string li a0, 1 call exit """.format(fdata._label_div_by_zero)) # Add the data for the message of the division by 0 output.write(""" {0}_msg: .string "Division by 0" """.format(fdata._label_div_by_zero))