154 lines
5.1 KiB
Python
154 lines
5.1 KiB
Python
"""
|
|
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()
|