CAP/MiniC/Lib/Terminator.py

154 lines
5.1 KiB
Python
Raw Permalink Normal View History

2024-10-06 19:58:11 +02:00
"""
MIF08, CAP, CFG library - Terminators.
Each :py:class:`block <Lib.CFG.Block>` of a :py:class:`CFG <Lib.CFG.CFG>`
ends with a branching instruction called a terminator.
There are three kinds of terminators:
- :py:class:`Lib.Statement.AbsoluteJump` is a non-conditional jump
to another block of the CFG
- :py:class:`BranchingTerminator` is a conditional branching
instruction with two successor blocks.
Unlike the class :py:class:`ConditionalJump <Lib.Statement.ConditionalJump>`
that was used in :py:class:`LinearCode <Lib.LinearCode.LinearCode>`,
both successor labels have to be specified.
- :py:class:`Return` marks the end of the function
During the construction of the CFG, :py:func:`jump2terminator` builds
a terminator for each extracted chunk of instructions.
"""
from dataclasses import dataclass
from typing import List, Dict
from Lib.Errors import MiniCInternalError
from Lib.Operands import Operand, Renamer, Temporary, Condition
from Lib.Statement import AbsoluteJump, ConditionalJump, Instruction, Label, Statement
@dataclass(unsafe_hash=True)
class Return(Statement):
"""A terminator that marks the end of the function."""
def __str__(self):
return ("return")
def printIns(self, stream):
print("return", file=stream)
def targets(self) -> List[Label]:
"""Return the labels targetted by the Return terminator."""
return []
def args(self) -> List[Operand]:
return []
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 != []:
raise Exception(
"substitute: No possible substitution on instruction {}"
.format(self))
return self
def is_read_only(self) -> bool:
return True
@dataclass(init=False)
class BranchingTerminator(Instruction):
"""A terminating statement with a condition."""
#: The condition of the branch
cond: Condition
#: The destination label if the condition is true
label_then: Label
#: The destination label if the condition is false
label_else: Label
#: The first operand of the condition
op1: Operand
#: The second operand of the condition
op2: Operand
_read_only = True
def __init__(self, cond: Condition, op1: Operand, op2: Operand,
label_then: Label, label_else: Label):
self.cond = cond
self.label_then = label_then
self.label_else = label_else
self.op1 = op1
self.op2 = op2
self.ins = str(self.cond)
def args(self) -> List[Operand]:
return [self.op1, self.op2, self.label_then, self.label_else]
def targets(self) -> List[Label]:
"""Return the labels targetted by the Branching terminator."""
return [self.label_then, self.label_else]
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 BranchingTerminator(self.cond, op1, op2, self.label_then, self.label_else)
def with_args(self, new_args: List[Operand]):
if len(new_args) != 4:
raise Exception(
"substitute: Invalid number of arguments for instruction {}, expected 4 got {}"
.format(self, new_args))
op1 = new_args[0]
op2 = new_args[1]
return BranchingTerminator(self.cond, op1, op2, self.label_then, self.label_else)
def __hash__(self):
return hash(super)
Terminator = Return | AbsoluteJump | BranchingTerminator
"""Type alias for terminators"""
def jump2terminator(j: ConditionalJump | AbsoluteJump | None,
next_label: Label | None) -> Terminator:
"""
Construct the Terminator associated to the potential jump j
to the potential label next_label.
"""
match j:
case ConditionalJump():
if (next_label is None):
raise MiniCInternalError(
"jump2terminator: Missing secondary label for instruction {}"
.format(j))
label_else = next_label
return BranchingTerminator(j.cond, j.op1, j.op2, j.label, label_else)
case AbsoluteJump():
return AbsoluteJump(label=j.label)
case _:
if next_label:
return AbsoluteJump(next_label)
else:
return Return()