CAP/MiniC/Lib/Statement.py
2024-10-06 19:58:11 +02:00

279 lines
8.4 KiB
Python

"""
The base class for RISCV ASM statements is :py:class:`Statement`.
It is inherited by :py:class:`Comment`, :py:class:`Label`
and :py:class:`Instruction`. In turn, :py:class:`Instruction`
is inherited by :py:class:`Instru3A`
(for regular non-branching 3-address instructions),
:py:class:`AbsoluteJump` and :py:class:`ConditionalJump`.
"""
from dataclasses import dataclass
from typing import (List, Dict, TypeVar)
from Lib.Operands import (Operand, Renamer, Temporary, Condition)
from Lib.Errors import MiniCInternalError
def regset_to_string(registerset) -> str:
"""Utility function: pretty-prints a set of locations."""
return "{" + ",".join(str(x) for x in registerset) + "}"
# Temporary until we can use Typing.Self in python 3.11
TStatement = TypeVar("TStatement", bound="Statement")
@dataclass(unsafe_hash=True)
class Statement:
"""A Statement, which is an instruction, a comment or a label."""
def defined(self) -> List[Operand]:
"""Operands defined (written) in this instruction"""
return []
def used(self) -> List[Operand]:
"""Operands used (read) in this instruction"""
return []
def substitute(self: TStatement, subst: Dict[Operand, Operand]) -> TStatement:
"""Return a new instruction, cloned from this one, replacing operands
that appear as key in subst by their value."""
raise Exception(
"substitute: Operands {} are not present in instruction {}"
.format(subst, self))
def with_args(self: TStatement, new_args: List[Operand]) -> TStatement:
"""Return a new instruction, cloned from this one, where operands have
been replaced by new_args."""
raise Exception(
"substitute: Operands {} are not present in instruction {}"
.format(new_args, self))
def printIns(self, stream):
"""
Print the statement on the given output.
Should never be called on the base class.
"""
raise NotImplementedError
@dataclass(unsafe_hash=True)
class Comment(Statement):
"""A comment."""
comment: str
def __str__(self): # use only for print_dot !
return "# {}".format(self.comment)
def printIns(self, stream):
print(' # ' + self.comment, file=stream)
@dataclass(unsafe_hash=True)
class Label(Statement, Operand):
"""A label is both a Statement and an Operand."""
name: str
def __str__(self):
return ("lbl_{}".format(self.name))
def __repr__(self):
return ("{}".format(self.name))
def printIns(self, stream):
print(str(self) + ':', file=stream)
@dataclass(init=False)
class Instruction(Statement):
ins: str
_read_only: bool
def is_read_only(self):
"""
True if the instruction only reads from its operands.
Otherwise, the first operand is considered as the destination
and others are source.
"""
return self._read_only
def rename(self, renamer: Renamer) -> None:
raise NotImplementedError
def args(self) -> List[Operand]:
"""List of operands the instruction takes"""
raise NotImplementedError
def defined(self):
if self.is_read_only():
defs = []
else:
defs = [self.args()[0]]
return defs
def used(self) -> List[Operand]:
if self.is_read_only():
uses = self.args()
else:
uses = self.args()[1:]
return uses
def __str__(self):
s = self.ins
first = True
for arg in self.args():
if first:
s += ' ' + str(arg)
first = False
else:
s += ', ' + str(arg)
return s
def __hash__(self):
return hash((self.ins, *self.args()))
def printIns(self, stream):
"""Print the instruction on the given output."""
print(' ', str(self), file=stream)
@dataclass(init=False)
class Instru3A(Instruction):
_args: List[Operand]
def __init__(self, ins, *args: Operand):
# convention is to use lower-case in RISCV
self.ins = ins.lower()
self._args = list(args)
self._read_only = (self.ins == "call"
or self.ins == "ld"
or self.ins == "lw"
or self.ins == "lb")
if (self.ins.startswith("b") or self.ins == "j"):
raise MiniCInternalError
def args(self):
return self._args
def rename(self, renamer: Renamer):
old_replaced = dict()
for i, arg in enumerate(self._args):
if isinstance(arg, Temporary):
if i == 0 and not self.is_read_only():
old_replaced[arg] = renamer.replace(arg)
new_t = renamer.fresh(arg)
elif arg in old_replaced.keys():
new_t = old_replaced[arg]
else:
new_t = renamer.replace(arg)
self._args[i] = new_t
def substitute(self, subst: Dict[Operand, Operand]):
for op in subst:
if op not in self.args():
raise Exception(
"substitute: Operand {} is not present in instruction {}"
.format(op, self))
args = [subst.get(arg, arg)
if isinstance(arg, Temporary) else arg
for arg in self.args()]
return Instru3A(self.ins, *args)
def with_args(self, new_args: List[Operand]):
if len(new_args) != len(self._args):
raise Exception(
"substitute: Expected {} operands for {}, got {}."
.format(len(self._args), self, new_args))
return Instru3A(self.ins, *new_args)
def __hash__(self):
return hash(super)
@dataclass(init=False)
class AbsoluteJump(Instruction):
""" An Absolute Jump is a specific kind of instruction"""
ins = "j"
label: Label
_read_only = True
def __init__(self, label: Label):
self.label = label
def args(self) -> List[Operand]:
return [self.label]
def rename(self, renamer: Renamer):
pass
def substitute(self, subst: Dict[Operand, Operand]):
if subst != {}:
raise Exception(
"substitute: No possible substitution on instruction {}"
.format(self))
return self
def with_args(self, new_args: List[Operand]):
if new_args != self.args():
raise Exception(
"substitute: No possible substitution on instruction {}. Old args={}, new args={}"
.format(self, self.args(), new_args))
return self
def __hash__(self):
return hash(super)
def targets(self) -> List[Label]:
"""Return the labels targetted by the AbsoluteJump."""
return [self.label]
@dataclass(init=False)
class ConditionalJump(Instruction):
""" A Conditional Jump is a specific kind of instruction"""
cond: Condition
label: Label
op1: Operand
op2: Operand
_read_only = True
def __init__(self, cond: Condition, op1: Operand, op2: Operand, label: Label):
self.cond = cond
self.label = label
self.op1 = op1
self.op2 = op2
self.ins = str(self.cond)
def args(self):
return [self.op1, self.op2, self.label]
def rename(self, renamer: Renamer):
if isinstance(self.op1, Temporary):
self.op1 = renamer.replace(self.op1)
if isinstance(self.op2, Temporary):
self.op2 = renamer.replace(self.op2)
def substitute(self, subst: Dict[Operand, Operand]):
for op in subst:
if op not in self.args():
raise Exception(
"substitute: Operand {} is not present in instruction {}"
.format(op, self))
op1 = subst.get(self.op1, self.op1) if isinstance(self.op1, Temporary) \
else self.op1
op2 = subst.get(self.op2, self.op2) if isinstance(self.op2, Temporary) \
else self.op2
return ConditionalJump(self.cond, op1, op2, self.label)
def with_args(self, new_args: List[Operand]):
if len(new_args) != 3:
raise Exception(
"substitute: Expected 3 operands for {}, got {}."
.format(self, new_args))
assert isinstance(new_args[2], Label)
label: Label = new_args[2]
return ConditionalJump(self.cond, new_args[0], new_args[1], label)
def __hash__(self):
return hash(super)