""" MIF08, CAP, CFG library - Terminators. Each :py:class:`block ` of a :py:class:`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 ` that was used in :py:class:`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()