Merge branch 'main' of https://github.com/Drup/cap-lab24.git
This commit is contained in:
commit
d8d7c45ad2
79
MiniC/Lib/Allocator.py
Normal file
79
MiniC/Lib/Allocator.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"""
|
||||||
|
This file defines the base class :py:class:`Allocator`
|
||||||
|
and the naïve implementation :py:class:`NaiveAllocator`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from Lib.Operands import Temporary, Operand, DataLocation, GP_REGS
|
||||||
|
from Lib.Statement import Instruction
|
||||||
|
from Lib.Errors import AllocationError
|
||||||
|
from Lib.FunctionData import FunctionData
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
|
||||||
|
class Allocator():
|
||||||
|
"""General base class for Naive, AllInMem and Smart Allocators.
|
||||||
|
Replace all temporaries in the code with actual data locations.
|
||||||
|
|
||||||
|
Allocation is done in two steps:
|
||||||
|
|
||||||
|
- First, :py:meth:`prepare` is responsible for calling
|
||||||
|
:py:meth:`Lib.Operands.TemporaryPool.set_temp_allocation`
|
||||||
|
with a mapping from temporaries to where they should actually be stored
|
||||||
|
(in registers or in memory).
|
||||||
|
- Then, :py:meth:`replace` is called for each instruction in order to
|
||||||
|
replace the temporary operands with the previously assigned locations
|
||||||
|
(and possibly add some instructions before or after).
|
||||||
|
Concretely, it returns a list of instructions that should replace the original
|
||||||
|
instruction. The actual iteration over all the instructions is handled transparently
|
||||||
|
by :py:meth:`Lib.LinearCode.LinearCode.iter_statements`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_fdata: FunctionData
|
||||||
|
|
||||||
|
def __init__(self, fdata: FunctionData):
|
||||||
|
self._fdata = fdata
|
||||||
|
|
||||||
|
def prepare(self) -> None: # pragma: no cover
|
||||||
|
pass
|
||||||
|
|
||||||
|
def replace(self, old_instr: Instruction) -> List[Instruction]:
|
||||||
|
"""Transform an instruction with temporaries into a list of instructions."""
|
||||||
|
return [old_instr]
|
||||||
|
|
||||||
|
def rewriteCode(self, listcode) -> None:
|
||||||
|
"""Modify the code to replace temporaries with
|
||||||
|
registers or memory locations.
|
||||||
|
"""
|
||||||
|
listcode.iter_statements(self.replace)
|
||||||
|
|
||||||
|
|
||||||
|
class NaiveAllocator(Allocator):
|
||||||
|
"""Naive Allocator: try to assign a register to each temporary,
|
||||||
|
fails if there are more temporaries than registers.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def replace(self, old_instr: Instruction) -> List[Instruction]:
|
||||||
|
"""Replace Temporary operands with the corresponding allocated Register."""
|
||||||
|
new_args: List[Operand] = []
|
||||||
|
for arg in old_instr.args():
|
||||||
|
if isinstance(arg, Temporary):
|
||||||
|
new_args.append(arg.get_alloced_loc())
|
||||||
|
else:
|
||||||
|
new_args.append(arg)
|
||||||
|
new_instr = old_instr.with_args(new_args)
|
||||||
|
return [new_instr]
|
||||||
|
|
||||||
|
def prepare(self) -> None:
|
||||||
|
"""Allocate all temporaries to registers.
|
||||||
|
Fail if there are too many temporaries."""
|
||||||
|
regs = list(GP_REGS) # Get a writable copy
|
||||||
|
temp_allocation: Dict[Temporary, DataLocation] = dict()
|
||||||
|
for tmp in self._fdata._pool.get_all_temps():
|
||||||
|
try:
|
||||||
|
reg = regs.pop()
|
||||||
|
except IndexError:
|
||||||
|
raise AllocationError(
|
||||||
|
"Too many temporaries ({}) for the naive allocation, sorry."
|
||||||
|
.format(len(self._fdata._pool.get_all_temps())))
|
||||||
|
temp_allocation[tmp] = reg
|
||||||
|
self._fdata._pool.set_temp_allocation(temp_allocation)
|
295
MiniC/Lib/CFG.py
Normal file
295
MiniC/Lib/CFG.py
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
"""
|
||||||
|
Classes for a RiscV CFG: :py:class:`CFG` for the CFG itself,
|
||||||
|
and :py:class:`Block` for its basic blocks.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from graphviz import Digraph # for dot output
|
||||||
|
from typing import cast, Any, Dict, List, Set, Iterator
|
||||||
|
|
||||||
|
from Lib.Errors import MiniCInternalError
|
||||||
|
from Lib.Operands import (Operand, Immediate, Function, A0)
|
||||||
|
from Lib.Statement import (
|
||||||
|
Statement, Instru3A, Label,
|
||||||
|
AbsoluteJump, ConditionalJump, Comment
|
||||||
|
)
|
||||||
|
from Lib.Terminator import (
|
||||||
|
Terminator, BranchingTerminator, Return)
|
||||||
|
from Lib.FunctionData import (FunctionData, _iter_statements, _print_code)
|
||||||
|
|
||||||
|
|
||||||
|
BlockInstr = Instru3A | Comment
|
||||||
|
|
||||||
|
|
||||||
|
class Block:
|
||||||
|
"""
|
||||||
|
A basic block of a :py:class:`CFG` is made of three main parts:
|
||||||
|
|
||||||
|
- a start :py:class:`label <Lib.Statement.Label>` that uniquely identifies the block in the CFG
|
||||||
|
- the main body of the block, a list of instructions
|
||||||
|
(excluding labels, jumps and branching instructions)
|
||||||
|
- a :py:class:`terminator <Lib.Terminator.Terminator>`
|
||||||
|
that represents the final jump or branching instruction of the block,
|
||||||
|
and points to the successors of the block.
|
||||||
|
See the documentation for :py:class:`Lib.Terminator.Terminator` for further explanations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_terminator: Terminator
|
||||||
|
_label: Label
|
||||||
|
_phis: List[Statement]
|
||||||
|
_instructions: List[BlockInstr]
|
||||||
|
_in: List['Block']
|
||||||
|
_gen: Set
|
||||||
|
_kill: Set
|
||||||
|
|
||||||
|
def __init__(self, label: Label, insts: List[BlockInstr], terminator: Terminator):
|
||||||
|
self._label = label
|
||||||
|
self._instructions = insts
|
||||||
|
self._in = []
|
||||||
|
self._phis = []
|
||||||
|
self._terminator = terminator
|
||||||
|
self._gen = set()
|
||||||
|
self._kill = set()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
instr = [i for i in self._instructions if not isinstance(i, Comment)]
|
||||||
|
instr_str = '\n'.join(map(str, instr))
|
||||||
|
s = '{}:\n\n{}'.format(self._label, instr_str)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def to_dot(self) -> str: # pragma: no cover
|
||||||
|
"""Outputs all statements of the block as a string."""
|
||||||
|
# dot is weird: lines ending with \l instead of \n are left-aligned.
|
||||||
|
NEWLINE = '\\l '
|
||||||
|
instr = []
|
||||||
|
instr += self._phis
|
||||||
|
instr += [i for i in self._instructions if not isinstance(i, Comment)]
|
||||||
|
instr += [self.get_terminator()]
|
||||||
|
instr_str = NEWLINE.join(map(str, instr))
|
||||||
|
s = '{}:{}{}\\l'.format(self._label, NEWLINE, instr_str)
|
||||||
|
return s
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self._label)
|
||||||
|
|
||||||
|
def get_body(self) -> List[BlockInstr]:
|
||||||
|
"""Return the statements in the body of the block (no phi-node nor the terminator)."""
|
||||||
|
return self._instructions
|
||||||
|
|
||||||
|
def get_all_statements(self) -> List[Statement]:
|
||||||
|
"""
|
||||||
|
Return all statements of the block
|
||||||
|
(including phi-nodes and the terminator, but not the label of the block).
|
||||||
|
"""
|
||||||
|
return (self._phis +
|
||||||
|
cast(List[Statement], self._instructions) +
|
||||||
|
[self.get_terminator()])
|
||||||
|
|
||||||
|
def get_body_and_terminator(self) -> List[Statement]:
|
||||||
|
"""
|
||||||
|
Return all statements of the block, except phi-nodes
|
||||||
|
(and the label of the block).
|
||||||
|
"""
|
||||||
|
return (cast(List[Statement], self._instructions) +
|
||||||
|
[self.get_terminator()])
|
||||||
|
|
||||||
|
def get_label(self) -> Label:
|
||||||
|
"""Return the label of the block."""
|
||||||
|
return self._label
|
||||||
|
|
||||||
|
def get_in(self) -> List['Block']:
|
||||||
|
"""Return the list of blocks with an edge to the considered block."""
|
||||||
|
return self._in
|
||||||
|
|
||||||
|
def get_terminator(self) -> Terminator:
|
||||||
|
"""Return the terminator of the block."""
|
||||||
|
return self._terminator
|
||||||
|
|
||||||
|
def set_terminator(self, term: Terminator) -> None:
|
||||||
|
"""Set the terminator of the block."""
|
||||||
|
self._terminator = term
|
||||||
|
|
||||||
|
def get_phis(self) -> List[Statement]:
|
||||||
|
"""Return the list of all φ instructions of the block."""
|
||||||
|
return self._phis
|
||||||
|
|
||||||
|
def add_phi(self, phi: Statement) -> None:
|
||||||
|
"""Add a φ instruction to the block."""
|
||||||
|
self._phis.append(phi)
|
||||||
|
|
||||||
|
def set_phis(self, phis: List[Statement]) -> None:
|
||||||
|
"""Replace the φ instructions in the block by the given list `phis`."""
|
||||||
|
self._phis = phis
|
||||||
|
|
||||||
|
def remove_all_phis(self) -> None:
|
||||||
|
"""Remove all φ instructions in the block."""
|
||||||
|
self._phis = []
|
||||||
|
|
||||||
|
def iter_statements(self, f) -> None:
|
||||||
|
"""Iterate over instructions.
|
||||||
|
For each real instruction i (not label or comment), replace it
|
||||||
|
with the list of instructions given by f(i).
|
||||||
|
|
||||||
|
Assume there is no phi-node.
|
||||||
|
"""
|
||||||
|
assert (self._phis == [])
|
||||||
|
new_statements = _iter_statements(self._instructions, f)
|
||||||
|
end_statements = f(self.get_terminator())
|
||||||
|
if len(end_statements) >= 1 and isinstance(end_statements[-1], Terminator):
|
||||||
|
new_terminator = end_statements.pop(-1)
|
||||||
|
self._instructions = new_statements + end_statements
|
||||||
|
self.set_terminator(new_terminator)
|
||||||
|
else:
|
||||||
|
raise MiniCInternalError(
|
||||||
|
"Block.iter_statements: Invalid replacement for terminator {}:\n {}"
|
||||||
|
.format(self.get_terminator(), end_statements))
|
||||||
|
|
||||||
|
def add_instruction(self, instr: BlockInstr) -> None:
|
||||||
|
"""Add an instruction to the body of the block."""
|
||||||
|
self._instructions.append(instr)
|
||||||
|
|
||||||
|
|
||||||
|
class CFG:
|
||||||
|
"""
|
||||||
|
A complete control-flow graph representing a function.
|
||||||
|
This class is mainly made of a list of basic :py:class:`Block`,
|
||||||
|
a label indicating the :py:meth:`entry point of the function <get_start>`,
|
||||||
|
and an :py:meth:`exit label <get_end>`.
|
||||||
|
|
||||||
|
As with linear code, metadata about the function can be found
|
||||||
|
in the :py:attr:`fdata` member variable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_start: Label
|
||||||
|
_end: Label
|
||||||
|
_blocks: Dict[Label, Block]
|
||||||
|
|
||||||
|
#: Metadata about the function represented by this CFG
|
||||||
|
fdata: FunctionData
|
||||||
|
|
||||||
|
def __init__(self, fdata: FunctionData):
|
||||||
|
self._blocks = {}
|
||||||
|
self.fdata = fdata
|
||||||
|
self._init_blks()
|
||||||
|
self._end = self.fdata.fresh_label("end")
|
||||||
|
|
||||||
|
def _init_blks(self) -> None:
|
||||||
|
"""Add a block for division by 0."""
|
||||||
|
# Label for the address of the error message
|
||||||
|
# This address is added by print_code
|
||||||
|
label_div_by_zero_msg = Label(self.fdata._label_div_by_zero.name + "_msg")
|
||||||
|
blk = Block(self.fdata._label_div_by_zero, [
|
||||||
|
Instru3A("la", A0, label_div_by_zero_msg),
|
||||||
|
Instru3A("call", Function("println_string")),
|
||||||
|
Instru3A("li", A0, Immediate(1)),
|
||||||
|
Instru3A("call", Function("exit")),
|
||||||
|
], terminator=Return())
|
||||||
|
self.add_block(blk)
|
||||||
|
|
||||||
|
def get_start(self) -> Label:
|
||||||
|
"""Return the entry label of the CFG."""
|
||||||
|
return self._start
|
||||||
|
|
||||||
|
def set_start(self, start: Label) -> None:
|
||||||
|
"""Set the entry label of the CFG."""
|
||||||
|
assert (start in self._blocks)
|
||||||
|
self._start = start
|
||||||
|
|
||||||
|
def get_end(self) -> Label:
|
||||||
|
"""Return the exit label of the CFG."""
|
||||||
|
return self._end
|
||||||
|
|
||||||
|
def add_block(self, blk: Block) -> None:
|
||||||
|
"""Add a new block to the CFG."""
|
||||||
|
self._blocks[blk._label] = blk
|
||||||
|
|
||||||
|
def get_block(self, name: Label) -> Block:
|
||||||
|
"""Return the block with label `name`."""
|
||||||
|
return self._blocks[name]
|
||||||
|
|
||||||
|
def get_blocks(self) -> List[Block]:
|
||||||
|
"""Return all the blocks."""
|
||||||
|
return [b for b in self._blocks.values()]
|
||||||
|
|
||||||
|
def get_entries(self) -> List[Block]:
|
||||||
|
"""Return all the blocks with no predecessors."""
|
||||||
|
return [b for b in self._blocks.values() if not b.get_in()]
|
||||||
|
|
||||||
|
def add_edge(self, src: Block, dest: Block) -> None:
|
||||||
|
"""Add the edge src -> dest in the control flow graph."""
|
||||||
|
dest.get_in().append(src)
|
||||||
|
# assert (dest.get_label() in src.get_terminator().targets())
|
||||||
|
|
||||||
|
def remove_edge(self, src: Block, dest: Block) -> None:
|
||||||
|
"""Remove the edge src -> dest in the control flow graph."""
|
||||||
|
dest.get_in().remove(src)
|
||||||
|
# assert (dest.get_label() not in src.get_terminator().targets())
|
||||||
|
|
||||||
|
def out_blocks(self, block: Block) -> List[Block]:
|
||||||
|
"""
|
||||||
|
Return the list of blocks in the CFG targeted by
|
||||||
|
the Terminator of Block block.
|
||||||
|
"""
|
||||||
|
return [self.get_block(dest) for dest in block.get_terminator().targets()]
|
||||||
|
|
||||||
|
def gather_defs(self) -> Dict[Any, Set[Block]]:
|
||||||
|
"""
|
||||||
|
Return a dictionary associating variables to all the blocks
|
||||||
|
containing one of their definitions.
|
||||||
|
"""
|
||||||
|
defs: Dict[Operand, Set[Block]] = dict()
|
||||||
|
for b in self.get_blocks():
|
||||||
|
for i in b.get_all_statements():
|
||||||
|
for v in i.defined():
|
||||||
|
if v not in defs:
|
||||||
|
defs[v] = {b}
|
||||||
|
else:
|
||||||
|
defs[v].add(b)
|
||||||
|
return defs
|
||||||
|
|
||||||
|
def iter_statements(self, f) -> None:
|
||||||
|
"""Apply f to all instructions in all the blocks."""
|
||||||
|
for b in self.get_blocks():
|
||||||
|
b.iter_statements(f)
|
||||||
|
|
||||||
|
def linearize_naive(self) -> Iterator[Statement]:
|
||||||
|
"""
|
||||||
|
Linearize the given control flow graph as a list of instructions.
|
||||||
|
Naive procedure that adds jumps everywhere.
|
||||||
|
"""
|
||||||
|
for label, block in self._blocks.items():
|
||||||
|
yield label
|
||||||
|
for i in block._instructions:
|
||||||
|
yield i
|
||||||
|
match block.get_terminator():
|
||||||
|
case BranchingTerminator() as j:
|
||||||
|
# In case of conditional jump, add the missing edge
|
||||||
|
yield ConditionalJump(j.cond, j.op1, j.op2, j.label_then)
|
||||||
|
yield AbsoluteJump(j.label_else)
|
||||||
|
case AbsoluteJump() as j:
|
||||||
|
yield AbsoluteJump(j.label)
|
||||||
|
case Return():
|
||||||
|
yield AbsoluteJump(self.get_end())
|
||||||
|
|
||||||
|
def print_code(self, output, linearize=(lambda cfg: list(cfg.linearize_naive())),
|
||||||
|
comment=None) -> None:
|
||||||
|
"""Print the linearization of the CFG."""
|
||||||
|
statements = linearize(self)
|
||||||
|
_print_code(statements, self.fdata, output, init_label=self._start,
|
||||||
|
fin_label=self._end, fin_div0=False, comment=comment)
|
||||||
|
|
||||||
|
def print_dot(self, filename, DF=None, view=False) -> None: # pragma: no cover
|
||||||
|
"""Print the CFG as a graph."""
|
||||||
|
graph = Digraph()
|
||||||
|
# nodes
|
||||||
|
for name, blk in self._blocks.items():
|
||||||
|
if DF is not None:
|
||||||
|
df_str = "{}" if blk not in DF or not len(DF[blk]) else str(DF[blk])
|
||||||
|
df_lab = blk.to_dot() + "\n\nDominance frontier:\n" + df_str
|
||||||
|
else:
|
||||||
|
df_lab = blk.to_dot()
|
||||||
|
graph.node(str(blk._label), label=df_lab, shape='rectangle')
|
||||||
|
# edges
|
||||||
|
for name, blk in self._blocks.items():
|
||||||
|
for child in blk.get_terminator().targets():
|
||||||
|
graph.edge(str(blk._label), str(child))
|
||||||
|
graph.render(filename, view=view)
|
128
MiniC/Lib/Dominators.py
Normal file
128
MiniC/Lib/Dominators.py
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
"""
|
||||||
|
Utility functions to work with dominators in a :py:class:`CFG <Lib.CFG.CFG>`.
|
||||||
|
|
||||||
|
Do not hesitate to look at the source of the functions
|
||||||
|
to get a better understanding of the algorithms.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Dict, Set
|
||||||
|
from graphviz import Digraph
|
||||||
|
from Lib.CFG import Block, CFG
|
||||||
|
|
||||||
|
|
||||||
|
def computeDom(cfg: CFG) -> Dict[Block, Set[Block]]:
|
||||||
|
"""
|
||||||
|
`computeDom(cfg)` computes the table associating blocks to their
|
||||||
|
dominators in `cfg`.
|
||||||
|
It works by solving the equation system.
|
||||||
|
|
||||||
|
This is an helper function called during SSA entry.
|
||||||
|
"""
|
||||||
|
all_blocks: Set[Block] = set(cfg.get_blocks())
|
||||||
|
dominators: Dict[Block, Set[Block]] = dict()
|
||||||
|
for b in all_blocks:
|
||||||
|
if b.get_in(): # If b has some predecessor
|
||||||
|
dominators[b] = all_blocks
|
||||||
|
else: # If b has no predecessors
|
||||||
|
dominators[b] = {b}
|
||||||
|
new_dominators: Dict[Block, Set[Block]] = dict()
|
||||||
|
while True:
|
||||||
|
for b in all_blocks:
|
||||||
|
if b.get_in():
|
||||||
|
dom_preds = [dominators[b2] for b2 in b.get_in()]
|
||||||
|
new_dominators[b] = {b}.union(set.intersection(*dom_preds))
|
||||||
|
else:
|
||||||
|
new_dominators[b] = {b}
|
||||||
|
if dominators == new_dominators:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
dominators = new_dominators
|
||||||
|
new_dominators = dict()
|
||||||
|
return dominators
|
||||||
|
|
||||||
|
|
||||||
|
def printDT(filename: str, graph: Dict[Block, Set[Block]]) -> None: # pragma: no cover
|
||||||
|
"""Display a graphical rendering of the given domination tree."""
|
||||||
|
dot = Digraph()
|
||||||
|
for k in graph:
|
||||||
|
dot.node(str(k.get_label()))
|
||||||
|
for k in graph:
|
||||||
|
for v in graph[k]:
|
||||||
|
dot.edge(str(k.get_label()), str(v.get_label()))
|
||||||
|
dot.render(filename, view=True)
|
||||||
|
|
||||||
|
|
||||||
|
def computeDT(cfg: CFG, dominators: Dict[Block, Set[Block]],
|
||||||
|
dom_graphs: bool, basename: str) -> Dict[Block, Set[Block]]:
|
||||||
|
"""
|
||||||
|
`computeDT(cfg, dominators)` computes the domination tree of `cfg`
|
||||||
|
using the previously computed `dominators`.
|
||||||
|
It returns `DT`, a dictionary which associates a block with its children
|
||||||
|
in the dominator tree.
|
||||||
|
|
||||||
|
This is an helper function called during SSA entry.
|
||||||
|
"""
|
||||||
|
# First, compute the immediate dominators
|
||||||
|
idominators: Dict[Block, Block] = {}
|
||||||
|
for b, doms in dominators.items():
|
||||||
|
# The immediate dominator of b is the unique vertex n ≠ b
|
||||||
|
# which dominates b and is dominated by all vertices in Dom(b) − b.
|
||||||
|
strict_doms = doms - {b}
|
||||||
|
idoms = set()
|
||||||
|
for n in strict_doms:
|
||||||
|
if strict_doms.issubset(dominators[n]):
|
||||||
|
idoms.add(n)
|
||||||
|
if idoms:
|
||||||
|
assert (len(idoms) == 1)
|
||||||
|
idominators[b] = idoms.pop()
|
||||||
|
# Then, simply inverse the relation to obtain the domination tree
|
||||||
|
DT = {b: set() for b in cfg.get_blocks()}
|
||||||
|
for i, idominator in idominators.items():
|
||||||
|
DT[idominator].add(i)
|
||||||
|
# Print the domination tree if asked
|
||||||
|
if dom_graphs:
|
||||||
|
s = "{}.{}.ssa.DT.dot".format(basename, cfg.fdata.get_name())
|
||||||
|
print("SSA - domination tree graph:", s)
|
||||||
|
printDT(s, DT)
|
||||||
|
return DT
|
||||||
|
|
||||||
|
|
||||||
|
def _computeDF_at_block(
|
||||||
|
cfg: CFG,
|
||||||
|
dominators: Dict[Block, Set[Block]],
|
||||||
|
DT: Dict[Block, Set[Block]],
|
||||||
|
b: Block,
|
||||||
|
DF: Dict[Block, Set[Block]]) -> None:
|
||||||
|
"""
|
||||||
|
`_computeDF_at_block(...)` computes the dominance frontier at the given block,
|
||||||
|
by updating `DF`.
|
||||||
|
|
||||||
|
This is an helper function called during SSA entry.
|
||||||
|
"""
|
||||||
|
S: Set[Block] = {succ for succ in cfg.out_blocks(b) if succ not in DT[b]}
|
||||||
|
for b_succ in DT[b]:
|
||||||
|
_computeDF_at_block(cfg, dominators, DT, b_succ, DF)
|
||||||
|
for b_frontier in DF[b_succ]:
|
||||||
|
if b not in (dominators[b_frontier] - {b_frontier}):
|
||||||
|
S.add(b_frontier)
|
||||||
|
DF[b] = S
|
||||||
|
|
||||||
|
|
||||||
|
def computeDF(cfg: CFG, dominators: Dict[Block, Set[Block]],
|
||||||
|
DT: Dict[Block, Set[Block]], dom_graphs: bool, basename: str
|
||||||
|
) -> Dict[Block, Set[Block]]:
|
||||||
|
"""
|
||||||
|
`computeDF(...)` computes the dominance frontier of a CFG.
|
||||||
|
It returns `DF` which associates a block to its frontier.
|
||||||
|
|
||||||
|
This is an helper function called during SSA entry.
|
||||||
|
"""
|
||||||
|
DF: Dict[Block, Set[Block]] = dict()
|
||||||
|
for b_entry in cfg.get_entries():
|
||||||
|
_computeDF_at_block(cfg, dominators, DT, b_entry, DF)
|
||||||
|
# Print the domination frontier on the CFG if asked
|
||||||
|
if dom_graphs:
|
||||||
|
s = "{}.{}.ssa.DF.dot".format(basename, cfg.fdata.get_name())
|
||||||
|
print("SSA - dominance frontier graph:", s)
|
||||||
|
cfg.print_dot(s, DF, True)
|
||||||
|
return DF
|
181
MiniC/Lib/FunctionData.py
Normal file
181
MiniC/Lib/FunctionData.py
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
"""
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
def get_name(self) -> str:
|
||||||
|
"""Return the name of the function."""
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
def fresh_tmp(self) -> Temporary:
|
||||||
|
"""
|
||||||
|
Return a new fresh Temporary,
|
||||||
|
which is added to the pool.
|
||||||
|
"""
|
||||||
|
return self._pool.fresh_tmp()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
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))
|
312
MiniC/Lib/Graphes.py
Normal file
312
MiniC/Lib/Graphes.py
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
""" Python Classes for Oriented and Non Oriented Graphs
|
||||||
|
"""
|
||||||
|
|
||||||
|
from graphviz import Digraph # for dot output
|
||||||
|
from typing import List, Dict, Set, Tuple, Any
|
||||||
|
|
||||||
|
|
||||||
|
class GraphError(Exception):
|
||||||
|
"""Exception raised for self loops.
|
||||||
|
"""
|
||||||
|
|
||||||
|
message: str
|
||||||
|
|
||||||
|
def __init__(self, message: str):
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
|
||||||
|
class GeneralGraph(object):
|
||||||
|
"""
|
||||||
|
General class regrouping similarities
|
||||||
|
between directed and non oriented graphs.
|
||||||
|
The only differences between the two are:
|
||||||
|
|
||||||
|
- how to compute the set of edges
|
||||||
|
- how to add an edge
|
||||||
|
- how to print the graph
|
||||||
|
- how to delete a vertex
|
||||||
|
- how to delete an edge
|
||||||
|
- we only color undirected graphs
|
||||||
|
"""
|
||||||
|
|
||||||
|
graph_dict: Dict[Any, Set]
|
||||||
|
|
||||||
|
def __init__(self, graph_dict=None):
|
||||||
|
"""
|
||||||
|
Initializes a graph object.
|
||||||
|
If no dictionary or None is given,
|
||||||
|
an empty dictionary will be used.
|
||||||
|
"""
|
||||||
|
if graph_dict is None:
|
||||||
|
graph_dict = {}
|
||||||
|
self.graph_dict = graph_dict
|
||||||
|
|
||||||
|
def vertices(self) -> List[Any]:
|
||||||
|
"""Return the vertices of a graph."""
|
||||||
|
return list(self.graph_dict.keys())
|
||||||
|
|
||||||
|
def add_vertex(self, vertex: Any) -> None:
|
||||||
|
"""
|
||||||
|
If the vertex "vertex" is not in
|
||||||
|
self.graph_dict, a key "vertex" with an empty
|
||||||
|
list as a value is added to the dictionary.
|
||||||
|
Otherwise nothing has to be done.
|
||||||
|
"""
|
||||||
|
if vertex not in self.graph_dict:
|
||||||
|
self.graph_dict[vertex] = set()
|
||||||
|
|
||||||
|
def edges(self) -> List[Set]:
|
||||||
|
"""Return the edges of the graph."""
|
||||||
|
return []
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
res = "vertices: "
|
||||||
|
for k in self.graph_dict:
|
||||||
|
res += str(k) + " "
|
||||||
|
res += "\nedges: "
|
||||||
|
for edge in self.edges():
|
||||||
|
res += str(edge) + " "
|
||||||
|
return res
|
||||||
|
|
||||||
|
def dfs_traversal(self, root: Any) -> List[Any]:
|
||||||
|
"""
|
||||||
|
Compute a depth first search of the graph,
|
||||||
|
from the vertex root.
|
||||||
|
"""
|
||||||
|
seen: List[Any] = []
|
||||||
|
todo: List[Any] = [root]
|
||||||
|
while len(todo) > 0: # while todo ...
|
||||||
|
current = todo.pop()
|
||||||
|
seen.append(current)
|
||||||
|
for neighbour in self.graph_dict[current]:
|
||||||
|
if neighbour not in seen:
|
||||||
|
todo.append(neighbour)
|
||||||
|
return seen
|
||||||
|
|
||||||
|
def is_reachable_from(self, v1: Any, v2: Any) -> bool:
|
||||||
|
"""True if there is a path from v1 to v2."""
|
||||||
|
return v2 in self.dfs_traversal(v1)
|
||||||
|
|
||||||
|
def connected_components(self) -> List[List[Any]]:
|
||||||
|
"""
|
||||||
|
Compute the list of all connected components of the graph,
|
||||||
|
each component being a list of vetices.
|
||||||
|
"""
|
||||||
|
components: List[List[Any]] = []
|
||||||
|
done: List[Any] = []
|
||||||
|
for v in self.vertices():
|
||||||
|
if v not in done:
|
||||||
|
v_comp = self.dfs_traversal(v)
|
||||||
|
components.append(v_comp)
|
||||||
|
done.extend(v_comp)
|
||||||
|
return components
|
||||||
|
|
||||||
|
def bfs_traversal(self, root: Any) -> List[Any]:
|
||||||
|
"""
|
||||||
|
Compute a breadth first search of the graph,
|
||||||
|
from the vertex root.
|
||||||
|
"""
|
||||||
|
seen: List[Any] = []
|
||||||
|
todo: List[Any] = [root]
|
||||||
|
while len(todo) > 0: # while todo ...
|
||||||
|
current = todo.pop(0) # list.pop(0): for dequeuing (on the left...) !
|
||||||
|
seen.append(current)
|
||||||
|
for neighbour in self.graph_dict[current]:
|
||||||
|
if neighbour not in seen:
|
||||||
|
todo.append(neighbour)
|
||||||
|
return seen
|
||||||
|
|
||||||
|
|
||||||
|
class Graph(GeneralGraph):
|
||||||
|
"""Class for non oriented graphs."""
|
||||||
|
|
||||||
|
def edges(self) -> List[Set]:
|
||||||
|
"""
|
||||||
|
A static method generating the set of edges
|
||||||
|
(they appear twice in the dictionnary).
|
||||||
|
Return a list of sets.
|
||||||
|
"""
|
||||||
|
edges = []
|
||||||
|
for vertex in self.graph_dict:
|
||||||
|
for neighbour in self.graph_dict[vertex]:
|
||||||
|
if {neighbour, vertex} not in edges:
|
||||||
|
edges.append({vertex, neighbour})
|
||||||
|
return edges
|
||||||
|
|
||||||
|
def add_edge(self, edge: Tuple[Any, Any]) -> None:
|
||||||
|
"""
|
||||||
|
Add an edge in the graph.
|
||||||
|
edge should be a pair and not (c,c)
|
||||||
|
(we call g.add_edge((v1,v2)))
|
||||||
|
"""
|
||||||
|
(vertex1, vertex2) = edge
|
||||||
|
if vertex1 == vertex2:
|
||||||
|
raise GraphError("Cannot add a self loop on vertex {} in an unoriented graph.".format(
|
||||||
|
str(vertex1)))
|
||||||
|
if vertex1 in self.graph_dict:
|
||||||
|
self.graph_dict[vertex1].add(vertex2)
|
||||||
|
else:
|
||||||
|
self.graph_dict[vertex1] = {vertex2}
|
||||||
|
if vertex2 in self.graph_dict:
|
||||||
|
self.graph_dict[vertex2].add(vertex1)
|
||||||
|
else:
|
||||||
|
self.graph_dict[vertex2] = {vertex1}
|
||||||
|
|
||||||
|
def print_dot(self, name: str, colors={}) -> None:
|
||||||
|
"""Print the graph."""
|
||||||
|
color_names = ['red', 'blue', 'green', 'yellow', 'cyan', 'magenta'] + \
|
||||||
|
[f"grey{i}" for i in range(0, 100, 10)]
|
||||||
|
color_shapes = ['ellipse', 'box', 'diamond', 'trapezium', 'egg',
|
||||||
|
'parallelogram', 'house', 'triangle', 'pentagon', 'hexagon',
|
||||||
|
'septagon', 'octagon']
|
||||||
|
dot = Digraph(comment='Conflict Graph')
|
||||||
|
for k in self.graph_dict:
|
||||||
|
shape = None
|
||||||
|
if not colors:
|
||||||
|
color = "red" # Graph not colored: red for everyone
|
||||||
|
elif k not in colors:
|
||||||
|
color = "grey" # Node not colored: grey
|
||||||
|
else:
|
||||||
|
n = colors[k]
|
||||||
|
if n < len(color_names):
|
||||||
|
color = color_names[colors[k]]
|
||||||
|
else:
|
||||||
|
color = "black" # Too many colors anyway, it won't be readable.
|
||||||
|
shape = color_shapes[n % len(color_shapes)]
|
||||||
|
dot.node(str(k), color=color, shape=shape)
|
||||||
|
for (v1, v2) in self.edges():
|
||||||
|
dot.edge(str(v1), str(v2), dir="none")
|
||||||
|
# print(dot.source)
|
||||||
|
dot.render(name, view=True) # print in pdf
|
||||||
|
|
||||||
|
def delete_vertex(self, vertex: Any) -> None:
|
||||||
|
"""Delete a vertex and all the adjacent edges."""
|
||||||
|
gdict = self.graph_dict
|
||||||
|
for neighbour in gdict[vertex]:
|
||||||
|
gdict[neighbour].remove(vertex)
|
||||||
|
del gdict[vertex]
|
||||||
|
|
||||||
|
def delete_edge(self, edge: Tuple[Any, Any]):
|
||||||
|
"""Delete an edge."""
|
||||||
|
(v1, v2) = edge
|
||||||
|
self.graph_dict[v1].remove(v2)
|
||||||
|
self.graph_dict[v2].remove(v1)
|
||||||
|
|
||||||
|
def color(self) -> Dict[Any, int]:
|
||||||
|
"""
|
||||||
|
Color the graph with an unlimited number of colors.
|
||||||
|
Return a dict vertex -> color, where color is an integer (0, 1, ...).
|
||||||
|
"""
|
||||||
|
coloring, _, _ = self.color_with_k_colors()
|
||||||
|
return coloring
|
||||||
|
|
||||||
|
# see algo of the course
|
||||||
|
def color_with_k_colors(self, K=None, avoidingnodes=()) -> Tuple[Dict[Any, int], bool, List]:
|
||||||
|
"""
|
||||||
|
Color with <= K colors (if K is unspecified, use unlimited colors).
|
||||||
|
|
||||||
|
Return 3 values:
|
||||||
|
|
||||||
|
- a dict vertex -> color
|
||||||
|
- a Boolean, True if the coloring succeeded
|
||||||
|
- the set of nodes actually colored
|
||||||
|
|
||||||
|
Do not color vertices belonging to avoidingnodes.
|
||||||
|
|
||||||
|
Continue even if the algo fails.
|
||||||
|
"""
|
||||||
|
if K is None:
|
||||||
|
K = len(self.graph_dict)
|
||||||
|
todo_vertices = []
|
||||||
|
is_total = True
|
||||||
|
gcopy = Graph(self.graph_dict.copy())
|
||||||
|
# suppress nodes that are not to be considered.
|
||||||
|
for node in avoidingnodes:
|
||||||
|
gcopy.delete_vertex(node)
|
||||||
|
# append nodes in the list according to their degree and node number:
|
||||||
|
while gcopy.graph_dict:
|
||||||
|
todo = list(gcopy.graph_dict)
|
||||||
|
todo.sort(key=lambda v: (len(gcopy.graph_dict[v]), str(v)))
|
||||||
|
lower = todo[0]
|
||||||
|
todo_vertices.append(lower)
|
||||||
|
gcopy.delete_vertex(lower)
|
||||||
|
# Now reverse the list: first elements are those with higher degree
|
||||||
|
# print(todo_vertices)
|
||||||
|
todo_vertices.reverse() # in place reversal
|
||||||
|
# print(todo_vertices)
|
||||||
|
coloring = {}
|
||||||
|
colored_nodes = []
|
||||||
|
# gdict will be the coloring map to return
|
||||||
|
gdict = self.graph_dict
|
||||||
|
for v in todo_vertices:
|
||||||
|
seen_neighbours = [x for x in gdict[v] if x in coloring]
|
||||||
|
choose_among = [i for i in range(K) if not (
|
||||||
|
i in [coloring[v1] for v1 in seen_neighbours])]
|
||||||
|
if choose_among:
|
||||||
|
# if the node can be colored, I choose the minimal color.
|
||||||
|
color = min(choose_among)
|
||||||
|
coloring[v] = color
|
||||||
|
colored_nodes.append(v)
|
||||||
|
else:
|
||||||
|
# if I cannot color some node, the coloring is not Total
|
||||||
|
# but I continue
|
||||||
|
is_total = False
|
||||||
|
return (coloring, is_total, colored_nodes)
|
||||||
|
|
||||||
|
|
||||||
|
class DiGraph(GeneralGraph):
|
||||||
|
"""Class for directed graphs."""
|
||||||
|
|
||||||
|
def pred(self, v: Any) -> Set:
|
||||||
|
"""Return all predecessors of the vertex `v` in the graph."""
|
||||||
|
return {src for src, dests in self.graph_dict.items() if v in dests}
|
||||||
|
|
||||||
|
def neighbourhoods(self) -> List[Tuple[Any, Set]]:
|
||||||
|
"""Return all neighbourhoods in the graph."""
|
||||||
|
return list(self.graph_dict.items())
|
||||||
|
|
||||||
|
def edges(self) -> List[Set]:
|
||||||
|
""" A static method generating the set of edges"""
|
||||||
|
edges = []
|
||||||
|
for vertex in self.graph_dict:
|
||||||
|
for neighbour in self.graph_dict[vertex]:
|
||||||
|
edges.append((vertex, neighbour))
|
||||||
|
return edges
|
||||||
|
|
||||||
|
def add_edge(self, edge: Tuple[Any, Any]) -> None:
|
||||||
|
"""
|
||||||
|
Add an edge in the graph.
|
||||||
|
edge should be a pair and not (c,c)
|
||||||
|
(we call g.add_edge((v1,v2)))
|
||||||
|
"""
|
||||||
|
(vertex1, vertex2) = edge
|
||||||
|
if vertex1 in self.graph_dict:
|
||||||
|
self.graph_dict[vertex1].add(vertex2)
|
||||||
|
else:
|
||||||
|
self.graph_dict[vertex1] = {vertex2}
|
||||||
|
if vertex2 not in self.graph_dict:
|
||||||
|
self.graph_dict[vertex2] = set()
|
||||||
|
|
||||||
|
def print_dot(self, name: str) -> None:
|
||||||
|
"""Print the graph."""
|
||||||
|
dot = Digraph(comment='Conflict Graph')
|
||||||
|
for k in self.graph_dict:
|
||||||
|
shape = None
|
||||||
|
color = "grey"
|
||||||
|
dot.node(str(k), color=color, shape=shape)
|
||||||
|
for (v1, v2) in self.edges():
|
||||||
|
dot.edge(str(v1), str(v2), dir="none")
|
||||||
|
# print(dot.source)
|
||||||
|
dot.render(name, view=True) # print in pdf
|
||||||
|
|
||||||
|
def delete_vertex(self, vertex: Any) -> None:
|
||||||
|
"""Delete a vertex and all the adjacent edges."""
|
||||||
|
for node, neighbours in self.graph_dict.items():
|
||||||
|
if vertex in neighbours:
|
||||||
|
neighbours.remove(vertex)
|
||||||
|
del self.graph_dict[vertex]
|
||||||
|
|
||||||
|
def delete_edge(self, edge: Tuple[Any, Any]) -> None:
|
||||||
|
"""Delete an edge."""
|
||||||
|
(v1, v2) = edge
|
||||||
|
self.graph_dict[v1].remove(v2)
|
103
MiniC/Lib/LinearCode.py
Normal file
103
MiniC/Lib/LinearCode.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
"""
|
||||||
|
CAP, CodeGeneration, LinearCode API
|
||||||
|
Classes for a RiscV linear code.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from Lib.Operands import (A0, Function, DataLocation)
|
||||||
|
from Lib.Statement import (
|
||||||
|
Instru3A, AbsoluteJump, ConditionalJump, Comment, Label
|
||||||
|
)
|
||||||
|
from Lib.RiscV import (mv, call)
|
||||||
|
from Lib.FunctionData import (FunctionData, _iter_statements, _print_code)
|
||||||
|
|
||||||
|
|
||||||
|
CodeStatement = Comment | Label | Instru3A | AbsoluteJump | ConditionalJump
|
||||||
|
|
||||||
|
|
||||||
|
class LinearCode:
|
||||||
|
"""
|
||||||
|
Representation of a RiscV program as a list of instructions.
|
||||||
|
|
||||||
|
:py:meth:`add_instruction` is repeatedly called in the codegen visitor
|
||||||
|
to build a complete list of RiscV instructions for the source program.
|
||||||
|
|
||||||
|
The :py:attr:`fdata` member variable contains some meta-information
|
||||||
|
on the program, for instance to allocate a new temporary.
|
||||||
|
See :py:class:`Lib.FunctionData.FunctionData`.
|
||||||
|
|
||||||
|
For debugging purposes, :py:meth:`print_code` allows to print
|
||||||
|
the RiscV program to a file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
The :py:attr:`fdata` member variable contains some meta-information
|
||||||
|
on the program, for instance to allocate a new temporary.
|
||||||
|
See :py:class:`Lib.FunctionData.FunctionData`.
|
||||||
|
"""
|
||||||
|
fdata: FunctionData
|
||||||
|
|
||||||
|
_listIns: List[CodeStatement]
|
||||||
|
|
||||||
|
def __init__(self, name: str):
|
||||||
|
self._listIns = []
|
||||||
|
self.fdata = FunctionData(name)
|
||||||
|
|
||||||
|
def add_instruction(self, i: CodeStatement) -> None:
|
||||||
|
"""
|
||||||
|
Utility function to add an instruction in the program.
|
||||||
|
|
||||||
|
See also :py:mod:`Lib.RiscV` to generate relevant instructions.
|
||||||
|
"""
|
||||||
|
self._listIns.append(i)
|
||||||
|
|
||||||
|
def iter_statements(self, f) -> None:
|
||||||
|
"""Iterate over instructions.
|
||||||
|
For each real instruction i (not label or comment), replace it
|
||||||
|
with the list of instructions given by f(i).
|
||||||
|
"""
|
||||||
|
new_list_ins = _iter_statements(self._listIns, f)
|
||||||
|
# TODO: we shoudn't need to split this assignment with intermediate
|
||||||
|
# variable new_list_ins, but at least pyright pyright 1.1.293
|
||||||
|
# raises an error here if we don't.
|
||||||
|
self._listIns = new_list_ins
|
||||||
|
|
||||||
|
def get_instructions(self) -> List[CodeStatement]:
|
||||||
|
"""Return the list of instructions of the program."""
|
||||||
|
return self._listIns
|
||||||
|
|
||||||
|
# each instruction has its own "add in list" version
|
||||||
|
def add_label(self, s: Label) -> None:
|
||||||
|
"""Add a label in the program."""
|
||||||
|
return self.add_instruction(s)
|
||||||
|
|
||||||
|
def add_comment(self, s: str) -> None:
|
||||||
|
"""Add a comment in the program."""
|
||||||
|
self.add_instruction(Comment(s))
|
||||||
|
|
||||||
|
def add_instruction_PRINTLN_INT(self, reg: DataLocation) -> None:
|
||||||
|
"""Print integer value, with newline. (see Expand)"""
|
||||||
|
# A print instruction generates the temp it prints.
|
||||||
|
self.add_instruction(mv(A0, reg))
|
||||||
|
self.add_instruction(call(Function('println_int')))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '\n'.join(map(str, self._listIns))
|
||||||
|
|
||||||
|
def print_code(self, output, comment=None) -> None:
|
||||||
|
"""Outputs the RiscV program as text to a file at the given path."""
|
||||||
|
_print_code(self._listIns, self.fdata, output, init_label=None,
|
||||||
|
fin_label=None, fin_div0=True, comment=comment)
|
||||||
|
|
||||||
|
def print_dot(self, filename: str, DF=None, view=False) -> None: # pragma: no cover
|
||||||
|
"""Outputs the RiscV program as graph to a file at the given path."""
|
||||||
|
# import graphviz here so that students who don't have it can still work on lab4
|
||||||
|
from graphviz import Digraph
|
||||||
|
graph = Digraph()
|
||||||
|
# nodes
|
||||||
|
content = ""
|
||||||
|
for i in self._listIns:
|
||||||
|
content += str(i) + "\\l"
|
||||||
|
graph.node("Code", label=content, shape='rectangle')
|
||||||
|
# no edges
|
||||||
|
graph.render(filename, view=view)
|
279
MiniC/Lib/Operands.py
Normal file
279
MiniC/Lib/Operands.py
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
"""
|
||||||
|
This file defines the base class :py:class:`Operand`
|
||||||
|
and its subclasses for different operands: :py:class:`Condition`,
|
||||||
|
:py:class:`DataLocation` and :py:class:`Function`.
|
||||||
|
|
||||||
|
The class :py:class:`DataLocation` itself has subclasses:
|
||||||
|
:py:class:`Register`, :py:class:`Offset` for address in memory,
|
||||||
|
:py:class:`Immediate` for constants and :py:class:`Temporary`
|
||||||
|
for location not yet allocated.
|
||||||
|
|
||||||
|
This file also define shortcuts for registers in RISCV.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Dict, List
|
||||||
|
from MiniCParser import MiniCParser
|
||||||
|
from Lib.Errors import MiniCInternalError
|
||||||
|
|
||||||
|
|
||||||
|
class Operand():
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# signed version for riscv
|
||||||
|
all_ops = ['blt', 'bgt', 'beq', 'bne', 'ble', 'bge', 'beqz', 'bnez']
|
||||||
|
opdict = {MiniCParser.LT: 'blt', MiniCParser.GT: 'bgt',
|
||||||
|
MiniCParser.LTEQ: 'ble', MiniCParser.GTEQ: 'bge',
|
||||||
|
MiniCParser.NEQ: 'bne', MiniCParser.EQ: 'beq'}
|
||||||
|
opnot_dict = {'bgt': 'ble',
|
||||||
|
'bge': 'blt',
|
||||||
|
'blt': 'bge',
|
||||||
|
'ble': 'bgt',
|
||||||
|
'beq': 'bne',
|
||||||
|
'bne': 'beq',
|
||||||
|
'beqz': 'bnez',
|
||||||
|
'bnez': 'beqz'}
|
||||||
|
|
||||||
|
|
||||||
|
class Condition(Operand):
|
||||||
|
"""Condition, i.e. comparison operand for a CondJump.
|
||||||
|
|
||||||
|
Example usage :
|
||||||
|
|
||||||
|
- Condition('beq') = branch if equal.
|
||||||
|
- Condition(MiniCParser.LT) = branch if lower than.
|
||||||
|
- ...
|
||||||
|
|
||||||
|
The constructor's argument shall be a string in the list all_ops, or a
|
||||||
|
comparison operator in MiniCParser.LT, MiniCParser.GT, ... (one of the keys
|
||||||
|
in opdict).
|
||||||
|
|
||||||
|
A 'negate' method allows getting the negation of this condition.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_op: str
|
||||||
|
|
||||||
|
def __init__(self, optype):
|
||||||
|
if optype in opdict:
|
||||||
|
self._op = opdict[optype]
|
||||||
|
elif str(optype) in all_ops:
|
||||||
|
self._op = str(optype)
|
||||||
|
else:
|
||||||
|
raise MiniCInternalError(f"Unsupported comparison operator {optype}")
|
||||||
|
|
||||||
|
def negate(self) -> 'Condition':
|
||||||
|
"""Return the opposite condition."""
|
||||||
|
return Condition(opnot_dict[self._op])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self._op
|
||||||
|
|
||||||
|
|
||||||
|
class Function(Operand):
|
||||||
|
"""Operand for build-in function call."""
|
||||||
|
|
||||||
|
_name: str
|
||||||
|
|
||||||
|
def __init__(self, name: str):
|
||||||
|
self._name = name
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
|
|
||||||
|
class DataLocation(Operand):
|
||||||
|
""" A Data Location is either a register, a temporary
|
||||||
|
or a place in memory (offset).
|
||||||
|
"""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# map for register shortcuts
|
||||||
|
reg_map = dict([(0, 'zero'), (1, 'ra'), (2, 'sp')] + # no (3, 'gp') nor (4, 'tp')
|
||||||
|
[(i+5, 't'+str(i)) for i in range(3)] +
|
||||||
|
[(8, 'fp'), (9, 's1')] +
|
||||||
|
[(i+10, 'a'+str(i)) for i in range(8)] +
|
||||||
|
[(i+18, 's'+str(i+2)) for i in range(10)] +
|
||||||
|
[(i+28, 't'+str(i+3)) for i in range(4)])
|
||||||
|
|
||||||
|
|
||||||
|
class Register(DataLocation):
|
||||||
|
""" A (physical) register."""
|
||||||
|
|
||||||
|
_number: int
|
||||||
|
|
||||||
|
def __init__(self, number: int):
|
||||||
|
self._number = number
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
if self._number not in reg_map:
|
||||||
|
raise MiniCInternalError(f"Register number {self._number} should not be used")
|
||||||
|
else:
|
||||||
|
return ("{}".format(reg_map[self._number]))
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return isinstance(other, Register) and self._number == other._number
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return self._number
|
||||||
|
|
||||||
|
|
||||||
|
# Shortcuts for registers in RISCV
|
||||||
|
# Only integer registers
|
||||||
|
|
||||||
|
#: Zero register
|
||||||
|
ZERO = Register(0)
|
||||||
|
#:
|
||||||
|
RA = Register(1)
|
||||||
|
#:
|
||||||
|
SP = Register(2)
|
||||||
|
#: Register not used for this course
|
||||||
|
GP = Register(3)
|
||||||
|
#: Register not used for this course
|
||||||
|
TP = Register(4)
|
||||||
|
#:
|
||||||
|
A = tuple(Register(i + 10) for i in range(8))
|
||||||
|
#:
|
||||||
|
S = tuple(Register(i + 8) for i in range(2)) + tuple(Register(i + 18) for i in range(10))
|
||||||
|
#:
|
||||||
|
T = tuple(Register(i + 5) for i in range(3)) + tuple(Register(i + 28) for i in range(4))
|
||||||
|
|
||||||
|
|
||||||
|
#:
|
||||||
|
A0 = A[0] # function args/return Values: A0, A1
|
||||||
|
#:
|
||||||
|
A1 = A[1]
|
||||||
|
#: Frame Pointer = Saved register 0
|
||||||
|
FP = S[0]
|
||||||
|
|
||||||
|
#: General purpose registers, usable for the allocator
|
||||||
|
GP_REGS = S[4:] + T # s0, s1, s2 and s3 are special
|
||||||
|
|
||||||
|
|
||||||
|
class Offset(DataLocation):
|
||||||
|
""" Offset = address in memory computed with base + offset."""
|
||||||
|
|
||||||
|
_basereg: Register
|
||||||
|
_offset: int
|
||||||
|
|
||||||
|
def __init__(self, basereg: Register, offset: int):
|
||||||
|
self._basereg = basereg
|
||||||
|
self._offset = offset
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ("{}({})".format(self._offset, self._basereg))
|
||||||
|
|
||||||
|
def get_offset(self) -> int:
|
||||||
|
"""Return the value of the offset."""
|
||||||
|
return self._offset
|
||||||
|
|
||||||
|
|
||||||
|
class Immediate(DataLocation):
|
||||||
|
"""Immediate operand (integer)."""
|
||||||
|
|
||||||
|
_val: int
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
self._val = val
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self._val)
|
||||||
|
|
||||||
|
|
||||||
|
class Temporary(DataLocation):
|
||||||
|
"""Temporary, a location that has not been allocated yet.
|
||||||
|
It will later be mapped to a physical register (Register) or to a memory location (Offset).
|
||||||
|
"""
|
||||||
|
|
||||||
|
_number: int
|
||||||
|
_pool: 'TemporaryPool'
|
||||||
|
|
||||||
|
def __init__(self, number: int, pool: 'TemporaryPool'):
|
||||||
|
self._number = number
|
||||||
|
self._pool = pool
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return ("temp_{}".format(str(self._number)))
|
||||||
|
|
||||||
|
def get_alloced_loc(self) -> DataLocation:
|
||||||
|
"""Return the DataLocation allocated to this Temporary."""
|
||||||
|
return self._pool.get_alloced_loc(self)
|
||||||
|
|
||||||
|
|
||||||
|
class TemporaryPool:
|
||||||
|
"""Manage a pool of temporaries."""
|
||||||
|
|
||||||
|
_all_temps: List[Temporary]
|
||||||
|
_current_num: int
|
||||||
|
_allocation: Dict[Temporary, DataLocation]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._all_temps = []
|
||||||
|
self._current_num = 0
|
||||||
|
self._allocation = dict()
|
||||||
|
|
||||||
|
def get_all_temps(self) -> List[Temporary]:
|
||||||
|
"""Return all the temporaries of the pool."""
|
||||||
|
return self._all_temps
|
||||||
|
|
||||||
|
def get_alloced_loc(self, t: Temporary) -> DataLocation:
|
||||||
|
"""Get the actual DataLocation allocated for the temporary t."""
|
||||||
|
return self._allocation[t]
|
||||||
|
|
||||||
|
def add_tmp(self, t: Temporary):
|
||||||
|
"""Add a temporary to the pool."""
|
||||||
|
self._all_temps.append(t)
|
||||||
|
self._allocation[t] = t # While no allocation, return the temporary itself
|
||||||
|
|
||||||
|
def set_temp_allocation(self, allocation: Dict[Temporary, DataLocation]) -> None:
|
||||||
|
"""Give a mapping from temporaries to actual registers.
|
||||||
|
The argument allocation must be a dict from Temporary to
|
||||||
|
DataLocation other than Temporary (typically Register or Offset).
|
||||||
|
Typing enforces that keys are Temporary and values are Datalocation.
|
||||||
|
We check the values are indeed not Temporary.
|
||||||
|
"""
|
||||||
|
for v in allocation.values():
|
||||||
|
assert not isinstance(v, Temporary), (
|
||||||
|
"Incorrect allocation scheme: value " +
|
||||||
|
str(v) + " is a Temporary.")
|
||||||
|
self._allocation = allocation
|
||||||
|
|
||||||
|
def fresh_tmp(self) -> Temporary:
|
||||||
|
"""Give a new fresh Temporary and add it to the pool."""
|
||||||
|
t = Temporary(self._current_num, self)
|
||||||
|
self._current_num += 1
|
||||||
|
self.add_tmp(t)
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
class Renamer:
|
||||||
|
"""Manage a renaming of temporaries."""
|
||||||
|
|
||||||
|
_pool: TemporaryPool
|
||||||
|
_env: Dict[Temporary, Temporary]
|
||||||
|
|
||||||
|
def __init__(self, pool: TemporaryPool):
|
||||||
|
self._pool = pool
|
||||||
|
self._env = dict()
|
||||||
|
|
||||||
|
def fresh(self, t: Temporary) -> Temporary:
|
||||||
|
"""Give a fresh rename for a Temporary."""
|
||||||
|
new_t = self._pool.fresh_tmp()
|
||||||
|
self._env[t] = new_t
|
||||||
|
return new_t
|
||||||
|
|
||||||
|
def replace(self, t: Temporary) -> Temporary:
|
||||||
|
"""Give the rename for a Temporary (which is itself if it is not renamed)."""
|
||||||
|
return self._env.get(t, t)
|
||||||
|
|
||||||
|
def defined(self, t: Temporary) -> bool:
|
||||||
|
"""True if the Temporary is renamed."""
|
||||||
|
return t in self._env
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
"""Give a copy of the Renamer."""
|
||||||
|
r = Renamer(self._pool)
|
||||||
|
r._env = self._env.copy()
|
||||||
|
return r
|
62
MiniC/Lib/PhiNode.py
Normal file
62
MiniC/Lib/PhiNode.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
"""
|
||||||
|
Classes for φ nodes in a RiscV CFG :py:class:`CFG <Lib.CFG.CFG>` under SSA Form:
|
||||||
|
:py:class:`PhiNode` for a statement of the form temp_x = φ(temp_0, ..., temp_n).
|
||||||
|
These particular kinds of statements are expected to be in the field
|
||||||
|
b._phis for a :py:class:`Block <Lib.CFG.Block>` b.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Dict
|
||||||
|
|
||||||
|
from Lib.Operands import Operand, Temporary, DataLocation, Renamer
|
||||||
|
from Lib.Statement import Statement, Label
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PhiNode(Statement):
|
||||||
|
"""
|
||||||
|
A φ node is a renaming in the CFG, of the form temp_x = φ(temp_0, ..., temp_n).
|
||||||
|
The field var contains the variable temp_x.
|
||||||
|
The field srcs relies for each precedent block in the CFG, identified with its label,
|
||||||
|
the variable temp_i of the φ node.
|
||||||
|
"""
|
||||||
|
var: DataLocation
|
||||||
|
srcs: Dict[Label, Operand]
|
||||||
|
|
||||||
|
def defined(self) -> List[Operand]:
|
||||||
|
"""Return the variable defined by the φ node."""
|
||||||
|
return [self.var]
|
||||||
|
|
||||||
|
def get_srcs(self) -> Dict[Label, Operand]:
|
||||||
|
"""
|
||||||
|
Return the dictionnary associating for each previous block the corresponding variable.
|
||||||
|
"""
|
||||||
|
return self.srcs
|
||||||
|
|
||||||
|
def used(self) -> List[Operand]:
|
||||||
|
"""Return the variables used by the statement."""
|
||||||
|
return list(self.srcs.values())
|
||||||
|
|
||||||
|
def rename(self, renamer: Renamer) -> None:
|
||||||
|
"""Rename the variable defined by the φ node with a fresh name."""
|
||||||
|
if isinstance(self.var, Temporary):
|
||||||
|
self.var = renamer.fresh(self.var)
|
||||||
|
|
||||||
|
def rename_from(self, renamer: Renamer, label: Label) -> None:
|
||||||
|
"""Rename the variable associated to the block identified by `label`."""
|
||||||
|
if label in self.srcs:
|
||||||
|
t = self.srcs[label]
|
||||||
|
if isinstance(t, Temporary):
|
||||||
|
if renamer.defined(t):
|
||||||
|
self.srcs[label] = renamer.replace(t)
|
||||||
|
else:
|
||||||
|
del self.srcs[label]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{} = φ({})".format(self.var, self.srcs)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.var, *self.srcs.items()))
|
||||||
|
|
||||||
|
def printIns(self, stream):
|
||||||
|
print(' # ' + str(self), file=stream)
|
92
MiniC/Lib/RiscV.py
Normal file
92
MiniC/Lib/RiscV.py
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
"""
|
||||||
|
MIF08, CAP, CodeGeneration, RiscV API
|
||||||
|
Functions to define instructions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from Lib.Errors import MiniCInternalError
|
||||||
|
from Lib.Operands import (Condition, Immediate, Operand, Function)
|
||||||
|
from Lib.Statement import (Instru3A, AbsoluteJump, ConditionalJump, Label)
|
||||||
|
|
||||||
|
|
||||||
|
def call(function: Function) -> Instru3A:
|
||||||
|
"""Function call."""
|
||||||
|
return Instru3A('call', function)
|
||||||
|
|
||||||
|
|
||||||
|
def jump(label: Label) -> AbsoluteJump:
|
||||||
|
"""Unconditional jump to label."""
|
||||||
|
return AbsoluteJump(label)
|
||||||
|
|
||||||
|
|
||||||
|
def conditional_jump(label: Label, op1: Operand, cond: Condition, op2: Operand):
|
||||||
|
"""Add a conditional jump to the code.
|
||||||
|
This is a wrapper around bge, bgt, beq, ... c is a Condition, like
|
||||||
|
Condition('bgt'), Condition(MiniCParser.EQ), ...
|
||||||
|
"""
|
||||||
|
return ConditionalJump(cond=cond, op1=op1, op2=op2, label=label)
|
||||||
|
|
||||||
|
|
||||||
|
def add(dr: Operand, sr1: Operand, sr2orimm7: Operand) -> Instru3A:
|
||||||
|
if isinstance(sr2orimm7, Immediate):
|
||||||
|
return Instru3A("addi", dr, sr1, sr2orimm7)
|
||||||
|
else:
|
||||||
|
return Instru3A("add", dr, sr1, sr2orimm7)
|
||||||
|
|
||||||
|
|
||||||
|
def mul(dr: Operand, sr1: Operand, sr2orimm7: Operand) -> Instru3A:
|
||||||
|
if isinstance(sr2orimm7, Immediate):
|
||||||
|
raise MiniCInternalError("Cant multiply by an immediate")
|
||||||
|
else:
|
||||||
|
return Instru3A("mul", dr, sr1, sr2orimm7)
|
||||||
|
|
||||||
|
|
||||||
|
def div(dr: Operand, sr1: Operand, sr2orimm7: Operand) -> Instru3A:
|
||||||
|
if isinstance(sr2orimm7, Immediate):
|
||||||
|
raise MiniCInternalError("Cant divide by an immediate")
|
||||||
|
else:
|
||||||
|
return Instru3A("div", dr, sr1, sr2orimm7)
|
||||||
|
|
||||||
|
|
||||||
|
def rem(dr: Operand, sr1: Operand, sr2orimm7: Operand) -> Instru3A:
|
||||||
|
if isinstance(sr2orimm7, Immediate):
|
||||||
|
raise MiniCInternalError("Cant divide by an immediate")
|
||||||
|
return Instru3A("rem", dr, sr1, sr2orimm7)
|
||||||
|
|
||||||
|
|
||||||
|
def sub(dr: Operand, sr1: Operand, sr2orimm7: Operand) -> Instru3A:
|
||||||
|
if isinstance(sr2orimm7, Immediate):
|
||||||
|
raise MiniCInternalError("Cant substract by an immediate")
|
||||||
|
return Instru3A("sub", dr, sr1, sr2orimm7)
|
||||||
|
|
||||||
|
|
||||||
|
def land(dr: Operand, sr1: Operand, sr2orimm7: Operand) -> Instru3A:
|
||||||
|
"""And instruction (cannot be called `and` due to Python and)."""
|
||||||
|
return Instru3A("and", dr, sr1, sr2orimm7)
|
||||||
|
|
||||||
|
|
||||||
|
def lor(dr: Operand, sr1: Operand, sr2orimm7: Operand) -> Instru3A:
|
||||||
|
"""Or instruction (cannot be called `or` due to Python or)."""
|
||||||
|
return Instru3A("or", dr, sr1, sr2orimm7)
|
||||||
|
|
||||||
|
|
||||||
|
def xor(dr: Operand, sr1: Operand, sr2orimm7: Operand) -> Instru3A: # pragma: no cover
|
||||||
|
if isinstance(sr2orimm7, Immediate):
|
||||||
|
return Instru3A("xori", dr, sr1, sr2orimm7)
|
||||||
|
else:
|
||||||
|
return Instru3A("xor", dr, sr1, sr2orimm7)
|
||||||
|
|
||||||
|
|
||||||
|
def li(dr: Operand, imm7: Immediate) -> Instru3A:
|
||||||
|
return Instru3A("li", dr, imm7)
|
||||||
|
|
||||||
|
|
||||||
|
def mv(dr: Operand, sr: Operand) -> Instru3A:
|
||||||
|
return Instru3A("mv", dr, sr)
|
||||||
|
|
||||||
|
|
||||||
|
def ld(dr: Operand, mem: Operand) -> Instru3A:
|
||||||
|
return Instru3A("ld", dr, mem)
|
||||||
|
|
||||||
|
|
||||||
|
def sd(sr: Operand, mem: Operand) -> Instru3A:
|
||||||
|
return Instru3A("sd", sr, mem)
|
278
MiniC/Lib/Statement.py
Normal file
278
MiniC/Lib/Statement.py
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
"""
|
||||||
|
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)
|
153
MiniC/Lib/Terminator.py
Normal file
153
MiniC/Lib/Terminator.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
"""
|
||||||
|
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()
|
0
MiniC/Lib/__init__.py
Normal file
0
MiniC/Lib/__init__.py
Normal file
@ -35,8 +35,8 @@ doc: antlr
|
|||||||
sphinx-apidoc -e -f -o doc/api . TP* replace_* *Wrapper* MiniC* conf* test*
|
sphinx-apidoc -e -f -o doc/api . TP* replace_* *Wrapper* MiniC* conf* test*
|
||||||
make -C doc html
|
make -C doc html
|
||||||
|
|
||||||
test: test-interpret
|
|
||||||
|
|
||||||
|
test: test-interpret test-codegen
|
||||||
|
|
||||||
|
|
||||||
test-pyright: antlr
|
test-pyright: antlr
|
||||||
@ -67,8 +67,6 @@ test-naive: test-pyright antlr
|
|||||||
test-mem: test-pyright antlr
|
test-mem: test-pyright antlr
|
||||||
$(LINEAR) python3 -m pytest $(PYTEST_BASE_OPTS) $(PYTEST_OPTS) ./test_codegen.py -k 'test_alloc_mem'
|
$(LINEAR) python3 -m pytest $(PYTEST_BASE_OPTS) $(PYTEST_OPTS) ./test_codegen.py -k 'test_alloc_mem'
|
||||||
|
|
||||||
test-hybrid: test-pyright antlr
|
|
||||||
$(LINEAR) python3 -m pytest $(PYTEST_BASE_OPTS) $(PYTEST_OPTS) ./test_codegen.py -k 'test_alloc_hybrid'
|
|
||||||
|
|
||||||
# Test for all but the smart allocator, i.e. everything that lab4 should pass:
|
# Test for all but the smart allocator, i.e. everything that lab4 should pass:
|
||||||
test-lab4: test-pyright antlr
|
test-lab4: test-pyright antlr
|
||||||
|
21
MiniC/README-SSA.md
Normal file
21
MiniC/README-SSA.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# MiniC Compiler
|
||||||
|
LAB5a (Control Flow Graph in SSA Form) & LAB5b (Smart Register Allocation), CAP 2022-23
|
||||||
|
|
||||||
|
# Authors
|
||||||
|
|
||||||
|
YOUR NAME HERE
|
||||||
|
|
||||||
|
# Contents
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- Explain any design choices you may have made.
|
||||||
|
- Do not forget to remove all debug traces from your code!
|
||||||
|
- Did you implement an extension?
|
||||||
|
|
||||||
|
# Test design
|
||||||
|
|
||||||
|
TODO: give the main objectives of your tests.
|
||||||
|
|
||||||
|
# Known bugs
|
||||||
|
|
||||||
|
TODO: bugs you could not fix (if any).
|
38
MiniC/README-alloc.md
Normal file
38
MiniC/README-alloc.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# MiniC Compiler
|
||||||
|
LAB5 (smart code generation), MIF08 / CAP 2022-23
|
||||||
|
|
||||||
|
# Authors
|
||||||
|
|
||||||
|
YOUR NAME HERE
|
||||||
|
|
||||||
|
# Contents
|
||||||
|
|
||||||
|
TODO for STUDENTS : Say a bit about the code infrastructure ...
|
||||||
|
|
||||||
|
# Howto
|
||||||
|
|
||||||
|
To compile and run a program:
|
||||||
|
```
|
||||||
|
$ python3 ./MiniCC.py --reg-alloc=smart TP04/tests/provided/step1/test00.c
|
||||||
|
Code will be generated in file TP04/tests/provided/step1/test00.s
|
||||||
|
$ riscv64-unknown-elf-gcc TP04/tests/provided/step1/test00.s ../TP01/riscv/libprint.s -o /tmp/a.out
|
||||||
|
$ spike pk /tmp/a.out
|
||||||
|
```
|
||||||
|
|
||||||
|
To launch the testsuite:
|
||||||
|
```
|
||||||
|
make test-smart
|
||||||
|
```
|
||||||
|
|
||||||
|
# Test design
|
||||||
|
|
||||||
|
TODO: explain your tests
|
||||||
|
|
||||||
|
# Design choices
|
||||||
|
|
||||||
|
TODO: explain your choices
|
||||||
|
|
||||||
|
# Known bugs
|
||||||
|
|
||||||
|
TODO: Bugs and limitations.
|
||||||
|
|
57
MiniC/README-codegen.md
Normal file
57
MiniC/README-codegen.md
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# MiniC Compiler
|
||||||
|
LAB4 (simple code generation), MIF08 / CAP 2022-23
|
||||||
|
|
||||||
|
# Authors
|
||||||
|
|
||||||
|
YOUR NAME HERE
|
||||||
|
|
||||||
|
# Contents
|
||||||
|
|
||||||
|
TODO for STUDENTS : Say a bit about the code infrastructure ...
|
||||||
|
|
||||||
|
# Test design
|
||||||
|
|
||||||
|
TODO: explain your tests
|
||||||
|
|
||||||
|
# Design choices
|
||||||
|
|
||||||
|
TODO: explain your choices. How did you implement boolean not? Did you implement an extension?
|
||||||
|
|
||||||
|
# Known bugs
|
||||||
|
|
||||||
|
TODO: Bugs and limitations.
|
||||||
|
|
||||||
|
# Checklists
|
||||||
|
|
||||||
|
A check ([X]) means that the feature is implemented
|
||||||
|
and *tested* with appropriate test cases.
|
||||||
|
|
||||||
|
## Code generation
|
||||||
|
|
||||||
|
- [ ] Number Atom
|
||||||
|
- [ ] Boolean Atom
|
||||||
|
- [ ] Id Atom
|
||||||
|
- [ ] Additive expression
|
||||||
|
- [ ] Multiplicative expression
|
||||||
|
- [ ] UnaryMinus expression
|
||||||
|
- [ ] Or expression
|
||||||
|
- [ ] And expression
|
||||||
|
- [ ] Equality expression
|
||||||
|
- [ ] Relational expression (! many cases -> many tests)
|
||||||
|
- [ ] Not expression
|
||||||
|
|
||||||
|
## Statements
|
||||||
|
|
||||||
|
- [ ] Prog, assignements
|
||||||
|
- [ ] While
|
||||||
|
- [ ] Cond Block
|
||||||
|
- [ ] If
|
||||||
|
- [ ] Nested ifs
|
||||||
|
- [ ] Nested whiles
|
||||||
|
|
||||||
|
## Allocation
|
||||||
|
|
||||||
|
- [ ] Naive allocation
|
||||||
|
- [ ] All in memory allocation
|
||||||
|
- [ ] Massive tests of memory allocation
|
||||||
|
|
60
MiniC/README-functions.md
Normal file
60
MiniC/README-functions.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# MiniC Compiler
|
||||||
|
LAB6 (code generation for functions), MIF08 / CAP 2022-23
|
||||||
|
|
||||||
|
# Authors
|
||||||
|
|
||||||
|
YOUR NAME HERE
|
||||||
|
|
||||||
|
# Contents
|
||||||
|
|
||||||
|
TODO: Say a bit about the code infrastructure ...
|
||||||
|
|
||||||
|
# Howto
|
||||||
|
|
||||||
|
As in the previous labs.
|
||||||
|
|
||||||
|
`make test-codegen SSA=1` to lauch pyright and the testsuite
|
||||||
|
with the three allocators (do not forget the options you can add).
|
||||||
|
|
||||||
|
# Test design
|
||||||
|
|
||||||
|
TODO: give the main objectives of your tests.
|
||||||
|
|
||||||
|
# Design choices
|
||||||
|
|
||||||
|
TODO: explain your choices
|
||||||
|
|
||||||
|
# Known bugs
|
||||||
|
|
||||||
|
TODO: bugs and limitations you could not fix (if any).
|
||||||
|
|
||||||
|
# Checklists
|
||||||
|
|
||||||
|
A check ([X]) means that the feature is implemented
|
||||||
|
and *tested* with appropriate test cases.
|
||||||
|
|
||||||
|
## Parser
|
||||||
|
|
||||||
|
- [ ] Function definition
|
||||||
|
- [ ] Function declaration
|
||||||
|
- [ ] Function call
|
||||||
|
|
||||||
|
## Typer
|
||||||
|
|
||||||
|
- [ ] Function declaration
|
||||||
|
- [ ] Function definition
|
||||||
|
- [ ] Function call
|
||||||
|
- [ ] Function return
|
||||||
|
|
||||||
|
## Code generation
|
||||||
|
|
||||||
|
- [ ] Function return
|
||||||
|
- [ ] Callee-saved registers
|
||||||
|
- [ ] Function call
|
||||||
|
- [ ] Getting the result of a function call
|
||||||
|
- [ ] Caller-saved registers
|
||||||
|
- [ ] Increase the size of the stack for callee/caller-saved registers
|
||||||
|
- [ ] Temporaries for giving arguments to a function call
|
||||||
|
- [ ] Temporaries for retriving arguments at the beginning of a function
|
||||||
|
|
||||||
|
|
33
MiniC/TP04/AllInMemAllocator.py
Normal file
33
MiniC/TP04/AllInMemAllocator.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
from Lib import RiscV
|
||||||
|
from Lib.Operands import Temporary, Operand, S
|
||||||
|
from Lib.Statement import Instruction
|
||||||
|
from Lib.Allocator import Allocator
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
class AllInMemAllocator(Allocator):
|
||||||
|
|
||||||
|
def replace(self, old_instr: Instruction) -> List[Instruction]:
|
||||||
|
"""Replace Temporary operands with the corresponding allocated
|
||||||
|
memory location."""
|
||||||
|
numreg = 1
|
||||||
|
before: List[Instruction] = []
|
||||||
|
after: List[Instruction] = []
|
||||||
|
new_args: List[Operand] = []
|
||||||
|
# TODO: compute before,after,args.
|
||||||
|
# TODO: iterate over old_args, check which argument
|
||||||
|
# TODO: is a temporary (e.g. isinstance(..., Temporary)),
|
||||||
|
# TODO: and if so, generate ld/sd accordingly. Replace the
|
||||||
|
# TODO: temporary with S[1], S[2] or S[3] physical registers.
|
||||||
|
new_instr = old_instr.with_args(new_args)
|
||||||
|
return before + [new_instr] + after
|
||||||
|
|
||||||
|
def prepare(self) -> None:
|
||||||
|
"""Allocate all temporaries to memory.
|
||||||
|
Invariants:
|
||||||
|
- Expanded instructions can use s2 and s3
|
||||||
|
(to store the values of temporaries before the actual instruction).
|
||||||
|
"""
|
||||||
|
self._fdata._pool.set_temp_allocation(
|
||||||
|
{temp: self._fdata.fresh_offset()
|
||||||
|
for temp in self._fdata._pool.get_all_temps()})
|
111
MiniC/TP04/BuildCFG.py
Normal file
111
MiniC/TP04/BuildCFG.py
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
"""
|
||||||
|
CAP, CodeGeneration, CFG construction from linear code
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List
|
||||||
|
from Lib.Errors import MiniCInternalError
|
||||||
|
from Lib.FunctionData import FunctionData
|
||||||
|
from Lib.LinearCode import LinearCode, CodeStatement
|
||||||
|
from Lib.Statement import (
|
||||||
|
Instru3A, Comment, Label, AbsoluteJump, ConditionalJump
|
||||||
|
)
|
||||||
|
from Lib.Terminator import jump2terminator
|
||||||
|
from Lib.CFG import Block, BlockInstr, CFG
|
||||||
|
|
||||||
|
|
||||||
|
def find_leaders(instructions: List[CodeStatement]) -> List[int]:
|
||||||
|
"""
|
||||||
|
Find the leaders in the given list of instructions as linear code.
|
||||||
|
Returns a list of indices in the instruction list whose first is 0 and
|
||||||
|
last is len(instructions)
|
||||||
|
"""
|
||||||
|
leaders: List[int] = [0]
|
||||||
|
# TODO fill leaders
|
||||||
|
# The final "ret" is also a form of jump
|
||||||
|
leaders.append(len(instructions))
|
||||||
|
return leaders
|
||||||
|
|
||||||
|
|
||||||
|
def separate_with_leaders(instructions: List[CodeStatement],
|
||||||
|
leaders: List[int]) -> List[List[CodeStatement]]:
|
||||||
|
"""
|
||||||
|
Partition the lists instructions into a list containing for
|
||||||
|
elements the lists of statements between indices
|
||||||
|
leaders[i] (included) and leaders[i+1] (excluded).
|
||||||
|
|
||||||
|
If leaders[i] = leaders[i+1], do not add the empty list.
|
||||||
|
"""
|
||||||
|
chunks: List[List[CodeStatement]] = []
|
||||||
|
for i in range(0, len(leaders)-1):
|
||||||
|
start = leaders[i]
|
||||||
|
end = leaders[i+1]
|
||||||
|
if start != end:
|
||||||
|
# Avoid corner-cases when a label immediately follows a jump
|
||||||
|
chunks.append(instructions[start:end])
|
||||||
|
return chunks
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_chunk(pre_chunk: List[CodeStatement], fdata: FunctionData) -> tuple[
|
||||||
|
Label, ConditionalJump | AbsoluteJump | None, List[BlockInstr]]:
|
||||||
|
"""
|
||||||
|
Extract the potential label (respectively jump)
|
||||||
|
at the start (respectively end) of the list instrs_chunk,
|
||||||
|
and return the tuple with this label, this jump and the
|
||||||
|
rest of instrs_chunk.
|
||||||
|
|
||||||
|
If there is no label at the start then return a fresh label instead,
|
||||||
|
thanks to fdata (use `fdata.fresh_label(fdata._name)` for instance).
|
||||||
|
If there is no jump at the end, return None instead.
|
||||||
|
|
||||||
|
Raise an error if there is a label not in first position in pre_chunk,
|
||||||
|
or a jump not in last position.
|
||||||
|
"""
|
||||||
|
label = None
|
||||||
|
jump = None
|
||||||
|
inner_statements: List[CodeStatement] = pre_chunk
|
||||||
|
# Extract the first instruction from inner_statements if it is a label, or create a fresh one
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
# Extract the last instruction from inner_statements if it is a jump, or do nothing
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
# Check that there is no other label or jump left in inner_statements
|
||||||
|
l: List[BlockInstr] = []
|
||||||
|
for i in inner_statements:
|
||||||
|
match i:
|
||||||
|
case AbsoluteJump() | ConditionalJump():
|
||||||
|
raise MiniCInternalError(
|
||||||
|
"prepare_chunk: Jump {} not in last position of a chunk"
|
||||||
|
.format(i))
|
||||||
|
case Label():
|
||||||
|
raise MiniCInternalError(
|
||||||
|
"prepare_chunk: Label {} not in first position of a chunk"
|
||||||
|
.format(i))
|
||||||
|
case Instru3A() | Comment():
|
||||||
|
l.append(i)
|
||||||
|
return (label, jump, l)
|
||||||
|
|
||||||
|
|
||||||
|
def build_cfg(linCode: LinearCode) -> CFG:
|
||||||
|
"""Extract the blocks from the linear code and add them to the CFG."""
|
||||||
|
fdata = linCode.fdata
|
||||||
|
cfg = CFG(fdata)
|
||||||
|
instructions = linCode.get_instructions()
|
||||||
|
# 1. Identify Leaders
|
||||||
|
leaders = find_leaders(instructions)
|
||||||
|
# 2. Extract Chunks of Instructions
|
||||||
|
pre_chunks: List[List[CodeStatement]] = separate_with_leaders(instructions, leaders)
|
||||||
|
chunks: List[tuple[Label, ConditionalJump | AbsoluteJump | None, List[BlockInstr]]] = [
|
||||||
|
prepare_chunk(pre_chunk, fdata) for pre_chunk in pre_chunks]
|
||||||
|
# 3. Build the Blocks
|
||||||
|
next_label = None
|
||||||
|
for (label, jump, block_instrs) in reversed(chunks):
|
||||||
|
term = jump2terminator(jump, next_label)
|
||||||
|
block = Block(label, block_instrs, term)
|
||||||
|
cfg.add_block(block)
|
||||||
|
next_label = label
|
||||||
|
# 4. Fill the edges
|
||||||
|
for block in cfg.get_blocks():
|
||||||
|
for dest in cfg.out_blocks(block):
|
||||||
|
cfg.add_edge(block, dest)
|
||||||
|
# 5. Identify the entry label of the CFG
|
||||||
|
cfg.set_start(chunks[0][0])
|
||||||
|
return cfg
|
40
MiniC/TP04/LinearizeCFG.py
Normal file
40
MiniC/TP04/LinearizeCFG.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
"""
|
||||||
|
CAP, CodeGeneration, CFG linearization to a list of statements
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Set
|
||||||
|
from Lib.Statement import Statement, AbsoluteJump, ConditionalJump
|
||||||
|
from Lib.Terminator import Return, BranchingTerminator
|
||||||
|
from Lib.CFG import Block, CFG
|
||||||
|
|
||||||
|
|
||||||
|
def ordered_blocks_list(cfg: CFG) -> List[Block]:
|
||||||
|
"""
|
||||||
|
Compute a list of blocks with optimized ordering for linearization.
|
||||||
|
"""
|
||||||
|
# TODO
|
||||||
|
return cfg.get_blocks()
|
||||||
|
|
||||||
|
|
||||||
|
def linearize(cfg: CFG) -> List[Statement]:
|
||||||
|
"""
|
||||||
|
Linearize the given control flow graph as a list of instructions.
|
||||||
|
"""
|
||||||
|
# TODO
|
||||||
|
l: List[Statement] = [] # Linearized CFG
|
||||||
|
blocks: List[Block] = ordered_blocks_list(cfg) # All blocks of the CFG
|
||||||
|
for i, block in enumerate(blocks):
|
||||||
|
# 1. Add the label of the block to the linearization
|
||||||
|
l.append(block.get_label())
|
||||||
|
# 2. Add the body of the block to the linearization
|
||||||
|
l.extend(block.get_body())
|
||||||
|
# 3. Add the terminator of the block to the linearization
|
||||||
|
match block.get_terminator():
|
||||||
|
case BranchingTerminator() as j:
|
||||||
|
l.append(ConditionalJump(j.cond, j.op1, j.op2, j.label_then))
|
||||||
|
l.append(AbsoluteJump(j.label_else))
|
||||||
|
case AbsoluteJump() as j:
|
||||||
|
l.append(AbsoluteJump(j.label))
|
||||||
|
case Return():
|
||||||
|
l.append(AbsoluteJump(cfg.get_end()))
|
||||||
|
return l
|
196
MiniC/TP04/MiniCCodeGen3AVisitor.py
Normal file
196
MiniC/TP04/MiniCCodeGen3AVisitor.py
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
from typing import List
|
||||||
|
from MiniCVisitor import MiniCVisitor
|
||||||
|
from MiniCParser import MiniCParser
|
||||||
|
from Lib.LinearCode import LinearCode
|
||||||
|
from Lib import RiscV
|
||||||
|
from Lib.RiscV import Condition
|
||||||
|
from Lib import Operands
|
||||||
|
from antlr4.tree.Trees import Trees
|
||||||
|
from Lib.Errors import MiniCInternalError, MiniCUnsupportedError
|
||||||
|
|
||||||
|
"""
|
||||||
|
CAP, MIF08, three-address code generation + simple alloc
|
||||||
|
This visitor constructs an object of type "LinearCode".
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MiniCCodeGen3AVisitor(MiniCVisitor):
|
||||||
|
|
||||||
|
_current_function: LinearCode
|
||||||
|
|
||||||
|
def __init__(self, debug, parser):
|
||||||
|
super().__init__()
|
||||||
|
self._parser = parser
|
||||||
|
self._debug = debug
|
||||||
|
self._functions = []
|
||||||
|
self._lastlabel = ""
|
||||||
|
|
||||||
|
def get_functions(self) -> List[LinearCode]:
|
||||||
|
return self._functions
|
||||||
|
|
||||||
|
def printSymbolTable(self): # pragma: no cover
|
||||||
|
print("--variables to temporaries map--")
|
||||||
|
for keys, values in self._symbol_table.items():
|
||||||
|
print(keys + '-->' + str(values))
|
||||||
|
|
||||||
|
# handle variable decl
|
||||||
|
|
||||||
|
def visitVarDecl(self, ctx) -> None:
|
||||||
|
type_str = ctx.typee().getText()
|
||||||
|
vars_l = self.visit(ctx.id_l())
|
||||||
|
for name in vars_l:
|
||||||
|
if name in self._symbol_table:
|
||||||
|
raise MiniCInternalError(
|
||||||
|
"Variable {} has already been declared".format(name))
|
||||||
|
else:
|
||||||
|
tmp = self._current_function.fdata.fresh_tmp()
|
||||||
|
self._symbol_table[name] = tmp
|
||||||
|
if type_str not in ("int", "bool"):
|
||||||
|
raise MiniCUnsupportedError("Unsupported type " + type_str)
|
||||||
|
# Initialization to 0 or False, both represented with 0
|
||||||
|
self._current_function.add_instruction(
|
||||||
|
RiscV.li(tmp, Operands.Immediate(0)))
|
||||||
|
|
||||||
|
def visitIdList(self, ctx) -> List[str]:
|
||||||
|
t = self.visit(ctx.id_l())
|
||||||
|
t.append(ctx.ID().getText())
|
||||||
|
return t
|
||||||
|
|
||||||
|
def visitIdListBase(self, ctx) -> List[str]:
|
||||||
|
return [ctx.ID().getText()]
|
||||||
|
|
||||||
|
# expressions
|
||||||
|
|
||||||
|
def visitParExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
return self.visit(ctx.expr())
|
||||||
|
|
||||||
|
def visitIntAtom(self, ctx) -> Operands.Temporary:
|
||||||
|
val = Operands.Immediate(int(ctx.getText()))
|
||||||
|
dest_temp = self._current_function.fdata.fresh_tmp()
|
||||||
|
self._current_function.add_instruction(RiscV.li(dest_temp, val))
|
||||||
|
return dest_temp
|
||||||
|
|
||||||
|
def visitFloatAtom(self, ctx) -> Operands.Temporary:
|
||||||
|
raise MiniCUnsupportedError("float literal")
|
||||||
|
|
||||||
|
def visitBooleanAtom(self, ctx) -> Operands.Temporary:
|
||||||
|
# true is 1 false is 0
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
|
def visitIdAtom(self, ctx) -> Operands.Temporary:
|
||||||
|
try:
|
||||||
|
# get the temporary associated to id
|
||||||
|
return self._symbol_table[ctx.getText()]
|
||||||
|
except KeyError: # pragma: no cover
|
||||||
|
raise MiniCInternalError(
|
||||||
|
"Undefined variable {}, this should have failed to typecheck."
|
||||||
|
.format(ctx.getText())
|
||||||
|
)
|
||||||
|
|
||||||
|
def visitStringAtom(self, ctx) -> Operands.Temporary:
|
||||||
|
raise MiniCUnsupportedError("string atom")
|
||||||
|
|
||||||
|
# now visit expressions
|
||||||
|
|
||||||
|
def visitAtomExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
return self.visit(ctx.atom())
|
||||||
|
|
||||||
|
def visitAdditiveExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
assert ctx.myop is not None
|
||||||
|
tmpl: Operands.Temporary = self.visit(ctx.expr(0))
|
||||||
|
tmpr: Operands.Temporary = self.visit(ctx.expr(1))
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
|
def visitOrExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
|
def visitAndExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
|
def visitEqualityExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
return self.visitRelationalExpr(ctx)
|
||||||
|
|
||||||
|
def visitRelationalExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
assert ctx.myop is not None
|
||||||
|
c = Condition(ctx.myop.type)
|
||||||
|
if self._debug:
|
||||||
|
print("relational expression:")
|
||||||
|
print(Trees.toStringTree(ctx, [], self._parser))
|
||||||
|
print("Condition:", c)
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
|
def visitMultiplicativeExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
assert ctx.myop is not None
|
||||||
|
div_by_zero_lbl = self._current_function.fdata.get_label_div_by_zero()
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
|
def visitNotExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
|
||||||
|
def visitUnaryMinusExpr(self, ctx) -> Operands.Temporary:
|
||||||
|
raise NotImplementedError("unaryminusexpr") # TODO
|
||||||
|
|
||||||
|
def visitProgRule(self, ctx) -> None:
|
||||||
|
self.visitChildren(ctx)
|
||||||
|
|
||||||
|
def visitFuncDef(self, ctx) -> None:
|
||||||
|
funcname = ctx.ID().getText()
|
||||||
|
self._current_function = LinearCode(funcname)
|
||||||
|
self._symbol_table = dict()
|
||||||
|
|
||||||
|
self.visit(ctx.vardecl_l())
|
||||||
|
self.visit(ctx.block())
|
||||||
|
self._current_function.add_comment("Return at end of function:")
|
||||||
|
# This skeleton doesn't deal properly with functions, and
|
||||||
|
# hardcodes a "return 0;" at the end of function. Generate
|
||||||
|
# code for this "return 0;".
|
||||||
|
self._current_function.add_instruction(
|
||||||
|
RiscV.li(Operands.A0, Operands.Immediate(0)))
|
||||||
|
self._functions.append(self._current_function)
|
||||||
|
del self._current_function
|
||||||
|
|
||||||
|
def visitAssignStat(self, ctx) -> None:
|
||||||
|
if self._debug:
|
||||||
|
print("assign statement, rightexpression is:")
|
||||||
|
print(Trees.toStringTree(ctx.expr(), [], self._parser))
|
||||||
|
expr_temp = self.visit(ctx.expr())
|
||||||
|
name = ctx.ID().getText()
|
||||||
|
self._current_function.add_instruction(RiscV.mv(self._symbol_table[name], expr_temp))
|
||||||
|
|
||||||
|
def visitIfStat(self, ctx) -> None:
|
||||||
|
if self._debug:
|
||||||
|
print("if statement")
|
||||||
|
end_if_label = self._current_function.fdata.fresh_label("end_if")
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
self._current_function.add_label(end_if_label)
|
||||||
|
|
||||||
|
def visitWhileStat(self, ctx) -> None:
|
||||||
|
if self._debug:
|
||||||
|
print("while statement, condition is:")
|
||||||
|
print(Trees.toStringTree(ctx.expr(), [], self._parser))
|
||||||
|
print("and block is:")
|
||||||
|
print(Trees.toStringTree(ctx.stat_block(), [], self._parser))
|
||||||
|
raise NotImplementedError() # TODO
|
||||||
|
# visit statements
|
||||||
|
|
||||||
|
def visitPrintlnintStat(self, ctx) -> None:
|
||||||
|
expr_loc = self.visit(ctx.expr())
|
||||||
|
if self._debug:
|
||||||
|
print("print_int statement, expression is:")
|
||||||
|
print(Trees.toStringTree(ctx.expr(), [], self._parser))
|
||||||
|
self._current_function.add_instruction_PRINTLN_INT(expr_loc)
|
||||||
|
|
||||||
|
def visitPrintlnboolStat(self, ctx) -> None:
|
||||||
|
expr_loc = self.visit(ctx.expr())
|
||||||
|
self._current_function.add_instruction_PRINTLN_INT(expr_loc)
|
||||||
|
|
||||||
|
def visitPrintlnfloatStat(self, ctx) -> None:
|
||||||
|
raise MiniCUnsupportedError("Unsupported type float")
|
||||||
|
|
||||||
|
def visitPrintlnstringStat(self, ctx) -> None:
|
||||||
|
raise MiniCUnsupportedError("Unsupported type string")
|
||||||
|
|
||||||
|
def visitStatList(self, ctx) -> None:
|
||||||
|
for stat in ctx.stat():
|
||||||
|
self._current_function.add_comment(Trees.toStringTree(stat, [], self._parser))
|
||||||
|
self.visit(stat)
|
13
MiniC/TP04/tests/provided/dataflow/df00.c
Normal file
13
MiniC/TP04/tests/provided/dataflow/df00.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int n,u;
|
||||||
|
n=6;
|
||||||
|
println_int(n);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 6
|
15
MiniC/TP04/tests/provided/dataflow/df01.c
Normal file
15
MiniC/TP04/tests/provided/dataflow/df01.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int n,u,v;
|
||||||
|
n=6;
|
||||||
|
u=12;
|
||||||
|
v=n+u;
|
||||||
|
println_int(v);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 18
|
14
MiniC/TP04/tests/provided/dataflow/df02.c
Normal file
14
MiniC/TP04/tests/provided/dataflow/df02.c
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int n,v;
|
||||||
|
bool u;
|
||||||
|
n=6;
|
||||||
|
u=12>n;
|
||||||
|
println_bool(1<n && u);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 1
|
18
MiniC/TP04/tests/provided/dataflow/df03.c
Normal file
18
MiniC/TP04/tests/provided/dataflow/df03.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int n, u, v;
|
||||||
|
n = 6;
|
||||||
|
u = 0;
|
||||||
|
while (n > 1)
|
||||||
|
{
|
||||||
|
n = n - 1;
|
||||||
|
u = u + n;
|
||||||
|
}
|
||||||
|
println_int(u);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 15
|
16
MiniC/TP04/tests/provided/dataflow/df04.c
Normal file
16
MiniC/TP04/tests/provided/dataflow/df04.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
x = 2;
|
||||||
|
if (x < 4)
|
||||||
|
x = 4;
|
||||||
|
else
|
||||||
|
x = 5;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
14
MiniC/TP04/tests/provided/dataflow/df05.c
Normal file
14
MiniC/TP04/tests/provided/dataflow/df05.c
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
x = 0;
|
||||||
|
while (x < 4)
|
||||||
|
{
|
||||||
|
x = x + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
9
MiniC/TP04/tests/provided/step1/test00.c
Normal file
9
MiniC/TP04/tests/provided/step1/test00.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
println_int(42);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 42
|
11
MiniC/TP04/tests/provided/step1/test00b.c
Normal file
11
MiniC/TP04/tests/provided/step1/test00b.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int x,y;
|
||||||
|
x=4;
|
||||||
|
y=12+x;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
11
MiniC/TP04/tests/provided/step1/test00d.c
Normal file
11
MiniC/TP04/tests/provided/step1/test00d.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int a,n;
|
||||||
|
n=1;
|
||||||
|
a=n+12;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
11
MiniC/TP04/tests/provided/step1/test01.c
Normal file
11
MiniC/TP04/tests/provided/step1/test01.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int n;
|
||||||
|
n=6;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
11
MiniC/TP04/tests/provided/step1/test_print.c
Normal file
11
MiniC/TP04/tests/provided/step1/test_print.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
println_int(43);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 43
|
||||||
|
|
12
MiniC/TP04/tests/provided/step1/test_var.c
Normal file
12
MiniC/TP04/tests/provided/step1/test_var.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int x;
|
||||||
|
x = 42;
|
||||||
|
println_int(x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 42
|
16
MiniC/TP04/tests/provided/step1/test_var_plus.c
Normal file
16
MiniC/TP04/tests/provided/step1/test_var_plus.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int x;
|
||||||
|
x = 42;
|
||||||
|
println_int(x + x);
|
||||||
|
println_int(x + 1);
|
||||||
|
println_int(1 + x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 84
|
||||||
|
// 43
|
||||||
|
// 43
|
20
MiniC/TP04/tests/provided/step1/test_vars.c
Normal file
20
MiniC/TP04/tests/provided/step1/test_vars.c
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int x, y;
|
||||||
|
x = 42;
|
||||||
|
y = 66;
|
||||||
|
println_int(x + y);
|
||||||
|
x = 1;
|
||||||
|
println_int(x + y);
|
||||||
|
y = 2;
|
||||||
|
println_int(x + y);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 108
|
||||||
|
// 67
|
||||||
|
// 3
|
||||||
|
|
16
MiniC/TP04/tests/provided/step2/test02.c
Normal file
16
MiniC/TP04/tests/provided/step2/test02.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int x,y;
|
||||||
|
|
||||||
|
x = 9;
|
||||||
|
if (x < 2)
|
||||||
|
y=7;
|
||||||
|
else
|
||||||
|
y=12;
|
||||||
|
x = y;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
13
MiniC/TP04/tests/provided/step2/test04.c
Normal file
13
MiniC/TP04/tests/provided/step2/test04.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int n;
|
||||||
|
bool a,b;
|
||||||
|
n=1;
|
||||||
|
a=true;
|
||||||
|
b=(a==(n<6));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
13
MiniC/TP04/tests/provided/step2/test05.c
Normal file
13
MiniC/TP04/tests/provided/step2/test05.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int x,y;
|
||||||
|
x=3;
|
||||||
|
if (x<5) {
|
||||||
|
y=x+1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
16
MiniC/TP04/tests/provided/step2/test06.c
Normal file
16
MiniC/TP04/tests/provided/step2/test06.c
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int x,y,z;
|
||||||
|
x=(2);
|
||||||
|
if (((x)<(3))) {
|
||||||
|
y=7;
|
||||||
|
} else {
|
||||||
|
y=8;
|
||||||
|
}
|
||||||
|
z=y+1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
20
MiniC/TP04/tests/provided/step2/test07.c
Normal file
20
MiniC/TP04/tests/provided/step2/test07.c
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int x,y,z,u;
|
||||||
|
x=3;
|
||||||
|
if (x < 4) {
|
||||||
|
z=4;
|
||||||
|
}
|
||||||
|
else if ( x < 5) {
|
||||||
|
z=5;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
z=6 ;
|
||||||
|
}
|
||||||
|
u=z+1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
19
MiniC/TP04/tests/provided/step2/test_bool.c
Normal file
19
MiniC/TP04/tests/provided/step2/test_bool.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
bool b;
|
||||||
|
b = false;
|
||||||
|
println_bool(b);
|
||||||
|
b = true;
|
||||||
|
println_bool(b);
|
||||||
|
println_bool(true);
|
||||||
|
println_bool(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 0
|
||||||
|
// 1
|
||||||
|
// 1
|
||||||
|
// 0
|
10
MiniC/TP04/tests/provided/step2/test_compare_1_1.c
Normal file
10
MiniC/TP04/tests/provided/step2/test_compare_1_1.c
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
println_bool(3 >= 2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 1
|
19
MiniC/TP04/tests/provided/step2/test_if2.c
Normal file
19
MiniC/TP04/tests/provided/step2/test_if2.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
if (10 == 10) {
|
||||||
|
println_int(12);
|
||||||
|
} else if (10 == 10) {
|
||||||
|
println_int(15);
|
||||||
|
} else {
|
||||||
|
println_int(13);
|
||||||
|
}
|
||||||
|
println_int(14);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 12
|
||||||
|
// 14
|
||||||
|
|
25
MiniC/TP04/tests/provided/step2/test_while1.c
Normal file
25
MiniC/TP04/tests/provided/step2/test_while1.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int n;
|
||||||
|
|
||||||
|
n = 9;
|
||||||
|
while (n > 0) {
|
||||||
|
n = n-1 ;
|
||||||
|
println_int(n) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 8
|
||||||
|
// 7
|
||||||
|
// 6
|
||||||
|
// 5
|
||||||
|
// 4
|
||||||
|
// 3
|
||||||
|
// 2
|
||||||
|
// 1
|
||||||
|
// 0
|
18
MiniC/TP04/tests/provided/test_while2b.c
Normal file
18
MiniC/TP04/tests/provided/test_while2b.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
int a,n;
|
||||||
|
|
||||||
|
n = 1;
|
||||||
|
a = 7;
|
||||||
|
while (n < a) {
|
||||||
|
n = n+1;
|
||||||
|
}
|
||||||
|
println_int(n);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 7
|
9
MiniC/TP04/tests/provided/unsupported/float.c
Normal file
9
MiniC/TP04/tests/provided/unsupported/float.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
int main() {
|
||||||
|
float f;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SKIP TEST EXPECTED
|
||||||
|
// EXITCODE 5
|
||||||
|
// EXPECTED
|
||||||
|
// Unsupported type float
|
9
MiniC/TP04/tests/provided/unsupported/print_float.c
Normal file
9
MiniC/TP04/tests/provided/unsupported/print_float.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
int main() {
|
||||||
|
println_float(0.0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SKIP TEST EXPECTED
|
||||||
|
// EXITCODE 5
|
||||||
|
// EXPECTED
|
||||||
|
// Unsupported type float
|
9
MiniC/TP04/tests/provided/unsupported/print_string.c
Normal file
9
MiniC/TP04/tests/provided/unsupported/print_string.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
int main() {
|
||||||
|
println_string("Hello");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SKIP TEST EXPECTED
|
||||||
|
// EXITCODE 5
|
||||||
|
// EXPECTED
|
||||||
|
// Unsupported type string
|
9
MiniC/TP04/tests/provided/unsupported/string.c
Normal file
9
MiniC/TP04/tests/provided/unsupported/string.c
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
int main() {
|
||||||
|
string b;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SKIP TEST EXPECTED
|
||||||
|
// EXITCODE 5
|
||||||
|
// EXPECTED
|
||||||
|
// Unsupported type string
|
70
MiniC/TP05/EnterSSA.py
Normal file
70
MiniC/TP05/EnterSSA.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
"""
|
||||||
|
CAP, SSA Intro, Elimination and Optimisations
|
||||||
|
Functions to convert a CFG into SSA Form.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Dict, Set
|
||||||
|
from Lib.CFG import Block, CFG
|
||||||
|
from Lib.Operands import Renamer
|
||||||
|
from Lib.Statement import Instruction
|
||||||
|
from Lib.PhiNode import PhiNode
|
||||||
|
from Lib.Dominators import computeDom, computeDT, computeDF
|
||||||
|
|
||||||
|
|
||||||
|
def insertPhis(cfg: CFG, DF: Dict[Block, Set[Block]]) -> None:
|
||||||
|
"""
|
||||||
|
`insertPhis(CFG, DF)` inserts phi nodes in `cfg` where needed.
|
||||||
|
At this point, phi nodes will look like `temp_x = φ(temp_x, ..., temp_x)`.
|
||||||
|
|
||||||
|
This is an helper function called during SSA entry.
|
||||||
|
"""
|
||||||
|
for var, defs in cfg.gather_defs().items():
|
||||||
|
has_phi: Set[Block] = set()
|
||||||
|
queue: List[Block] = list(defs)
|
||||||
|
while queue:
|
||||||
|
d = queue.pop(0)
|
||||||
|
for b in DF[d]:
|
||||||
|
if b not in has_phi:
|
||||||
|
# TODO add a phi node in block `b` (Lab 5a, Exercise 4)
|
||||||
|
raise NotImplementedError("insertPhis")
|
||||||
|
|
||||||
|
|
||||||
|
def rename_block(cfg: CFG, DT: Dict[Block, Set[Block]], renamer: Renamer, b: Block) -> None:
|
||||||
|
"""
|
||||||
|
Rename variables from block b.
|
||||||
|
|
||||||
|
This is an auxiliary function for `rename_variables`.
|
||||||
|
"""
|
||||||
|
renamer = renamer.copy()
|
||||||
|
for i in b.get_all_statements():
|
||||||
|
if isinstance(i, Instruction | PhiNode):
|
||||||
|
i.rename(renamer)
|
||||||
|
for succ in cfg.out_blocks(b):
|
||||||
|
for i in succ.get_phis():
|
||||||
|
assert (isinstance(i, PhiNode))
|
||||||
|
i.rename_from(renamer, b.get_label())
|
||||||
|
# TODO recursive call(s) of rename_block (Lab 5a, Exercise 5)
|
||||||
|
|
||||||
|
|
||||||
|
def rename_variables(cfg: CFG, DT: Dict[Block, Set[Block]]) -> None:
|
||||||
|
"""
|
||||||
|
Rename variables in the CFG, to transform `temp_x = φ(temp_x, ..., temp_x)`
|
||||||
|
into `temp_x = φ(temp_0, ... temp_n)`.
|
||||||
|
|
||||||
|
This is an helper function called during SSA entry.
|
||||||
|
"""
|
||||||
|
renamer = Renamer(cfg.fdata._pool)
|
||||||
|
# TODO initial call(s) to rename_block (Lab 5a, Exercise 5)
|
||||||
|
|
||||||
|
|
||||||
|
def enter_ssa(cfg: CFG, dom_graphs=False, basename="prog") -> None:
|
||||||
|
"""
|
||||||
|
Convert the CFG `cfg` into SSA Form:
|
||||||
|
compute the dominance frontier, then insert phi nodes and finally
|
||||||
|
rename variables accordingly.
|
||||||
|
|
||||||
|
`dom_graphs` indicates if we have to print the domination graphs.
|
||||||
|
`basename` is used for the names of the produced graphs.
|
||||||
|
"""
|
||||||
|
# TODO implement this function (Lab 5a, Exercise 2)
|
||||||
|
raise NotImplementedError("enter_ssa")
|
44
MiniC/TP05/ExitSSA.py
Normal file
44
MiniC/TP05/ExitSSA.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
"""
|
||||||
|
CAP, SSA Intro, Elimination and Optimisations
|
||||||
|
Functions to convert a CFG out of SSA Form.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import cast, List
|
||||||
|
from Lib import RiscV
|
||||||
|
from Lib.CFG import Block, BlockInstr, CFG
|
||||||
|
from Lib.Operands import Temporary
|
||||||
|
from Lib.Statement import AbsoluteJump
|
||||||
|
from Lib.Terminator import BranchingTerminator, Return
|
||||||
|
from Lib.PhiNode import PhiNode
|
||||||
|
from TP05.SequentializeMoves import sequentialize_moves
|
||||||
|
|
||||||
|
|
||||||
|
def generate_moves_from_phis(phis: List[PhiNode], parent: Block) -> List[BlockInstr]:
|
||||||
|
"""
|
||||||
|
`generate_moves_from_phis(phis, parent)` builds a list of move instructions
|
||||||
|
to be inserted in a new block between `parent` and the block with phi nodes
|
||||||
|
`phis`.
|
||||||
|
|
||||||
|
This is an helper function called during SSA exit.
|
||||||
|
"""
|
||||||
|
moves: List[BlockInstr] = []
|
||||||
|
# TODO compute 'moves', a list of 'mv' instructions to insert under parent
|
||||||
|
# (Lab 5a, Exercise 6)
|
||||||
|
return moves
|
||||||
|
|
||||||
|
|
||||||
|
def exit_ssa(cfg: CFG, is_smart: bool) -> None:
|
||||||
|
"""
|
||||||
|
`exit_ssa(cfg)` replaces phi nodes with move instructions to exit SSA form.
|
||||||
|
|
||||||
|
`is_smart` is set to true when smart register allocation is enabled (Lab 5b).
|
||||||
|
"""
|
||||||
|
for b in cfg.get_blocks():
|
||||||
|
phis = cast(List[PhiNode], b.get_phis()) # Use cast for Pyright
|
||||||
|
b.remove_all_phis() # Remove all phi nodes in the block
|
||||||
|
parents: List[Block] = b.get_in().copy() # Copy as we modify it by adding blocks
|
||||||
|
for parent in parents:
|
||||||
|
moves = generate_moves_from_phis(phis, parent)
|
||||||
|
# TODO Add the block containing 'moves' to 'cfg'
|
||||||
|
# and update edges and jumps accordingly (Lab 5a, Exercise 6)
|
||||||
|
raise NotImplementedError("exit_ssa")
|
99
MiniC/TP05/LivenessDataFlow.py
Normal file
99
MiniC/TP05/LivenessDataFlow.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
from typing import Dict, Set
|
||||||
|
from Lib.Operands import Operand, Temporary
|
||||||
|
from Lib.Statement import Statement, Instruction, regset_to_string
|
||||||
|
from Lib.CFG import CFG, Block
|
||||||
|
import copy
|
||||||
|
|
||||||
|
|
||||||
|
class LivenessDataFlow:
|
||||||
|
|
||||||
|
def __init__(self, function, debug=False):
|
||||||
|
self._function: CFG = function
|
||||||
|
self._debug = debug
|
||||||
|
# Live Operands at input and output of blocks
|
||||||
|
self._blockin: Dict[Block, Set[Operand]] = {}
|
||||||
|
self._blockout: Dict[Block, Set[Operand]] = {}
|
||||||
|
# Live Operands at outputs of instructions
|
||||||
|
self._liveout: Dict[tuple[Block, Statement], Set[Operand]] = {}
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.set_gen_kill()
|
||||||
|
if self._debug:
|
||||||
|
self.print_gen_kill()
|
||||||
|
|
||||||
|
self.run_dataflow_analysis()
|
||||||
|
if self._debug:
|
||||||
|
self.print_map_in_out()
|
||||||
|
|
||||||
|
self.fill_liveout()
|
||||||
|
|
||||||
|
def set_gen_kill(self):
|
||||||
|
"""Set the _gen and _kill field for each block in the function."""
|
||||||
|
for blk in self._function.get_blocks():
|
||||||
|
self.set_gen_kill_in_block(blk)
|
||||||
|
|
||||||
|
def set_gen_kill_in_block(self, block: Block) -> None:
|
||||||
|
gen = set()
|
||||||
|
kill = set()
|
||||||
|
for i in block.get_all_statements():
|
||||||
|
# Reminder: '|' is set union, '-' is subtraction.
|
||||||
|
raise NotImplementedError()
|
||||||
|
block._gen = gen
|
||||||
|
block._kill = kill
|
||||||
|
|
||||||
|
def run_dataflow_analysis(self):
|
||||||
|
"""Run the dataflow liveness analysis, i.e. compute self._blockin and
|
||||||
|
self._blockout based on the ._kill and ._gen fields of individual
|
||||||
|
instructions."""
|
||||||
|
if self._debug:
|
||||||
|
print("Dataflow Analysis")
|
||||||
|
# initialisation of all blockout,blockin sets, and def = kill
|
||||||
|
for blk in self._function.get_blocks():
|
||||||
|
self._blockin[blk] = set()
|
||||||
|
self._blockout[blk] = set()
|
||||||
|
stable = False
|
||||||
|
while not stable:
|
||||||
|
# Iterate until fixpoint :
|
||||||
|
# make calls to self._function.dataflow_one_step
|
||||||
|
stable = True # CHANGE
|
||||||
|
# TODO (lab5) ! (perform iterations until fixpoint).
|
||||||
|
|
||||||
|
def dataflow_one_step(self):
|
||||||
|
"""Run one iteration of the dataflow analysis. This function is meant
|
||||||
|
to be run iteratively until fixpoint."""
|
||||||
|
for blk in self._function.get_blocks():
|
||||||
|
self._blockout[blk] = set()
|
||||||
|
for child_label in blk.get_terminator().targets():
|
||||||
|
child = self._function.get_block(child_label)
|
||||||
|
self._blockout[blk] = self._blockout[blk] | self._blockin[child]
|
||||||
|
|
||||||
|
self._blockin[blk] = (self._blockout[blk] - blk._kill) | blk._gen
|
||||||
|
|
||||||
|
def fill_liveout(self):
|
||||||
|
"""Propagate the information from blockout/blockin inside the block
|
||||||
|
to get liveset instruction by instructions."""
|
||||||
|
for blk in self._function.get_blocks():
|
||||||
|
liveset = self._blockout[blk]
|
||||||
|
for instr in reversed(blk.get_all_statements()):
|
||||||
|
self._liveout[blk, instr] = liveset
|
||||||
|
liveset = (liveset - set(instr.defined())) | set(instr.used())
|
||||||
|
|
||||||
|
def print_gen_kill(self): # pragma: no cover
|
||||||
|
print("Dataflow Analysis, Initialisation")
|
||||||
|
for i, block in enumerate(self._function.get_blocks()):
|
||||||
|
print("block " + str(block._label), ":", i)
|
||||||
|
print("gen: " + regset_to_string(block._gen))
|
||||||
|
print("kill: " + regset_to_string(block._kill) + "\n")
|
||||||
|
|
||||||
|
def print_map_in_out(self): # pragma: no cover
|
||||||
|
"""Print in/out sets, useful for debug!"""
|
||||||
|
print("In: {" +
|
||||||
|
", ".join(str(x.get_label()) + ": "
|
||||||
|
+ regset_to_string(self._blockin[x])
|
||||||
|
for x in self._blockin.keys()) +
|
||||||
|
"}")
|
||||||
|
print("Out: {" +
|
||||||
|
", ".join(str(x.get_label()) + ": "
|
||||||
|
+ regset_to_string(self._blockout[x])
|
||||||
|
for x in self._blockout.keys()) +
|
||||||
|
"}")
|
81
MiniC/TP05/LivenessSSA.py
Normal file
81
MiniC/TP05/LivenessSSA.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
from typing import Dict, Set, Tuple
|
||||||
|
from Lib.Operands import Temporary
|
||||||
|
from Lib.Statement import Statement, regset_to_string
|
||||||
|
from Lib.CFG import Block, CFG
|
||||||
|
from Lib.PhiNode import PhiNode
|
||||||
|
|
||||||
|
|
||||||
|
class LivenessSSA:
|
||||||
|
"""Liveness Analysis on a CFG under SSA Form."""
|
||||||
|
|
||||||
|
def __init__(self, cfg: CFG, debug=False):
|
||||||
|
self._cfg: CFG = cfg
|
||||||
|
self._debug: bool = debug
|
||||||
|
# Temporary already propagated, by Block
|
||||||
|
self._seen: Dict[Block, Set[Temporary]] = dict()
|
||||||
|
# Live Temporary at outputs of Statement
|
||||||
|
self._liveout: Dict[tuple[Block, Statement], Set[Temporary]] = dict()
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
"""Compute the liveness: fill out self._seen and self._liveout."""
|
||||||
|
# Initialization
|
||||||
|
for block in self._cfg.get_blocks():
|
||||||
|
self._seen[block] = set()
|
||||||
|
for instr in block.get_all_statements():
|
||||||
|
self._liveout[block, instr] = set()
|
||||||
|
# Start the used-defined chains with backward propagation of liveness information
|
||||||
|
for var, uses in self.gather_uses().items():
|
||||||
|
for block, pos in uses:
|
||||||
|
self.livein_at_instruction(block, pos, var)
|
||||||
|
# Add conflicts on phis
|
||||||
|
self.conflict_on_phis()
|
||||||
|
# Final debugging print
|
||||||
|
if self._debug:
|
||||||
|
self.print_map_in_out()
|
||||||
|
|
||||||
|
def livein_at_instruction(self, block: Block, pos: int, var: Temporary) -> None:
|
||||||
|
"""Backward propagation of liveness information at the beginning of an instruction."""
|
||||||
|
raise NotImplementedError("LivenessSSA") # TODO (Lab 5b, Exercise 1)
|
||||||
|
|
||||||
|
def liveout_at_instruction(self, block: Block, pos: int, var: Temporary) -> None:
|
||||||
|
"""Backward propagation of liveness information at the end of an instruction."""
|
||||||
|
instr = block.get_all_statements()[pos]
|
||||||
|
raise NotImplementedError("LivenessSSA") # TODO (Lab 5b, Exercise 1)
|
||||||
|
|
||||||
|
def liveout_at_block(self, block: Block, var: Temporary) -> None:
|
||||||
|
"""Backward propagation of liveness information at the end of a block."""
|
||||||
|
raise NotImplementedError("LivenessSSA") # TODO (Lab 5b, Exercise 1)
|
||||||
|
|
||||||
|
def gather_uses(self) -> Dict[Temporary, Set[Tuple[Block, int]]]:
|
||||||
|
"""
|
||||||
|
Return a dictionnary giving for each variable the set of statements using it,
|
||||||
|
with a statement identified by its block and its position inside the latter.
|
||||||
|
Phi instructions are at the beginning of their block, while a Terminator is at
|
||||||
|
the last position of its block.
|
||||||
|
"""
|
||||||
|
uses: Dict[Temporary, Set[Tuple[Block, int]]] = dict()
|
||||||
|
for block in self._cfg.get_blocks():
|
||||||
|
for pos, instr in enumerate(block.get_all_statements()):
|
||||||
|
# Variables used by a statement `s` are `s.used()`
|
||||||
|
for var in instr.used():
|
||||||
|
if isinstance(var, Temporary):
|
||||||
|
# Already computed uses of the var if any, otherwise the empty set
|
||||||
|
var_uses = uses.get(var, set())
|
||||||
|
# Add for a statement its block and its position inside to the uses of var
|
||||||
|
uses[var] = var_uses.union({(block, pos)})
|
||||||
|
return uses
|
||||||
|
|
||||||
|
def conflict_on_phis(self) -> None:
|
||||||
|
"""Ensures that variables defined by φ instructions are in conflict with one-another."""
|
||||||
|
raise NotImplementedError("LivenessSSA") # TODO (Lab 5b, Exercise 1)
|
||||||
|
|
||||||
|
def print_map_in_out(self) -> None: # pragma: no cover
|
||||||
|
"""Print live out sets at each instruction, group by block, useful for debugging!"""
|
||||||
|
print("Liveout: [")
|
||||||
|
for block in self._cfg.get_blocks():
|
||||||
|
print("Block " + str(block.get_label()) + ": {\n "
|
||||||
|
+ ",\n ".join("\"{}\": {}"
|
||||||
|
.format(instr, regset_to_string(self._liveout[block, instr]))
|
||||||
|
for instr in block.get_all_statements()) +
|
||||||
|
"}")
|
||||||
|
print("]")
|
60
MiniC/TP05/SequentializeMoves.py
Normal file
60
MiniC/TP05/SequentializeMoves.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
"""
|
||||||
|
CAP, SSA Intro, Elimination and Optimisations
|
||||||
|
Helper functions to convert a CFG out of SSA Form
|
||||||
|
for the Smart Allocator.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Set, Tuple
|
||||||
|
from Lib import RiscV
|
||||||
|
from Lib.Graphes import DiGraph
|
||||||
|
from Lib.CFG import BlockInstr
|
||||||
|
from Lib.Operands import Register, Offset, DataLocation, S
|
||||||
|
|
||||||
|
|
||||||
|
def generate_smart_move(dest: DataLocation, src: DataLocation) -> List[BlockInstr]:
|
||||||
|
"""
|
||||||
|
Generate a list of move, store and load instructions, depending on
|
||||||
|
whether the operands are registers or memory locations.
|
||||||
|
This is an helper function for `sequentialize_moves`.
|
||||||
|
"""
|
||||||
|
instr: List[BlockInstr] = []
|
||||||
|
# TODO Compute the moves (Lab 5b, Exercise 4)
|
||||||
|
raise NotImplementedError("generate_smart_move")
|
||||||
|
return instr
|
||||||
|
|
||||||
|
|
||||||
|
def sequentialize_moves(parallel_moves: Set[Tuple[DataLocation, DataLocation]]
|
||||||
|
) -> List[BlockInstr]:
|
||||||
|
"""
|
||||||
|
Take a set of parallel moves represented as (destination, source) pairs,
|
||||||
|
and return a list of sequential moves which respect the cycles.
|
||||||
|
Use the register `tmp` S2 for the cycles.
|
||||||
|
Return a corresponding list of RiscV instructions.
|
||||||
|
This is an helper function called during SSA exit.
|
||||||
|
"""
|
||||||
|
tmp: Register = S[2] # S2 is not a general purpose register
|
||||||
|
# Build the graph of the moves
|
||||||
|
move_graph: DiGraph = DiGraph()
|
||||||
|
for dest, src in parallel_moves:
|
||||||
|
move_graph.add_edge((src, dest))
|
||||||
|
# List for the sequentialized moves to do
|
||||||
|
# Convention: in moves we put (dest, src) for each move
|
||||||
|
moves: List[Tuple[DataLocation, DataLocation]] = []
|
||||||
|
# First iteratively remove all the vetices without successors
|
||||||
|
vars_without_successor = {src
|
||||||
|
for src, dests in move_graph.neighbourhoods()
|
||||||
|
if len(dests) == 0}
|
||||||
|
while vars_without_successor:
|
||||||
|
# TODO Remove the leaves iteratively (Lab 5b, Exercise 4)
|
||||||
|
raise NotImplementedError("sequentialize_moves: leaves")
|
||||||
|
# Then handle the cycles
|
||||||
|
cycles: List = move_graph.connected_components()
|
||||||
|
for cycle in cycles:
|
||||||
|
# TODO Handle each cycle (Lab 5b, Exercise 4)
|
||||||
|
raise NotImplementedError("sequentialize_moves: cycles")
|
||||||
|
# Transform the moves to do in actual RiscV instructions
|
||||||
|
moves_instr: List[BlockInstr] = []
|
||||||
|
for dest, src in moves:
|
||||||
|
instrs = generate_smart_move(dest, src)
|
||||||
|
moves_instr.extend(instrs)
|
||||||
|
return moves_instr
|
99
MiniC/TP05/SmartAllocator.py
Normal file
99
MiniC/TP05/SmartAllocator.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
from typing import List, Dict
|
||||||
|
from Lib.Errors import MiniCInternalError
|
||||||
|
from Lib.Operands import Temporary, Operand, S, Offset, DataLocation, GP_REGS
|
||||||
|
from Lib.Statement import Instruction
|
||||||
|
from Lib.Allocator import Allocator
|
||||||
|
from Lib.FunctionData import FunctionData
|
||||||
|
from Lib import RiscV
|
||||||
|
from Lib.Graphes import Graph # For Graph coloring utility functions
|
||||||
|
|
||||||
|
|
||||||
|
class SmartAllocator(Allocator):
|
||||||
|
|
||||||
|
_igraph: Graph # interference graph
|
||||||
|
|
||||||
|
def __init__(self, fdata: FunctionData, basename: str, liveness,
|
||||||
|
debug=False, debug_graphs=False):
|
||||||
|
self._liveness = liveness
|
||||||
|
self._basename: str = basename
|
||||||
|
self._debug: bool = debug
|
||||||
|
self._debug_graphs: bool = debug_graphs
|
||||||
|
super().__init__(fdata)
|
||||||
|
|
||||||
|
def replace(self, old_instr: Instruction) -> List[Instruction]:
|
||||||
|
"""
|
||||||
|
Replace Temporary operands with the corresponding allocated
|
||||||
|
physical register (Register) OR memory location (Offset).
|
||||||
|
"""
|
||||||
|
before: List[Instruction] = []
|
||||||
|
after: List[Instruction] = []
|
||||||
|
new_args: List[Operand] = []
|
||||||
|
# TODO (lab5): Compute before, after, subst. This is similar to what
|
||||||
|
# TODO (lab5): replace from the Naive and AllInMem Allocators do (Lab 4).
|
||||||
|
raise NotImplementedError("Smart Replace (lab5)") # TODO
|
||||||
|
# And now return the new list!
|
||||||
|
instr = old_instr.with_args(new_args)
|
||||||
|
return before + [instr] + after
|
||||||
|
|
||||||
|
def prepare(self) -> None:
|
||||||
|
"""
|
||||||
|
Perform all preparatory steps related to smart register allocation:
|
||||||
|
|
||||||
|
- Dataflow analysis to compute the liveness range of each
|
||||||
|
temporary.
|
||||||
|
- Interference graph construction.
|
||||||
|
- Graph coloring.
|
||||||
|
- Associating temporaries with actual locations.
|
||||||
|
"""
|
||||||
|
# Liveness analysis
|
||||||
|
self._liveness.run()
|
||||||
|
# Interference graph
|
||||||
|
self.build_interference_graph()
|
||||||
|
if self._debug_graphs:
|
||||||
|
print("Printing the interference graph")
|
||||||
|
self._igraph.print_dot(self._basename + "interference.dot")
|
||||||
|
# Smart Allocation via graph coloring
|
||||||
|
self.smart_alloc()
|
||||||
|
|
||||||
|
def build_interference_graph(self) -> None:
|
||||||
|
"""
|
||||||
|
Build the interference graph (in self._igraph).
|
||||||
|
Vertices of the graph are temporaries,
|
||||||
|
and an edge exists between temporaries iff they are in conflict.
|
||||||
|
"""
|
||||||
|
self._igraph: Graph = Graph()
|
||||||
|
# Create a vertex for every temporary
|
||||||
|
# There may be temporaries the code does not use anymore,
|
||||||
|
# but it does not matter as they interfere with no one.
|
||||||
|
for v in self._fdata._pool.get_all_temps():
|
||||||
|
self._igraph.add_vertex(v)
|
||||||
|
# Iterate over self._liveness._liveout (dictionary containing all
|
||||||
|
# live out temporaries for each instruction), and for each conflict use
|
||||||
|
# self._igraph.add_edge((t1, t2)) to add the corresponding edge.
|
||||||
|
raise NotImplementedError("build_interference_graph (lab5)") # TODO
|
||||||
|
|
||||||
|
def smart_alloc(self) -> None:
|
||||||
|
"""
|
||||||
|
Allocates all temporaries via graph coloring.
|
||||||
|
Prints the colored graph if self._debug_graphs is True.
|
||||||
|
|
||||||
|
Precondition: the interference graph _igraph must have been built.
|
||||||
|
"""
|
||||||
|
# Checking the interference graph has been built
|
||||||
|
if not self._igraph:
|
||||||
|
raise MiniCInternalError("Empty interference graph in the Smart Allocator")
|
||||||
|
# Coloring of the interference graph
|
||||||
|
coloringreg: Dict[Temporary, int] = self._igraph.color()
|
||||||
|
if self._debug_graphs:
|
||||||
|
print("coloring = " + str(coloringreg))
|
||||||
|
self._igraph.print_dot(self._basename + "_colored.dot", coloringreg)
|
||||||
|
# Temporary -> DataLocation (Register or Offset) dictionary,
|
||||||
|
# specifying where a given Temporary should be allocated:
|
||||||
|
alloc_dict: Dict[Temporary, DataLocation] = dict()
|
||||||
|
# Use the coloring `coloringreg` to fill `alloc_dict`.
|
||||||
|
# Our version is less than 5 lines of code.
|
||||||
|
raise NotImplementedError("Allocation based on graph coloring (lab5)") # TODO
|
||||||
|
if self._debug:
|
||||||
|
print("Allocation:")
|
||||||
|
print(alloc_dict)
|
||||||
|
self._fdata._pool.set_temp_allocation(alloc_dict)
|
19
MiniC/TP06/tests/provided/basic-functions/bad_type_fun01.c
Normal file
19
MiniC/TP06/tests/provided/basic-functions/bad_type_fun01.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int x ;
|
||||||
|
x = f(41, 41);
|
||||||
|
println_int(x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function main: Line 11 col 6: wrong number of arguments in call to function f
|
20
MiniC/TP06/tests/provided/basic-functions/bad_type_fun02.c
Normal file
20
MiniC/TP06/tests/provided/basic-functions/bad_type_fun02.c
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int x ;
|
||||||
|
bool b;
|
||||||
|
b = true;
|
||||||
|
x = f(b);
|
||||||
|
println_int(x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function main: Line 13 col 6: type mismatch for method argument in call to function f: boolean and integer
|
19
MiniC/TP06/tests/provided/basic-functions/bad_type_fun03.c
Normal file
19
MiniC/TP06/tests/provided/basic-functions/bad_type_fun03.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
bool b;
|
||||||
|
b = f(12);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function main: Line 11 col 2: type mismatch for b: boolean and integer
|
||||||
|
|
19
MiniC/TP06/tests/provided/basic-functions/bad_type_fun04.c
Normal file
19
MiniC/TP06/tests/provided/basic-functions/bad_type_fun04.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x, bool b, int z);
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
bool b;
|
||||||
|
b = f(12,1,2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f(int x, bool b, int z){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function main: Line 7 col 6: type mismatch for method argument in call to function f: integer and boolean
|
19
MiniC/TP06/tests/provided/basic-functions/bad_type_fun05.c
Normal file
19
MiniC/TP06/tests/provided/basic-functions/bad_type_fun05.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x, bool b, int z);
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int b;
|
||||||
|
b = f(12,true,2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f(int x, bool b, int z){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function f: Line 11 col 0: type mismatch for return type of function f: integer and boolean
|
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x, bool x){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int x ;
|
||||||
|
x = f(41, 41);
|
||||||
|
println_int(x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function f: Line 3 col 13: Parameter x already defined
|
||||||
|
|
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
bool f(int x, bool b){
|
||||||
|
int x ;
|
||||||
|
x = 42;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int x ;
|
||||||
|
x = f(41, 41);
|
||||||
|
println_int(x);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function f: Line 4 col 2: Variable x already declared
|
||||||
|
|
@ -0,0 +1,21 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x, bool y);
|
||||||
|
|
||||||
|
int f(int x, int y){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
bool b;
|
||||||
|
b = f(12);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function f: Line 5 col 0: function f already declared with a different signature
|
@ -0,0 +1,23 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x, bool y);
|
||||||
|
|
||||||
|
int f(int x, bool y){
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f(int x, bool y){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
bool b;
|
||||||
|
b = f(12);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function f: Line 9 col 0: function f already defined
|
||||||
|
|
@ -0,0 +1,14 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
bool bbb(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8, bool b9){
|
||||||
|
return b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool main() {
|
||||||
|
bool dummy;
|
||||||
|
return dummy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function bbb: Line 3 col 0: function bbb declared with 10 > 8 arguments
|
@ -0,0 +1,17 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
bool bbb(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8, bool b9, bool b10);
|
||||||
|
|
||||||
|
bool bbb(bool b0, bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8, bool b9, bool b10){
|
||||||
|
return b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool main() {
|
||||||
|
bool dummy;
|
||||||
|
dummy = bbb(true, true, true, true, true, true, true, true, true, false);
|
||||||
|
return dummy;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function bbb: Line 3 col 0: function bbb declared with 11 > 8 arguments
|
5
MiniC/TP06/tests/provided/basic-functions/lib/_hello.c
Normal file
5
MiniC/TP06/tests/provided/basic-functions/lib/_hello.c
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int print_hello() {
|
||||||
|
printf("Hello, world!\n");
|
||||||
|
}
|
7
MiniC/TP06/tests/provided/basic-functions/no_def.c
Normal file
7
MiniC/TP06/tests/provided/basic-functions/no_def.c
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
int main() {
|
||||||
|
x = f(1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// EXITCODE 2
|
||||||
|
// EXPECTED
|
||||||
|
// In function main: Line 2 col 6: Undefined function f
|
13
MiniC/TP06/tests/provided/basic-functions/test_bool.c
Normal file
13
MiniC/TP06/tests/provided/basic-functions/test_bool.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
bool bbb(bool b){
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
bool dummy;
|
||||||
|
dummy = bbb(false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
int f() {return 42;}
|
||||||
|
|
||||||
|
int f();
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int dummy;
|
||||||
|
dummy = f();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// EXITCODE 0
|
13
MiniC/TP06/tests/provided/basic-functions/test_extern.c
Normal file
13
MiniC/TP06/tests/provided/basic-functions/test_extern.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Call to external functions
|
||||||
|
|
||||||
|
int print_hello();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int dummy;
|
||||||
|
dummy = print_hello();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// Hello, world!
|
||||||
|
// LINKARGS $dir/lib/_hello.c
|
13
MiniC/TP06/tests/provided/basic-functions/test_extern_asm.c
Normal file
13
MiniC/TP06/tests/provided/basic-functions/test_extern_asm.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// Call to external functions written in assembly
|
||||||
|
|
||||||
|
int print_42();
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int dummy;
|
||||||
|
dummy = print_42();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 42
|
||||||
|
// LINKARGS $dir/lib/_print42.s
|
19
MiniC/TP06/tests/provided/basic-functions/test_fun_call01.c
Normal file
19
MiniC/TP06/tests/provided/basic-functions/test_fun_call01.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Call of function with no parameter
|
||||||
|
|
||||||
|
int f(int x){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int val;
|
||||||
|
val = f(123);
|
||||||
|
println_int(val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 42
|
19
MiniC/TP06/tests/provided/basic-functions/test_fun_call02.c
Normal file
19
MiniC/TP06/tests/provided/basic-functions/test_fun_call02.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Call of function with a single parameter
|
||||||
|
|
||||||
|
int f(int x){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int val;
|
||||||
|
val = f(12);
|
||||||
|
println_int(val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 42
|
17
MiniC/TP06/tests/provided/basic-functions/test_fun_call03.c
Normal file
17
MiniC/TP06/tests/provided/basic-functions/test_fun_call03.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Call of function with two parameters
|
||||||
|
|
||||||
|
int f(int x, int y){
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int val;
|
||||||
|
val = f(12, 30);
|
||||||
|
println_int(val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 42
|
17
MiniC/TP06/tests/provided/basic-functions/test_fun_call04.c
Normal file
17
MiniC/TP06/tests/provided/basic-functions/test_fun_call04.c
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Call of function with two parameters
|
||||||
|
|
||||||
|
int f(int x, int y){
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int val;
|
||||||
|
val = f(12, 31) - f(13,-12);
|
||||||
|
println_int(val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 42
|
11
MiniC/TP06/tests/provided/basic-functions/test_fun_decl01.c
Normal file
11
MiniC/TP06/tests/provided/basic-functions/test_fun_decl01.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Forward declaration of function with no parameter
|
||||||
|
|
||||||
|
int f();
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
11
MiniC/TP06/tests/provided/basic-functions/test_fun_decl02.c
Normal file
11
MiniC/TP06/tests/provided/basic-functions/test_fun_decl02.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Forward declaration of function with a single parameter
|
||||||
|
|
||||||
|
int f(int x);
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
11
MiniC/TP06/tests/provided/basic-functions/test_fun_decl03.c
Normal file
11
MiniC/TP06/tests/provided/basic-functions/test_fun_decl03.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Forward declaration of function with two parameters
|
||||||
|
|
||||||
|
int f(int x, bool y);
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
15
MiniC/TP06/tests/provided/basic-functions/test_fun_def01.c
Normal file
15
MiniC/TP06/tests/provided/basic-functions/test_fun_def01.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Definition of function with a single parameter
|
||||||
|
|
||||||
|
int f(int x){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
12
MiniC/TP06/tests/provided/basic-functions/test_fun_def02.c
Normal file
12
MiniC/TP06/tests/provided/basic-functions/test_fun_def02.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Declaration of a function with two parameters
|
||||||
|
int f(int x, bool y) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
15
MiniC/TP06/tests/provided/basic-functions/test_fun_expr.c
Normal file
15
MiniC/TP06/tests/provided/basic-functions/test_fun_expr.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int f(int x){
|
||||||
|
return x + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int dummy;
|
||||||
|
dummy = f(f(10) + f(100));
|
||||||
|
println_int(dummy);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 113
|
@ -0,0 +1,20 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Definition of function with a single parameter
|
||||||
|
// with forward declaration, i.e. declaration here and definition after main
|
||||||
|
int f(int x);
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
int z;
|
||||||
|
z = f(0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int f(int x){
|
||||||
|
int y ;
|
||||||
|
y = 42;
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// EXPECTED
|
10
MiniC/TP06/tests/provided/basic-functions/test_fun_ret01.c
Normal file
10
MiniC/TP06/tests/provided/basic-functions/test_fun_ret01.c
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
// Return statememnt with expression
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
return 1 + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// EXECCODE 3
|
427
MiniC/TPoptim/OptimSSA.py
Normal file
427
MiniC/TPoptim/OptimSSA.py
Normal file
@ -0,0 +1,427 @@
|
|||||||
|
"""
|
||||||
|
CAP, SSA Intro, Elimination and Optimisations
|
||||||
|
Optimisations on SSA.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from enum import Enum
|
||||||
|
from typing import List, Dict, Tuple, cast
|
||||||
|
from Lib.Errors import MiniCInternalError
|
||||||
|
from Lib.Operands import (Operand, Temporary, Immediate, A, ZERO)
|
||||||
|
from Lib.Statement import (Statement, Instruction, Label, AbsoluteJump)
|
||||||
|
from Lib.CFG import (BlockInstr, Terminator, Block, CFG)
|
||||||
|
from Lib.Terminator import (Return, BranchingTerminator)
|
||||||
|
from Lib.PhiNode import PhiNode
|
||||||
|
from Lib import RiscV
|
||||||
|
|
||||||
|
|
||||||
|
def div_rd_0(a: int, b: int) -> int:
|
||||||
|
"""Division rounded towards 0 (integer division in Python rounds down)."""
|
||||||
|
return -(-a // b) if (a < 0) ^ (b < 0) else a // b
|
||||||
|
|
||||||
|
|
||||||
|
def mod_rd_0(a: int, b: int) -> int:
|
||||||
|
"""Modulo rounded towards 0 (integer division in Python rounds down)."""
|
||||||
|
return -(-a % b) if (a < 0) ^ (b < 0) else a % b
|
||||||
|
|
||||||
|
|
||||||
|
class Lattice(Enum):
|
||||||
|
Bottom = 0
|
||||||
|
Top = 1
|
||||||
|
|
||||||
|
|
||||||
|
LATTICE_VALUE = int | Lattice # Type for our values: Bottom < int < Top
|
||||||
|
|
||||||
|
|
||||||
|
def join(v1: LATTICE_VALUE, v2: LATTICE_VALUE) -> LATTICE_VALUE:
|
||||||
|
"""Compute the join of the two lattice values."""
|
||||||
|
match v1, v2:
|
||||||
|
case Lattice.Top, _:
|
||||||
|
return Lattice.Top
|
||||||
|
case _, Lattice.Top:
|
||||||
|
return Lattice.Top
|
||||||
|
case Lattice.Bottom, _:
|
||||||
|
return v2
|
||||||
|
case _, Lattice.Bottom:
|
||||||
|
return v1
|
||||||
|
case _, _: # both int
|
||||||
|
if v1 == v2:
|
||||||
|
return v1
|
||||||
|
else:
|
||||||
|
return Lattice.Top
|
||||||
|
|
||||||
|
|
||||||
|
def joinl(values: List[LATTICE_VALUE]) -> LATTICE_VALUE:
|
||||||
|
"""Compute the join of the list of lattice values."""
|
||||||
|
res = Lattice.Bottom
|
||||||
|
for v in values:
|
||||||
|
res = join(res, v)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
class CondConstantPropagation:
|
||||||
|
"""
|
||||||
|
Class that optimises a CFG under SSA form
|
||||||
|
following the algorithm "Sparse Conditionnal Constant Propagation".
|
||||||
|
"""
|
||||||
|
|
||||||
|
cfg: CFG
|
||||||
|
# CFG under SSA form to optimise
|
||||||
|
|
||||||
|
valueness: Dict[Operand, LATTICE_VALUE]
|
||||||
|
# Values of each variable v:
|
||||||
|
# valueness[v] = Lattice.Bottom if no evidence that v is assigned
|
||||||
|
# valueness[v] = n if we found evidence that only n is assigned to v
|
||||||
|
# valueness[v] = Lattice.Top if we found evidence that v is assigned
|
||||||
|
# to at least two different values
|
||||||
|
|
||||||
|
executability: Dict[Tuple[Block | None, Block], bool]
|
||||||
|
# Exectuability of an edge (B, C):
|
||||||
|
# executability[B, C] = False if no evidence that the edge (B, C) can ever be executed
|
||||||
|
# executability[B, C] = True if (B, C) may be executed (over-approximation)
|
||||||
|
# There is an initial edge from None to the start block
|
||||||
|
|
||||||
|
modified_flag: bool
|
||||||
|
# Flag to check if we reach the fixpoint
|
||||||
|
|
||||||
|
debug: bool
|
||||||
|
# Print valueness and executability at each step if True
|
||||||
|
|
||||||
|
all_vars: List[Operand]
|
||||||
|
# All the variables of the CFG
|
||||||
|
|
||||||
|
all_blocks: List[Block]
|
||||||
|
# All the blocks of the CFG
|
||||||
|
|
||||||
|
def __init__(self, cfg: CFG, debug: bool):
|
||||||
|
self.cfg = cfg
|
||||||
|
self.valueness = dict()
|
||||||
|
self.executability = dict()
|
||||||
|
self.debug = debug
|
||||||
|
self.all_vars = list(cfg.gather_defs().keys())
|
||||||
|
self.all_blocks = cfg.get_blocks()
|
||||||
|
|
||||||
|
# Initialisation of valueness and executability
|
||||||
|
for var in self.all_vars:
|
||||||
|
self.valueness[var] = Lattice.Bottom
|
||||||
|
for block in self.all_blocks:
|
||||||
|
for succ in cfg.out_blocks(block):
|
||||||
|
self.executability[block, succ] = False
|
||||||
|
# Add an initial edge from None to the start block
|
||||||
|
start_blk = self.cfg.get_block(self.cfg.get_start())
|
||||||
|
self.executability[None, start_blk] = False
|
||||||
|
|
||||||
|
def dump(self) -> None: # pragma: no cover
|
||||||
|
"""
|
||||||
|
For debug purposes: print valueness and executability.
|
||||||
|
"""
|
||||||
|
print("Valueness:")
|
||||||
|
for x, v in self.valueness.items():
|
||||||
|
print("{0}: {1}".format(x, v))
|
||||||
|
print("Executability:")
|
||||||
|
for (B, C), v in self.executability.items():
|
||||||
|
print("{0} -> {1}: {2}".format(B.get_label() if B is not None else "",
|
||||||
|
C.get_label(), v))
|
||||||
|
|
||||||
|
def set_valueness(self, v: Operand, x: LATTICE_VALUE) -> None:
|
||||||
|
"""
|
||||||
|
Update the valueness of a variable `v` by performing a join with
|
||||||
|
its current value.
|
||||||
|
"""
|
||||||
|
old_x = self.valueness[v]
|
||||||
|
new_x = join(x, old_x)
|
||||||
|
if new_x != old_x:
|
||||||
|
self.modified_flag = True
|
||||||
|
self.valueness[v] = new_x
|
||||||
|
|
||||||
|
def set_executability(self, B: Block | None, C: Block) -> None:
|
||||||
|
"""
|
||||||
|
Mark the edge from `B` to `C` as executable.
|
||||||
|
"""
|
||||||
|
old_x = self.executability[B, C]
|
||||||
|
if not old_x:
|
||||||
|
self.modified_flag = True
|
||||||
|
self.executability[B, C] = True
|
||||||
|
|
||||||
|
def is_constant(self, op: Operand) -> bool:
|
||||||
|
"""True if the value of `op` is constant."""
|
||||||
|
return isinstance(self.valueness.get(op, None), int)
|
||||||
|
|
||||||
|
def is_executable(self, B: Block) -> bool:
|
||||||
|
"""True if the block `B` may be executed."""
|
||||||
|
return B in (C for ((_, C), b) in self.executability.items() if b)
|
||||||
|
|
||||||
|
def compute(self) -> None:
|
||||||
|
"""
|
||||||
|
Compute executability for all edges and valueness for all variables
|
||||||
|
using a fixpoint algorithm.
|
||||||
|
"""
|
||||||
|
# 1. For any v coming from outside the CFG (parameters, function calls),
|
||||||
|
# set valueness[v] = Top. These are exactly the registers of A.
|
||||||
|
for var in A:
|
||||||
|
self.valueness[var] = Lattice.Top
|
||||||
|
|
||||||
|
# 2. The start block is executable, with an initial edge coming from None.
|
||||||
|
start_blk = self.cfg.get_block(self.cfg.get_start())
|
||||||
|
self.set_executability(None, start_blk)
|
||||||
|
|
||||||
|
# Start the fixpoint.
|
||||||
|
self.modified_flag = True
|
||||||
|
while self.modified_flag:
|
||||||
|
# Whenever executability or valueness is modified,
|
||||||
|
# modified_flag is set to True (see set_executability and set_valueness)
|
||||||
|
# so that the fixpoint continues.
|
||||||
|
self.modified_flag = False
|
||||||
|
if self.debug:
|
||||||
|
self.dump()
|
||||||
|
|
||||||
|
# 3. For any executable block B with only one successor C,
|
||||||
|
# set executability[B, C] = True.
|
||||||
|
for B in self.all_blocks:
|
||||||
|
nexts = self.cfg.out_blocks(B)
|
||||||
|
if self.is_executable(B) and len(nexts) == 1:
|
||||||
|
C = nexts[0]
|
||||||
|
self.set_executability(B, C)
|
||||||
|
|
||||||
|
for B in self.all_blocks:
|
||||||
|
if self.is_executable(B):
|
||||||
|
for stat in B.get_all_statements():
|
||||||
|
self.propagate_in(B, stat)
|
||||||
|
|
||||||
|
def propagate_in(self, B: Block, stat: Statement) -> None:
|
||||||
|
"""
|
||||||
|
Propagate valueness and executability to the given statement `stat`
|
||||||
|
located in the given executable block `B`.
|
||||||
|
See the `compute` function for more context.
|
||||||
|
"""
|
||||||
|
# 4. For any executable assignment v <- op (x, y),
|
||||||
|
# set valueness[v] = eval (op, x, y)
|
||||||
|
# TODO (Exercise 4)
|
||||||
|
|
||||||
|
# 5. For any executable assignment v <- phi (x1, ..., xn),
|
||||||
|
# set valueness[v] = join(x1, .., xn)
|
||||||
|
# TODO (Exercise 6)
|
||||||
|
|
||||||
|
# 6. For any executable conditional branch to blocks B1 and B2,
|
||||||
|
# set executability[B1] = True and/or executability[B2] = True
|
||||||
|
# depending on the valueness of its condition
|
||||||
|
# TODO (Exercise 6)
|
||||||
|
|
||||||
|
def get_executable_srcs(self, B: Block, phi: PhiNode) -> List[Operand]:
|
||||||
|
"""
|
||||||
|
Given a phi node `phi` belonging to the block `B`,
|
||||||
|
return its operands coming from an executable edge.
|
||||||
|
"""
|
||||||
|
return [x for lbl, x in phi.get_srcs().items()
|
||||||
|
if self.executability[self.cfg.get_block(lbl), B]]
|
||||||
|
|
||||||
|
def get_operands(self, ins: Instruction) -> List[LATTICE_VALUE]:
|
||||||
|
"""
|
||||||
|
Returns the valueness of the operands of the given instruction `ins`.
|
||||||
|
Also takes into account immediate values and the zero register.
|
||||||
|
"""
|
||||||
|
args: List[LATTICE_VALUE] = []
|
||||||
|
for x in ins.used():
|
||||||
|
if isinstance(x, Temporary):
|
||||||
|
args.append(self.valueness[x])
|
||||||
|
elif isinstance(x, Immediate):
|
||||||
|
args.append(x._val)
|
||||||
|
elif (x == ZERO):
|
||||||
|
args.append(0)
|
||||||
|
elif isinstance(x, Label):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
args.append(Lattice.Top)
|
||||||
|
return args
|
||||||
|
|
||||||
|
def eval_arith_instr(self, ins: Instruction) -> LATTICE_VALUE:
|
||||||
|
"""
|
||||||
|
Computes the result of an arithmetic instruction in the valueness lattice,
|
||||||
|
from the valueness of its operands.
|
||||||
|
"""
|
||||||
|
args = self.get_operands(ins)
|
||||||
|
name: str = ins.ins
|
||||||
|
|
||||||
|
if Lattice.Top in args:
|
||||||
|
return Lattice.Top
|
||||||
|
elif Lattice.Bottom in args:
|
||||||
|
return Lattice.Bottom
|
||||||
|
|
||||||
|
args = cast(List[int], args)
|
||||||
|
if name == "add" or name == "addi":
|
||||||
|
return args[0] + args[1]
|
||||||
|
elif name == "mul":
|
||||||
|
return args[0] * args[1]
|
||||||
|
elif name == "div":
|
||||||
|
return div_rd_0(args[0], args[1])
|
||||||
|
elif name == "rem":
|
||||||
|
return mod_rd_0(args[0], args[1])
|
||||||
|
elif name == "sub":
|
||||||
|
return args[0] - args[1]
|
||||||
|
elif name == "and":
|
||||||
|
return args[0] & args[1]
|
||||||
|
elif name == "or":
|
||||||
|
return args[0] | args[1]
|
||||||
|
elif name == "xor" or name == "xori":
|
||||||
|
return args[0] ^ args[1]
|
||||||
|
elif name == "li":
|
||||||
|
assert (isinstance(ins.used()[0], Immediate))
|
||||||
|
return args[0]
|
||||||
|
elif name == "mv":
|
||||||
|
return args[0]
|
||||||
|
|
||||||
|
raise MiniCInternalError("Instruction modifying a temporary not in\
|
||||||
|
['add', 'addi', 'mul', 'div', 'rem',\
|
||||||
|
'sub', 'and', 'or', 'xor', 'xori', 'li', 'mv']")
|
||||||
|
|
||||||
|
def eval_bool_instr(self, ins: BranchingTerminator) -> LATTICE_VALUE:
|
||||||
|
"""
|
||||||
|
Computes the result of the comparison of a branching instruction
|
||||||
|
in the valueness lattice, from the valueness of its operands.
|
||||||
|
"""
|
||||||
|
args = self.get_operands(ins)
|
||||||
|
name: str = ins.ins
|
||||||
|
|
||||||
|
if Lattice.Top in args:
|
||||||
|
return Lattice.Top
|
||||||
|
elif Lattice.Bottom in args:
|
||||||
|
return Lattice.Bottom
|
||||||
|
|
||||||
|
args = cast(List[int], args)
|
||||||
|
if name == "blt":
|
||||||
|
return args[0] < args[1]
|
||||||
|
elif name == "bgt":
|
||||||
|
return args[0] > args[1]
|
||||||
|
elif name == "beq":
|
||||||
|
return args[0] == args[1]
|
||||||
|
elif name == "bne":
|
||||||
|
return args[0] != args[1]
|
||||||
|
elif name == "ble":
|
||||||
|
return args[0] <= args[1]
|
||||||
|
elif name == "bge":
|
||||||
|
return args[0] >= args[1]
|
||||||
|
elif name == "beqz":
|
||||||
|
return args[0] == 0
|
||||||
|
elif name == "bnez":
|
||||||
|
return args[0] != 0
|
||||||
|
|
||||||
|
raise MiniCInternalError("Condition of a CondJump not in ['blt',\
|
||||||
|
'bgt', 'beq', 'bne', 'ble', 'bge',\
|
||||||
|
'beqz', 'bnez']")
|
||||||
|
|
||||||
|
def replacePhi(self, B: Block, ins: PhiNode) -> PhiNode:
|
||||||
|
"""
|
||||||
|
Replace a phi node that has constant operands
|
||||||
|
according to the valueness computation.
|
||||||
|
"""
|
||||||
|
to_remove: List[Label] = [] # List of block's labels with no executable edge to B
|
||||||
|
for Bi_label, xi in ins.get_srcs().items():
|
||||||
|
Bi = self.cfg.get_block(Bi_label)
|
||||||
|
if self.executability[Bi, B]:
|
||||||
|
if self.is_constant(xi):
|
||||||
|
# Add a LI instruction in the block from where xi comes,
|
||||||
|
# at the end of its body (i.e. just before its Terminator),
|
||||||
|
# and replace xi by this new temporary
|
||||||
|
new_xi = self.cfg.fdata.fresh_tmp()
|
||||||
|
ins.srcs[Bi_label] = new_xi
|
||||||
|
imm = Immediate(self.valueness[xi])
|
||||||
|
li_ins = RiscV.li(new_xi, imm)
|
||||||
|
Bi.add_instruction(li_ins)
|
||||||
|
else:
|
||||||
|
to_remove.append(Bi_label)
|
||||||
|
for Bi_label in to_remove:
|
||||||
|
del ins.srcs[Bi_label]
|
||||||
|
return ins
|
||||||
|
|
||||||
|
def replaceInstruction(self, ins: BlockInstr) -> List[BlockInstr]:
|
||||||
|
"""
|
||||||
|
Replace an instruction that has constant operands
|
||||||
|
according to the valueness computation.
|
||||||
|
"""
|
||||||
|
# Add some LI instructions before the instruction `ins`
|
||||||
|
li_instrs: List[BlockInstr] = []
|
||||||
|
# Replace the constant variables with the temporaries defined by the new LI instructions
|
||||||
|
subst: Dict[Operand, Operand] = {}
|
||||||
|
|
||||||
|
# Compute `li_instrs` and `subst`
|
||||||
|
# TODO (Exercise 5)
|
||||||
|
|
||||||
|
new_ins = ins.substitute(subst)
|
||||||
|
return li_instrs + [new_ins]
|
||||||
|
|
||||||
|
def replaceTerminator(self, ins: Terminator) -> Tuple[List[BlockInstr], Terminator]:
|
||||||
|
"""
|
||||||
|
Replace a terminator that has constant operands
|
||||||
|
according to the valueness computation.
|
||||||
|
Return the list of LI instructions to do before,
|
||||||
|
and the new terminator.
|
||||||
|
"""
|
||||||
|
# Add some LI instructions at the end of the body of the block
|
||||||
|
li_instrs: List[BlockInstr] = []
|
||||||
|
# Replace the constant variables with the temporaries defined by the new LI instructions
|
||||||
|
subst: Dict[Operand, Operand] = {}
|
||||||
|
|
||||||
|
# Compute `li_instrs` and `subst`
|
||||||
|
# TODO (Exercise 5)
|
||||||
|
|
||||||
|
new_ins = ins.substitute(subst)
|
||||||
|
return li_instrs, new_ins
|
||||||
|
|
||||||
|
def rewriteCFG(self) -> None:
|
||||||
|
"""Update the CFG."""
|
||||||
|
# a. Whenever executability[B, C] = False, delete this edge
|
||||||
|
for (B, C) in [(B, C) for ((B, C), b) in self.executability.items()
|
||||||
|
if not b and B is not None]:
|
||||||
|
# Remove the edge
|
||||||
|
self.cfg.remove_edge(B, C)
|
||||||
|
# Update the corresponding terminator
|
||||||
|
targets = B.get_terminator().targets()
|
||||||
|
targets.remove(C.get_label())
|
||||||
|
if len(targets) == 0:
|
||||||
|
B.set_terminator(Return())
|
||||||
|
elif len(targets) == 1:
|
||||||
|
B.set_terminator(AbsoluteJump(targets[0]))
|
||||||
|
else:
|
||||||
|
raise MiniCInternalError(
|
||||||
|
"rewriteCFG: A terminator has more than 2 targets: {}"
|
||||||
|
.format(targets + [C.get_label()]))
|
||||||
|
# b. Whenever valueness[x] = c, substitute c for x and delete assignment to x
|
||||||
|
for block in self.all_blocks:
|
||||||
|
if self.is_executable(block):
|
||||||
|
new_phis: List[PhiNode] = []
|
||||||
|
for ins in block.get_phis():
|
||||||
|
assert (isinstance(ins, PhiNode))
|
||||||
|
v = ins.defined()[0]
|
||||||
|
if self.is_constant(v):
|
||||||
|
# We do not keep instructions defining operands of constant values
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
new_phis.append(self.replacePhi(block, ins))
|
||||||
|
new_instrs: List[BlockInstr] = []
|
||||||
|
for ins in block.get_body():
|
||||||
|
defs = ins.defined()
|
||||||
|
if len(defs) == 1 and self.is_constant(defs[0]):
|
||||||
|
# We do not keep instructions defining operands of constant values
|
||||||
|
continue
|
||||||
|
elif isinstance(ins, Instruction):
|
||||||
|
# We replace others instructions
|
||||||
|
new_instrs.extend(self.replaceInstruction(ins))
|
||||||
|
else:
|
||||||
|
# We do nothing for comments
|
||||||
|
new_instrs.append(ins)
|
||||||
|
term_instrs, new_term = self.replaceTerminator(block.get_terminator())
|
||||||
|
block.set_phis(cast(List[Statement], new_phis))
|
||||||
|
block._instructions = new_instrs + term_instrs
|
||||||
|
block.set_terminator(new_term)
|
||||||
|
# c. Whenever a block B is not executable, delete B
|
||||||
|
# There are no edge implicating B, for such an edge would not be
|
||||||
|
# executable, whence would have been deleted beforehand
|
||||||
|
for block in self.all_blocks:
|
||||||
|
if not self.is_executable(block):
|
||||||
|
del self.cfg._blocks[block.get_label()]
|
||||||
|
|
||||||
|
|
||||||
|
def OptimSSA(cfg: CFG, debug: bool) -> None:
|
||||||
|
"""Optimise a CFG under SSA form."""
|
||||||
|
optim = CondConstantPropagation(cfg, debug)
|
||||||
|
optim.compute()
|
||||||
|
optim.rewriteCFG()
|
23
MiniC/TPoptim/tests/provided/example_lecture.c
Normal file
23
MiniC/TPoptim/tests/provided/example_lecture.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int i, j, k;
|
||||||
|
i = 1;
|
||||||
|
j = 1;
|
||||||
|
k = 0;
|
||||||
|
while (k < 100) {
|
||||||
|
if (j < 20) {
|
||||||
|
j = i;
|
||||||
|
k = k+1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
j = k;
|
||||||
|
k = k+2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println_int(j);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 1
|
12
MiniC/TPoptim/tests/provided/no_loop_constant.c
Normal file
12
MiniC/TPoptim/tests/provided/no_loop_constant.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#include "printlib.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int i, j;
|
||||||
|
i = 1;
|
||||||
|
j = i;
|
||||||
|
println_int(j);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECTED
|
||||||
|
// 1
|
157
MiniC/test_codegen.py
Executable file
157
MiniC/test_codegen.py
Executable file
@ -0,0 +1,157 @@
|
|||||||
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
import glob
|
||||||
|
from test_expect_pragma import (
|
||||||
|
TestExpectPragmas, TestCompiler,
|
||||||
|
cat
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Usage:
|
||||||
|
python3 test_codegen.py
|
||||||
|
(or make test)
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
MIF08 and CAP, 2019
|
||||||
|
Unit test infrastructure for testing code generation:
|
||||||
|
1) compare the actual output to the expected one (in comments)
|
||||||
|
2) compare the actual output to the one obtained by simulation
|
||||||
|
3) for different allocation algorithms
|
||||||
|
"""
|
||||||
|
|
||||||
|
MINICC_OPTS = []
|
||||||
|
if "MINICC_OPTS" in os.environ and os.environ["MINICC_OPTS"]:
|
||||||
|
MINICC_OPTS = os.environ["MINICC_OPTS"].split()
|
||||||
|
else:
|
||||||
|
MINICC_OPTS = ["--mode=codegen-cfg"]
|
||||||
|
|
||||||
|
DISABLE_TYPECHECK = "--disable-typecheck" in MINICC_OPTS \
|
||||||
|
or "--mode=parse" in MINICC_OPTS or "parse" in MINICC_OPTS
|
||||||
|
DISABLE_CODEGEN = "--mode=parse" in MINICC_OPTS or "--mode=typecheck" in MINICC_OPTS \
|
||||||
|
or "parse" in MINICC_OPTS or "typecheck" in MINICC_OPTS
|
||||||
|
|
||||||
|
HERE = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
if HERE == os.path.realpath('.'):
|
||||||
|
HERE = '.'
|
||||||
|
TEST_DIR = HERE
|
||||||
|
IMPLEM_DIR = HERE
|
||||||
|
SKIP_EXPECT = False
|
||||||
|
if 'SKIP_EXPECT' in os.environ:
|
||||||
|
SKIP_EXPECT = True
|
||||||
|
|
||||||
|
MINIC_COMPILE = os.path.join(IMPLEM_DIR, 'MiniCC.py')
|
||||||
|
|
||||||
|
ALL_FILES = []
|
||||||
|
# tests for typing AND evaluation
|
||||||
|
ALL_FILES += glob.glob(os.path.join(TEST_DIR, 'TP04/tests/**/[a-zA-Z]*.c'),
|
||||||
|
recursive=True)
|
||||||
|
|
||||||
|
|
||||||
|
ALLOC_FILES = glob.glob(os.path.join(HERE, 'TP05/tests/**/*.c'), recursive=True)
|
||||||
|
|
||||||
|
SKIP_NOT_IMPLEMENTED = False
|
||||||
|
if 'SKIP_NOT_IMPLEMENTED' in os.environ:
|
||||||
|
SKIP_NOT_IMPLEMENTED = True
|
||||||
|
|
||||||
|
if 'TEST_FILES' in os.environ:
|
||||||
|
ALL_FILES = glob.glob(os.environ['TEST_FILES'], recursive=True)
|
||||||
|
|
||||||
|
MINIC_EVAL = os.path.join(
|
||||||
|
HERE, '..', '..', 'TP03', 'MiniC-type-interpret', 'Main.py')
|
||||||
|
|
||||||
|
# if 'COMPIL_MINIC_EVAL' in os.environ:
|
||||||
|
# MINIC_EVAL = os.environ['COMPIL_MINIC_EVAL']
|
||||||
|
# else:
|
||||||
|
# MINIC_EVAL = os.path.join(
|
||||||
|
# HERE, '..', '..', 'TP03', 'MiniC-type-interpret', 'Main.py')
|
||||||
|
|
||||||
|
if 'TEST_FILES' in os.environ:
|
||||||
|
ALLOC_FILES = ALL_FILES
|
||||||
|
ALL_IN_MEM_FILES = ALL_FILES
|
||||||
|
|
||||||
|
ALL_IN_MEM_FILES = list(set(ALL_FILES) | set(ALLOC_FILES))
|
||||||
|
if 'FILTER' in os.environ:
|
||||||
|
FILTER_FILES = glob.glob(os.path.join(HERE, os.environ['FILTER']), recursive=True)
|
||||||
|
ALL_FILES = list(set(FILTER_FILES) & set(ALL_FILES))
|
||||||
|
ALLOC_FILES = list(set(FILTER_FILES) & set(ALLOC_FILES))
|
||||||
|
ALL_IN_MEM_FILES = list(set(FILTER_FILES) & set(ALL_IN_MEM_FILES))
|
||||||
|
|
||||||
|
# Avoid duplicates
|
||||||
|
ALL_IN_MEM_FILES.sort()
|
||||||
|
ALL_FILES = list(set(ALL_FILES))
|
||||||
|
ALL_FILES.sort()
|
||||||
|
|
||||||
|
class TestCodeGen(TestExpectPragmas, TestCompiler):
|
||||||
|
DISABLE_CODEGEN = DISABLE_CODEGEN
|
||||||
|
SKIP_NOT_IMPLEMENTED = SKIP_NOT_IMPLEMENTED
|
||||||
|
MINIC_COMPILE = MINIC_COMPILE
|
||||||
|
MINICC_OPTS = MINICC_OPTS
|
||||||
|
|
||||||
|
# Not in test_expect_pragma to get assertion rewritting
|
||||||
|
def assert_equal(self, actual, expected, compiler):
|
||||||
|
if DISABLE_CODEGEN and expected.exitcode in (0, 5):
|
||||||
|
# Compiler does not fail => no output expected
|
||||||
|
assert actual.output == "", \
|
||||||
|
"Compiler unexpectedly generated some output with codegen disabled"
|
||||||
|
assert actual.exitcode == 0, \
|
||||||
|
"Compiler unexpectedly failed with codegen disabled"
|
||||||
|
return
|
||||||
|
if DISABLE_TYPECHECK and expected.exitcode != 0:
|
||||||
|
# Test should fail at typecheck, and we don't do
|
||||||
|
# typechecking => nothing to check.
|
||||||
|
pytest.skip("Test that doesn't typecheck with --disable-typecheck")
|
||||||
|
assert actual.exitcode == expected.exitcode, \
|
||||||
|
f"Exit code of the compiler ({compiler}) is incorrect"
|
||||||
|
if expected.output is not None and actual.output is not None:
|
||||||
|
assert actual.output == expected.output, \
|
||||||
|
f"Output of the program is incorrect (using {compiler})."
|
||||||
|
assert actual.execcode == expected.execcode, \
|
||||||
|
f"Exit code of the execution is incorrect (after compiling with {compiler})"
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('filename', ALL_FILES)
|
||||||
|
def test_expect(self, filename):
|
||||||
|
"""Test the EXPECTED annotations in test files by launching the
|
||||||
|
program with GCC."""
|
||||||
|
if SKIP_EXPECT:
|
||||||
|
pytest.skip("Skipping all test_expect because $SKIP_EXPECT is set.")
|
||||||
|
expect = self.get_expect(filename)
|
||||||
|
if expect.skip_test_expected:
|
||||||
|
pytest.skip("Skipping test_expect with GCC because "
|
||||||
|
"the test contains SKIP TEST EXPECTED")
|
||||||
|
if expect.exitcode != 0:
|
||||||
|
# GCC is more permissive than us, so trying to compile an
|
||||||
|
# incorrect program would bring us no information (it may
|
||||||
|
# compile, or fail with a different message...)
|
||||||
|
pytest.skip("Not testing the expected value for tests expecting exitcode!=0")
|
||||||
|
gcc_result = self.run_with_gcc(filename, expect)
|
||||||
|
self.assert_equal(gcc_result, expect, "GCC")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('filename', ALL_FILES)
|
||||||
|
def test_naive_alloc(self, filename):
|
||||||
|
cat(filename) # For diagnosis
|
||||||
|
expect = self.get_expect(filename)
|
||||||
|
naive = self.compile_and_simulate(filename, expect, 'naive')
|
||||||
|
self.assert_equal(naive, expect, "MiniCC with naive alloc")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('filename', ALL_IN_MEM_FILES)
|
||||||
|
def test_alloc_mem(self, filename):
|
||||||
|
cat(filename) # For diagnosis
|
||||||
|
expect = self.get_expect(filename)
|
||||||
|
actual = self.compile_and_simulate(filename, expect, 'all-in-mem')
|
||||||
|
self.assert_equal(actual, expect, "MiniCC with all-in-mem")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('filename', ALL_IN_MEM_FILES)
|
||||||
|
def test_smart_alloc(self, filename):
|
||||||
|
"""Generate code with smart allocation."""
|
||||||
|
cat(filename) # For diagnosis
|
||||||
|
expect = self.get_expect(filename)
|
||||||
|
actual = self.compile_and_simulate(filename, expect, 'smart')
|
||||||
|
self.assert_equal(actual, expect, "MiniCC with smart alloc")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
pytest.main(sys.argv)
|
@ -5,7 +5,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from test_expect_pragma import (
|
from test_expect_pragma import (
|
||||||
TestExpectPragmas, cat,
|
TestExpectPragmas, cat,
|
||||||
TestCompiler, filter_pathnames
|
TestCompiler
|
||||||
)
|
)
|
||||||
|
|
||||||
HERE = os.path.dirname(os.path.realpath(__file__))
|
HERE = os.path.dirname(os.path.realpath(__file__))
|
||||||
@ -34,7 +34,8 @@ if 'TEST_FILES' in os.environ:
|
|||||||
MINIC_EVAL = os.path.join(IMPLEM_DIR, 'MiniCC.py')
|
MINIC_EVAL = os.path.join(IMPLEM_DIR, 'MiniCC.py')
|
||||||
|
|
||||||
if 'FILTER' in os.environ:
|
if 'FILTER' in os.environ:
|
||||||
ALL_FILES = filter_pathnames(ALL_FILES, os.environ['FILTER'])
|
FILTER_FILES = glob.glob(os.path.join(HERE, os.environ['FILTER']), recursive=True)
|
||||||
|
ALL_FILES = list(set(FILTER_FILES) & set(ALL_FILES))
|
||||||
|
|
||||||
|
|
||||||
class TestInterpret(TestExpectPragmas, TestCompiler):
|
class TestInterpret(TestExpectPragmas, TestCompiler):
|
||||||
|
11
PLANNING.md
11
PLANNING.md
@ -43,7 +43,7 @@ _Academic first semester 2024-2025_
|
|||||||
|
|
||||||
# Week 4:
|
# Week 4:
|
||||||
|
|
||||||
- hammer: Lab 3: Monday 30/09/2024, 13h30-15h30. Room E001 (Samuel Humeau & Emma Nardino)
|
- :hammer: Lab 3: Monday 30/09/2024, 13h30-15h30. Room E001 (Samuel Humeau & Emma Nardino)
|
||||||
|
|
||||||
* Interpreter & Typer [TP03](TP03/tp3.pdf).
|
* Interpreter & Typer [TP03](TP03/tp3.pdf).
|
||||||
* Code in [TP03/](TP03/) and [MiniC/TP03/](MiniC/TP03/).
|
* Code in [TP03/](TP03/) and [MiniC/TP03/](MiniC/TP03/).
|
||||||
@ -51,3 +51,12 @@ _Academic first semester 2024-2025_
|
|||||||
- :book: Course: Thursday 3/10/2024, 10h15-12h15. Amphi B (Gabriel Radanne)
|
- :book: Course: Thursday 3/10/2024, 10h15-12h15. Amphi B (Gabriel Radanne)
|
||||||
|
|
||||||
* CFG [slides in english](course/capmif_cours06_irs.pdf).
|
* CFG [slides in english](course/capmif_cours06_irs.pdf).
|
||||||
|
|
||||||
|
# Week 5:
|
||||||
|
|
||||||
|
- :hammer: Lab 4a: Monday 07/10/2024, 13h30-15h30. Room E001 (Samuel Humeau & Emma Nardino)
|
||||||
|
|
||||||
|
* Syntax directed code generation [TP04](TP04/tp4a.pdf).
|
||||||
|
* Code in [MiniC/TP04/](MiniC/TP04/).
|
||||||
|
* Documentation [here](docs/html/index.html).
|
||||||
|
|
||||||
|
BIN
TP03/tp3.pdf
BIN
TP03/tp3.pdf
Binary file not shown.
BIN
TP04/tp4a.pdf
Normal file
BIN
TP04/tp4a.pdf
Normal file
Binary file not shown.
0
docs/.nojekyll
Normal file
0
docs/.nojekyll
Normal file
4
docs/html/.buildinfo
Normal file
4
docs/html/.buildinfo
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Sphinx build info version 1
|
||||||
|
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||||
|
config: 486d1ff73ffb3f41d31651516deff8a8
|
||||||
|
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
185
docs/html/_modules/Lib/Allocator.html
Normal file
185
docs/html/_modules/Lib/Allocator.html
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="writer-html5" lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Lib.Allocator — MiniC documentation</title>
|
||||||
|
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="../../_static/css/theme.css" type="text/css" />
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="../../_static/js/html5shiv.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
||||||
|
<script src="../../_static/doctools.js"></script>
|
||||||
|
<script src="../../_static/sphinx_highlight.js"></script>
|
||||||
|
<script src="../../_static/js/theme.js"></script>
|
||||||
|
<link rel="index" title="Index" href="../../genindex.html" />
|
||||||
|
<link rel="search" title="Search" href="../../search.html" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="wy-body-for-nav">
|
||||||
|
<div class="wy-grid-for-nav">
|
||||||
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||||
|
<div class="wy-side-scroll">
|
||||||
|
<div class="wy-side-nav-search" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../../index.html" class="icon icon-home">
|
||||||
|
MiniC
|
||||||
|
</a>
|
||||||
|
<div role="search">
|
||||||
|
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
|
||||||
|
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||||
|
<input type="hidden" name="check_keywords" value="yes" />
|
||||||
|
<input type="hidden" name="area" value="default" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||||
|
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Errors.html">Base library - Errors</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Statement.html">Base library - Statement</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.RiscV.html">Base library - RISC-V instructions</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Operands.html">Base library - Operands</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.FunctionData.html">Base library - Function data</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.LinearCode.html">Linear intermediate representation</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Allocator.html">Temporary allocation</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||||
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||||
|
<a href="../../index.html">MiniC</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="wy-nav-content">
|
||||||
|
<div class="rst-content">
|
||||||
|
<div role="navigation" aria-label="Page navigation">
|
||||||
|
<ul class="wy-breadcrumbs">
|
||||||
|
<li><a href="../../index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="../index.html">Module code</a></li>
|
||||||
|
<li class="breadcrumb-item active">Lib.Allocator</li>
|
||||||
|
<li class="wy-breadcrumbs-aside">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||||
|
<div itemprop="articleBody">
|
||||||
|
|
||||||
|
<h1>Source code for Lib.Allocator</h1><div class="highlight"><pre>
|
||||||
|
<span></span><span class="sd">"""</span>
|
||||||
|
<span class="sd">This file defines the base class :py:class:`Allocator`</span>
|
||||||
|
<span class="sd">and the naïve implementation :py:class:`NaiveAllocator`.</span>
|
||||||
|
<span class="sd">"""</span>
|
||||||
|
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Operands</span> <span class="kn">import</span> <span class="n">Temporary</span><span class="p">,</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">DataLocation</span><span class="p">,</span> <span class="n">GP_REGS</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Statement</span> <span class="kn">import</span> <span class="n">Instruction</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Errors</span> <span class="kn">import</span> <span class="n">AllocationError</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.FunctionData</span> <span class="kn">import</span> <span class="n">FunctionData</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">List</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Allocator"><a class="viewcode-back" href="../../api/Lib.Allocator.html#Lib.Allocator.Allocator">[docs]</a><span class="k">class</span> <span class="nc">Allocator</span><span class="p">():</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""General base class for Naive, AllInMem and Smart Allocators.</span>
|
||||||
|
<span class="sd"> Replace all temporaries in the code with actual data locations.</span>
|
||||||
|
|
||||||
|
<span class="sd"> Allocation is done in two steps:</span>
|
||||||
|
|
||||||
|
<span class="sd"> - First, :py:meth:`prepare` is responsible for calling</span>
|
||||||
|
<span class="sd"> :py:meth:`Lib.Operands.TemporaryPool.set_temp_allocation`</span>
|
||||||
|
<span class="sd"> with a mapping from temporaries to where they should actually be stored</span>
|
||||||
|
<span class="sd"> (in registers or in memory).</span>
|
||||||
|
<span class="sd"> - Then, :py:meth:`replace` is called for each instruction in order to</span>
|
||||||
|
<span class="sd"> replace the temporary operands with the previously assigned locations</span>
|
||||||
|
<span class="sd"> (and possibly add some instructions before or after).</span>
|
||||||
|
<span class="sd"> Concretely, it returns a list of instructions that should replace the original</span>
|
||||||
|
<span class="sd"> instruction. The actual iteration over all the instructions is handled transparently</span>
|
||||||
|
<span class="sd"> by :py:meth:`Lib.LinearCode.LinearCode.iter_statements`.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
|
||||||
|
<span class="n">_fdata</span><span class="p">:</span> <span class="n">FunctionData</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">fdata</span><span class="p">:</span> <span class="n">FunctionData</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_fdata</span> <span class="o">=</span> <span class="n">fdata</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Allocator.prepare"><a class="viewcode-back" href="../../api/Lib.Allocator.html#Lib.Allocator.Allocator.prepare">[docs]</a> <span class="k">def</span> <span class="nf">prepare</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="c1"># pragma: no cover</span>
|
||||||
|
<span class="k">pass</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Allocator.replace"><a class="viewcode-back" href="../../api/Lib.Allocator.html#Lib.Allocator.Allocator.replace">[docs]</a> <span class="k">def</span> <span class="nf">replace</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">old_instr</span><span class="p">:</span> <span class="n">Instruction</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">Instruction</span><span class="p">]:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Transform an instruction with temporaries into a list of instructions."""</span>
|
||||||
|
<span class="k">return</span> <span class="p">[</span><span class="n">old_instr</span><span class="p">]</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Allocator.rewriteCode"><a class="viewcode-back" href="../../api/Lib.Allocator.html#Lib.Allocator.Allocator.rewriteCode">[docs]</a> <span class="k">def</span> <span class="nf">rewriteCode</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">listcode</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Modify the code to replace temporaries with</span>
|
||||||
|
<span class="sd"> registers or memory locations.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="n">listcode</span><span class="o">.</span><span class="n">iter_statements</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">replace</span><span class="p">)</span></div></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="NaiveAllocator"><a class="viewcode-back" href="../../api/Lib.Allocator.html#Lib.Allocator.NaiveAllocator">[docs]</a><span class="k">class</span> <span class="nc">NaiveAllocator</span><span class="p">(</span><span class="n">Allocator</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Naive Allocator: try to assign a register to each temporary,</span>
|
||||||
|
<span class="sd"> fails if there are more temporaries than registers.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="NaiveAllocator.replace"><a class="viewcode-back" href="../../api/Lib.Allocator.html#Lib.Allocator.NaiveAllocator.replace">[docs]</a> <span class="k">def</span> <span class="nf">replace</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">old_instr</span><span class="p">:</span> <span class="n">Instruction</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">Instruction</span><span class="p">]:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Replace Temporary operands with the corresponding allocated Register."""</span>
|
||||||
|
<span class="n">new_args</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Operand</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||||||
|
<span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">old_instr</span><span class="o">.</span><span class="n">args</span><span class="p">():</span>
|
||||||
|
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">Temporary</span><span class="p">):</span>
|
||||||
|
<span class="n">new_args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">arg</span><span class="o">.</span><span class="n">get_alloced_loc</span><span class="p">())</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="n">new_args</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span>
|
||||||
|
<span class="n">new_instr</span> <span class="o">=</span> <span class="n">old_instr</span><span class="o">.</span><span class="n">with_args</span><span class="p">(</span><span class="n">new_args</span><span class="p">)</span>
|
||||||
|
<span class="k">return</span> <span class="p">[</span><span class="n">new_instr</span><span class="p">]</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="NaiveAllocator.prepare"><a class="viewcode-back" href="../../api/Lib.Allocator.html#Lib.Allocator.NaiveAllocator.prepare">[docs]</a> <span class="k">def</span> <span class="nf">prepare</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Allocate all temporaries to registers.</span>
|
||||||
|
<span class="sd"> Fail if there are too many temporaries."""</span>
|
||||||
|
<span class="n">regs</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">GP_REGS</span><span class="p">)</span> <span class="c1"># Get a writable copy</span>
|
||||||
|
<span class="n">temp_allocation</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="n">Temporary</span><span class="p">,</span> <span class="n">DataLocation</span><span class="p">]</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
|
||||||
|
<span class="k">for</span> <span class="n">tmp</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_fdata</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">get_all_temps</span><span class="p">():</span>
|
||||||
|
<span class="k">try</span><span class="p">:</span>
|
||||||
|
<span class="n">reg</span> <span class="o">=</span> <span class="n">regs</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
|
||||||
|
<span class="k">except</span> <span class="ne">IndexError</span><span class="p">:</span>
|
||||||
|
<span class="k">raise</span> <span class="n">AllocationError</span><span class="p">(</span>
|
||||||
|
<span class="s2">"Too many temporaries (</span><span class="si">{}</span><span class="s2">) for the naive allocation, sorry."</span>
|
||||||
|
<span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_fdata</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">get_all_temps</span><span class="p">())))</span>
|
||||||
|
<span class="n">temp_allocation</span><span class="p">[</span><span class="n">tmp</span><span class="p">]</span> <span class="o">=</span> <span class="n">reg</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_fdata</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">set_temp_allocation</span><span class="p">(</span><span class="n">temp_allocation</span><span class="p">)</span></div></div>
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div role="contentinfo">
|
||||||
|
<p>© Copyright 2023, compil-lyon.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||||
|
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||||
|
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||||
|
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
jQuery(function () {
|
||||||
|
SphinxRtdTheme.Navigation.enable(true);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
124
docs/html/_modules/Lib/Errors.html
Normal file
124
docs/html/_modules/Lib/Errors.html
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="writer-html5" lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Lib.Errors — MiniC documentation</title>
|
||||||
|
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="../../_static/css/theme.css" type="text/css" />
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="../../_static/js/html5shiv.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
||||||
|
<script src="../../_static/doctools.js"></script>
|
||||||
|
<script src="../../_static/sphinx_highlight.js"></script>
|
||||||
|
<script src="../../_static/js/theme.js"></script>
|
||||||
|
<link rel="index" title="Index" href="../../genindex.html" />
|
||||||
|
<link rel="search" title="Search" href="../../search.html" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="wy-body-for-nav">
|
||||||
|
<div class="wy-grid-for-nav">
|
||||||
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||||
|
<div class="wy-side-scroll">
|
||||||
|
<div class="wy-side-nav-search" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../../index.html" class="icon icon-home">
|
||||||
|
MiniC
|
||||||
|
</a>
|
||||||
|
<div role="search">
|
||||||
|
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
|
||||||
|
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||||
|
<input type="hidden" name="check_keywords" value="yes" />
|
||||||
|
<input type="hidden" name="area" value="default" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||||
|
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Errors.html">Base library - Errors</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Statement.html">Base library - Statement</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.RiscV.html">Base library - RISC-V instructions</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Operands.html">Base library - Operands</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.FunctionData.html">Base library - Function data</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.LinearCode.html">Linear intermediate representation</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Allocator.html">Temporary allocation</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||||
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||||
|
<a href="../../index.html">MiniC</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="wy-nav-content">
|
||||||
|
<div class="rst-content">
|
||||||
|
<div role="navigation" aria-label="Page navigation">
|
||||||
|
<ul class="wy-breadcrumbs">
|
||||||
|
<li><a href="../../index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="../index.html">Module code</a></li>
|
||||||
|
<li class="breadcrumb-item active">Lib.Errors</li>
|
||||||
|
<li class="wy-breadcrumbs-aside">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||||
|
<div itemprop="articleBody">
|
||||||
|
|
||||||
|
<h1>Source code for Lib.Errors</h1><div class="highlight"><pre>
|
||||||
|
<div class="viewcode-block" id="MiniCRuntimeError"><a class="viewcode-back" href="../../api/Lib.Errors.html#Lib.Errors.MiniCRuntimeError">[docs]</a><span></span><span class="k">class</span> <span class="nc">MiniCRuntimeError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
||||||
|
<span class="k">pass</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="MiniCInternalError"><a class="viewcode-back" href="../../api/Lib.Errors.html#Lib.Errors.MiniCInternalError">[docs]</a><span class="k">class</span> <span class="nc">MiniCInternalError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
||||||
|
<span class="k">pass</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="MiniCUnsupportedError"><a class="viewcode-back" href="../../api/Lib.Errors.html#Lib.Errors.MiniCUnsupportedError">[docs]</a><span class="k">class</span> <span class="nc">MiniCUnsupportedError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
||||||
|
<span class="k">pass</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="MiniCTypeError"><a class="viewcode-back" href="../../api/Lib.Errors.html#Lib.Errors.MiniCTypeError">[docs]</a><span class="k">class</span> <span class="nc">MiniCTypeError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
||||||
|
<span class="k">pass</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="AllocationError"><a class="viewcode-back" href="../../api/Lib.Errors.html#Lib.Errors.AllocationError">[docs]</a><span class="k">class</span> <span class="nc">AllocationError</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
|
||||||
|
<span class="k">pass</span></div>
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div role="contentinfo">
|
||||||
|
<p>© Copyright 2023, compil-lyon.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||||
|
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||||
|
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||||
|
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
jQuery(function () {
|
||||||
|
SphinxRtdTheme.Navigation.enable(true);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
287
docs/html/_modules/Lib/FunctionData.html
Normal file
287
docs/html/_modules/Lib/FunctionData.html
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="writer-html5" lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Lib.FunctionData — MiniC documentation</title>
|
||||||
|
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="../../_static/css/theme.css" type="text/css" />
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="../../_static/js/html5shiv.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
||||||
|
<script src="../../_static/doctools.js"></script>
|
||||||
|
<script src="../../_static/sphinx_highlight.js"></script>
|
||||||
|
<script src="../../_static/js/theme.js"></script>
|
||||||
|
<link rel="index" title="Index" href="../../genindex.html" />
|
||||||
|
<link rel="search" title="Search" href="../../search.html" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="wy-body-for-nav">
|
||||||
|
<div class="wy-grid-for-nav">
|
||||||
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||||
|
<div class="wy-side-scroll">
|
||||||
|
<div class="wy-side-nav-search" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../../index.html" class="icon icon-home">
|
||||||
|
MiniC
|
||||||
|
</a>
|
||||||
|
<div role="search">
|
||||||
|
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
|
||||||
|
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||||
|
<input type="hidden" name="check_keywords" value="yes" />
|
||||||
|
<input type="hidden" name="area" value="default" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||||
|
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Errors.html">Base library - Errors</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Statement.html">Base library - Statement</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.RiscV.html">Base library - RISC-V instructions</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Operands.html">Base library - Operands</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.FunctionData.html">Base library - Function data</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.LinearCode.html">Linear intermediate representation</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Allocator.html">Temporary allocation</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||||
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||||
|
<a href="../../index.html">MiniC</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="wy-nav-content">
|
||||||
|
<div class="rst-content">
|
||||||
|
<div role="navigation" aria-label="Page navigation">
|
||||||
|
<ul class="wy-breadcrumbs">
|
||||||
|
<li><a href="../../index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="../index.html">Module code</a></li>
|
||||||
|
<li class="breadcrumb-item active">Lib.FunctionData</li>
|
||||||
|
<li class="wy-breadcrumbs-aside">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||||
|
<div itemprop="articleBody">
|
||||||
|
|
||||||
|
<h1>Source code for Lib.FunctionData</h1><div class="highlight"><pre>
|
||||||
|
<span></span><span class="sd">"""</span>
|
||||||
|
<span class="sd">This file defines the base class :py:class:`FunctionData`,</span>
|
||||||
|
<span class="sd">containing metadata on a RiscV function, as well as utility</span>
|
||||||
|
<span class="sd">functions common to the different intermediate representations.</span>
|
||||||
|
<span class="sd">"""</span>
|
||||||
|
|
||||||
|
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="p">(</span><span class="n">List</span><span class="p">,</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">TypeVar</span><span class="p">)</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Errors</span> <span class="kn">import</span> <span class="n">AllocationError</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Operands</span> <span class="kn">import</span> <span class="p">(</span>
|
||||||
|
<span class="n">Offset</span><span class="p">,</span> <span class="n">Temporary</span><span class="p">,</span> <span class="n">TemporaryPool</span><span class="p">,</span>
|
||||||
|
<span class="n">S</span><span class="p">,</span> <span class="n">T</span><span class="p">,</span> <span class="n">FP</span><span class="p">)</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Statement</span> <span class="kn">import</span> <span class="p">(</span><span class="n">Statement</span><span class="p">,</span> <span class="n">Instruction</span><span class="p">,</span> <span class="n">Label</span><span class="p">,</span> <span class="n">Comment</span><span class="p">)</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="FunctionData"><a class="viewcode-back" href="../../api/Lib.FunctionData.html#Lib.FunctionData.FunctionData">[docs]</a><span class="k">class</span> <span class="nc">FunctionData</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Stores some metadata on a RiscV function:</span>
|
||||||
|
<span class="sd"> name of the function, label names, temporary variables</span>
|
||||||
|
<span class="sd"> (using :py:class:`Lib.Operands.TemporaryPool`),</span>
|
||||||
|
<span class="sd"> and div_by_zero label.</span>
|
||||||
|
|
||||||
|
<span class="sd"> This class is usually used indirectly through the</span>
|
||||||
|
<span class="sd"> different intermediate representations we work with,</span>
|
||||||
|
<span class="sd"> such as :py:attr:`Lib.LinearCode.LinearCode.fdata`.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
|
||||||
|
<span class="n">_nblabel</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
|
<span class="n">_dec</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
|
<span class="n">_pool</span><span class="p">:</span> <span class="n">TemporaryPool</span>
|
||||||
|
<span class="n">_name</span><span class="p">:</span> <span class="nb">str</span>
|
||||||
|
<span class="n">_label_div_by_zero</span><span class="p">:</span> <span class="n">Label</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_nblabel</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_dec</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_pool</span> <span class="o">=</span> <span class="n">TemporaryPool</span><span class="p">()</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_label_div_by_zero</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">fresh_label</span><span class="p">(</span><span class="s2">"div_by_zero"</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="FunctionData.get_name"><a class="viewcode-back" href="../../api/Lib.FunctionData.html#Lib.FunctionData.FunctionData.get_name">[docs]</a> <span class="k">def</span> <span class="nf">get_name</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Return the name of the function."""</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="FunctionData.fresh_tmp"><a class="viewcode-back" href="../../api/Lib.FunctionData.html#Lib.FunctionData.FunctionData.fresh_tmp">[docs]</a> <span class="k">def</span> <span class="nf">fresh_tmp</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Temporary</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Return a new fresh Temporary,</span>
|
||||||
|
<span class="sd"> which is added to the pool.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">fresh_tmp</span><span class="p">()</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="FunctionData.fresh_offset"><a class="viewcode-back" href="../../api/Lib.FunctionData.html#Lib.FunctionData.FunctionData.fresh_offset">[docs]</a> <span class="k">def</span> <span class="nf">fresh_offset</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Offset</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Return a new offset in the memory stack.</span>
|
||||||
|
<span class="sd"> Offsets are decreasing relative to FP.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_dec</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dec</span> <span class="o">+</span> <span class="mi">1</span>
|
||||||
|
<span class="c1"># For ld or sd, an offset on 12 signed bits is expected</span>
|
||||||
|
<span class="c1"># Raise an error if the offset is too big</span>
|
||||||
|
<span class="k">if</span> <span class="o">-</span><span class="mi">8</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dec</span> <span class="o"><</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">**</span> <span class="mi">11</span><span class="p">:</span>
|
||||||
|
<span class="k">raise</span> <span class="n">AllocationError</span><span class="p">(</span>
|
||||||
|
<span class="s2">"Offset given by the allocation too big to be manipulated (</span><span class="si">{}</span><span class="s2">), sorry."</span>
|
||||||
|
<span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_dec</span><span class="p">))</span>
|
||||||
|
<span class="k">return</span> <span class="n">Offset</span><span class="p">(</span><span class="n">FP</span><span class="p">,</span> <span class="o">-</span><span class="mi">8</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dec</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="FunctionData.get_offset"><a class="viewcode-back" href="../../api/Lib.FunctionData.html#Lib.FunctionData.FunctionData.get_offset">[docs]</a> <span class="k">def</span> <span class="nf">get_offset</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Return the current offset in the memory stack.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_dec</span></div>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">_fresh_label_name</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Return a new unique label name based on the given string.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_nblabel</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_nblabel</span> <span class="o">+</span> <span class="mi">1</span>
|
||||||
|
<span class="k">return</span> <span class="n">name</span> <span class="o">+</span> <span class="s2">"_"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_nblabel</span><span class="p">)</span> <span class="o">+</span> <span class="s2">"_"</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="FunctionData.fresh_label"><a class="viewcode-back" href="../../api/Lib.FunctionData.html#Lib.FunctionData.FunctionData.fresh_label">[docs]</a> <span class="k">def</span> <span class="nf">fresh_label</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span> <span class="o">-></span> <span class="n">Label</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Return a new label, with a unique name based on the given string.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="k">return</span> <span class="n">Label</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_fresh_label_name</span><span class="p">(</span><span class="n">name</span><span class="p">))</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="FunctionData.get_label_div_by_zero"><a class="viewcode-back" href="../../api/Lib.FunctionData.html#Lib.FunctionData.FunctionData.get_label_div_by_zero">[docs]</a> <span class="k">def</span> <span class="nf">get_label_div_by_zero</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Label</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_label_div_by_zero</span></div></div>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="n">_T</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"_T"</span><span class="p">,</span> <span class="n">bound</span><span class="o">=</span><span class="n">Statement</span><span class="p">)</span>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">_iter_statements</span><span class="p">(</span>
|
||||||
|
<span class="n">listIns</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">_T</span><span class="p">],</span> <span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">_T</span><span class="p">],</span> <span class="n">List</span><span class="p">[</span><span class="n">_T</span><span class="p">]])</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">_T</span> <span class="o">|</span> <span class="n">Comment</span><span class="p">]:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Iterate over instructions.</span>
|
||||||
|
<span class="sd"> For each real instruction i (not label or comment), replace it</span>
|
||||||
|
<span class="sd"> with the list of instructions given by f(i).</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="n">newListIns</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">_T</span> <span class="o">|</span> <span class="n">Comment</span><span class="p">]</span> <span class="o">=</span> <span class="p">[]</span>
|
||||||
|
<span class="k">for</span> <span class="n">old_i</span> <span class="ow">in</span> <span class="n">listIns</span><span class="p">:</span>
|
||||||
|
<span class="c1"># Do nothing for label or comment</span>
|
||||||
|
<span class="k">if</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">old_i</span><span class="p">,</span> <span class="n">Instruction</span><span class="p">):</span>
|
||||||
|
<span class="n">newListIns</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">old_i</span><span class="p">)</span>
|
||||||
|
<span class="k">continue</span>
|
||||||
|
<span class="n">new_i_list</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">old_i</span><span class="p">)</span>
|
||||||
|
<span class="c1"># Otherwise, replace the instruction by the list</span>
|
||||||
|
<span class="c1"># returned by f, with comments giving the replacement</span>
|
||||||
|
<span class="n">newListIns</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Comment</span><span class="p">(</span><span class="s2">"Replaced "</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">old_i</span><span class="p">)))</span>
|
||||||
|
<span class="n">newListIns</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">new_i_list</span><span class="p">)</span>
|
||||||
|
<span class="k">return</span> <span class="n">newListIns</span>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="nf">_print_code</span><span class="p">(</span><span class="n">listIns</span><span class="p">:</span> <span class="n">List</span><span class="p">,</span> <span class="n">fdata</span><span class="p">:</span> <span class="n">FunctionData</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span>
|
||||||
|
<span class="n">init_label</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fin_label</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fin_div0</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">comment</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Please use print_code from LinearCode or CFG, not directly this one.</span>
|
||||||
|
|
||||||
|
<span class="sd"> Print the instructions from listIns, forming fdata, on output.</span>
|
||||||
|
<span class="sd"> If init_label is given, add an initial jump to it before the generated code.</span>
|
||||||
|
<span class="sd"> If fin_label is given, add it after the generated code.</span>
|
||||||
|
<span class="sd"> If fin_div0 is given equal to true, add the code for returning an</span>
|
||||||
|
<span class="sd"> error when dividing by 0, at the very end.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="c1"># compute size for the local stack - do not forget to align by 16</span>
|
||||||
|
<span class="n">fo</span> <span class="o">=</span> <span class="n">fdata</span><span class="o">.</span><span class="n">get_offset</span><span class="p">()</span> <span class="c1"># allocate enough memory for stack</span>
|
||||||
|
<span class="c1"># Room for S_i (except S_0 which is fp) and T_i backup</span>
|
||||||
|
<span class="n">fo</span> <span class="o">+=</span> <span class="nb">len</span><span class="p">(</span><span class="n">S</span><span class="p">[</span><span class="mi">1</span><span class="p">:])</span> <span class="o">+</span> <span class="nb">len</span><span class="p">(</span><span class="n">T</span><span class="p">)</span>
|
||||||
|
<span class="n">cardoffset</span> <span class="o">=</span> <span class="mi">8</span> <span class="o">*</span> <span class="p">(</span><span class="n">fo</span> <span class="o">+</span> <span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">fo</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span> <span class="k">else</span> <span class="mi">1</span><span class="p">))</span> <span class="o">+</span> <span class="mi">16</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span>
|
||||||
|
<span class="s2">"##Automatically generated RISCV code, MIF08 & CAP</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="k">if</span> <span class="n">comment</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"##</span><span class="si">{}</span><span class="s2"> version</span><span class="se">\n</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">comment</span><span class="p">))</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"</span><span class="se">\n\n</span><span class="s2">##prelude</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="c1"># We put an li t0, cardoffset in case it is greater than 2**11</span>
|
||||||
|
<span class="c1"># We use t0 because it is caller-saved</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"""</span>
|
||||||
|
<span class="s2"> .text</span>
|
||||||
|
<span class="s2"> .globl </span><span class="si">{0}</span>
|
||||||
|
<span class="si">{0}</span><span class="s2">:</span>
|
||||||
|
<span class="s2"> li t0, </span><span class="si">{1}</span>
|
||||||
|
<span class="s2"> sub sp, sp, t0</span>
|
||||||
|
<span class="s2"> sd ra, 0(sp)</span>
|
||||||
|
<span class="s2"> sd fp, 8(sp)</span>
|
||||||
|
<span class="s2"> add fp, sp, t0</span>
|
||||||
|
<span class="s2">"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">fdata</span><span class="o">.</span><span class="n">get_name</span><span class="p">(),</span> <span class="n">cardoffset</span><span class="p">))</span>
|
||||||
|
<span class="c1"># Stack in RiscV is managed with SP</span>
|
||||||
|
<span class="k">if</span> <span class="n">init_label</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="c1"># Add a jump to init_label before the generated code.</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"""</span>
|
||||||
|
<span class="s2"> j </span><span class="si">{0}</span>
|
||||||
|
<span class="s2">"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">init_label</span><span class="p">))</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"</span><span class="se">\n\n</span><span class="s2">##Generated Code</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="c1"># Generated code</span>
|
||||||
|
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">listIns</span><span class="p">:</span>
|
||||||
|
<span class="n">i</span><span class="o">.</span><span class="n">printIns</span><span class="p">(</span><span class="n">output</span><span class="p">)</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"</span><span class="se">\n\n</span><span class="s2">##postlude</span><span class="se">\n</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
<span class="k">if</span> <span class="n">fin_label</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="c1"># Add fin_label after the generated code.</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"""</span>
|
||||||
|
<span class="si">{0}</span><span class="s2">:</span>
|
||||||
|
<span class="s2">"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">fin_label</span><span class="p">))</span>
|
||||||
|
<span class="c1"># We put an li t0, cardoffset in case it is greater than 2**11</span>
|
||||||
|
<span class="c1"># We use t0 because it is caller-saved</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"""</span>
|
||||||
|
<span class="s2"> ld ra, 0(sp)</span>
|
||||||
|
<span class="s2"> ld fp, 8(sp)</span>
|
||||||
|
<span class="s2"> li t0, </span><span class="si">{0}</span>
|
||||||
|
<span class="s2"> add sp, sp, t0</span>
|
||||||
|
<span class="s2"> ret</span>
|
||||||
|
<span class="s2">"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">cardoffset</span><span class="p">))</span>
|
||||||
|
<span class="k">if</span> <span class="n">fin_div0</span><span class="p">:</span>
|
||||||
|
<span class="c1"># Add code for division by 0 at the end.</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"""</span>
|
||||||
|
<span class="si">{0}</span><span class="s2">:</span>
|
||||||
|
<span class="s2"> la a0, </span><span class="si">{0}</span><span class="s2">_msg</span>
|
||||||
|
<span class="s2"> call println_string</span>
|
||||||
|
<span class="s2"> li a0, 1</span>
|
||||||
|
<span class="s2"> call exit</span>
|
||||||
|
<span class="s2">"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">fdata</span><span class="o">.</span><span class="n">_label_div_by_zero</span><span class="p">))</span>
|
||||||
|
<span class="c1"># Add the data for the message of the division by 0</span>
|
||||||
|
<span class="n">output</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">"""</span>
|
||||||
|
<span class="si">{0}</span><span class="s2">_msg: .string "Division by 0"</span>
|
||||||
|
<span class="s2">"""</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">fdata</span><span class="o">.</span><span class="n">_label_div_by_zero</span><span class="p">))</span>
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div role="contentinfo">
|
||||||
|
<p>© Copyright 2023, compil-lyon.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||||
|
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||||
|
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||||
|
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
jQuery(function () {
|
||||||
|
SphinxRtdTheme.Navigation.enable(true);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
209
docs/html/_modules/Lib/LinearCode.html
Normal file
209
docs/html/_modules/Lib/LinearCode.html
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="writer-html5" lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Lib.LinearCode — MiniC documentation</title>
|
||||||
|
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="../../_static/css/theme.css" type="text/css" />
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="../../_static/js/html5shiv.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
||||||
|
<script src="../../_static/doctools.js"></script>
|
||||||
|
<script src="../../_static/sphinx_highlight.js"></script>
|
||||||
|
<script src="../../_static/js/theme.js"></script>
|
||||||
|
<link rel="index" title="Index" href="../../genindex.html" />
|
||||||
|
<link rel="search" title="Search" href="../../search.html" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="wy-body-for-nav">
|
||||||
|
<div class="wy-grid-for-nav">
|
||||||
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||||
|
<div class="wy-side-scroll">
|
||||||
|
<div class="wy-side-nav-search" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../../index.html" class="icon icon-home">
|
||||||
|
MiniC
|
||||||
|
</a>
|
||||||
|
<div role="search">
|
||||||
|
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
|
||||||
|
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||||
|
<input type="hidden" name="check_keywords" value="yes" />
|
||||||
|
<input type="hidden" name="area" value="default" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||||
|
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Errors.html">Base library - Errors</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Statement.html">Base library - Statement</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.RiscV.html">Base library - RISC-V instructions</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Operands.html">Base library - Operands</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.FunctionData.html">Base library - Function data</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.LinearCode.html">Linear intermediate representation</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Allocator.html">Temporary allocation</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||||
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||||
|
<a href="../../index.html">MiniC</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="wy-nav-content">
|
||||||
|
<div class="rst-content">
|
||||||
|
<div role="navigation" aria-label="Page navigation">
|
||||||
|
<ul class="wy-breadcrumbs">
|
||||||
|
<li><a href="../../index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="../index.html">Module code</a></li>
|
||||||
|
<li class="breadcrumb-item active">Lib.LinearCode</li>
|
||||||
|
<li class="wy-breadcrumbs-aside">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||||
|
<div itemprop="articleBody">
|
||||||
|
|
||||||
|
<h1>Source code for Lib.LinearCode</h1><div class="highlight"><pre>
|
||||||
|
<span></span><span class="sd">"""</span>
|
||||||
|
<span class="sd">CAP, CodeGeneration, LinearCode API</span>
|
||||||
|
<span class="sd">Classes for a RiscV linear code.</span>
|
||||||
|
<span class="sd">"""</span>
|
||||||
|
|
||||||
|
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Operands</span> <span class="kn">import</span> <span class="p">(</span><span class="n">A0</span><span class="p">,</span> <span class="n">Function</span><span class="p">,</span> <span class="n">DataLocation</span><span class="p">)</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Statement</span> <span class="kn">import</span> <span class="p">(</span>
|
||||||
|
<span class="n">Instru3A</span><span class="p">,</span> <span class="n">AbsoluteJump</span><span class="p">,</span> <span class="n">ConditionalJump</span><span class="p">,</span> <span class="n">Comment</span><span class="p">,</span> <span class="n">Label</span>
|
||||||
|
<span class="p">)</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.RiscV</span> <span class="kn">import</span> <span class="p">(</span><span class="n">mv</span><span class="p">,</span> <span class="n">call</span><span class="p">)</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.FunctionData</span> <span class="kn">import</span> <span class="p">(</span><span class="n">FunctionData</span><span class="p">,</span> <span class="n">_iter_statements</span><span class="p">,</span> <span class="n">_print_code</span><span class="p">)</span>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="n">CodeStatement</span> <span class="o">=</span> <span class="n">Comment</span> <span class="o">|</span> <span class="n">Label</span> <span class="o">|</span> <span class="n">Instru3A</span> <span class="o">|</span> <span class="n">AbsoluteJump</span> <span class="o">|</span> <span class="n">ConditionalJump</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="LinearCode"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode">[docs]</a><span class="k">class</span> <span class="nc">LinearCode</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Representation of a RiscV program as a list of instructions.</span>
|
||||||
|
|
||||||
|
<span class="sd"> :py:meth:`add_instruction` is repeatedly called in the codegen visitor</span>
|
||||||
|
<span class="sd"> to build a complete list of RiscV instructions for the source program.</span>
|
||||||
|
|
||||||
|
<span class="sd"> The :py:attr:`fdata` member variable contains some meta-information</span>
|
||||||
|
<span class="sd"> on the program, for instance to allocate a new temporary.</span>
|
||||||
|
<span class="sd"> See :py:class:`Lib.FunctionData.FunctionData`.</span>
|
||||||
|
|
||||||
|
<span class="sd"> For debugging purposes, :py:meth:`print_code` allows to print</span>
|
||||||
|
<span class="sd"> the RiscV program to a file.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> The :py:attr:`fdata` member variable contains some meta-information</span>
|
||||||
|
<span class="sd"> on the program, for instance to allocate a new temporary.</span>
|
||||||
|
<span class="sd"> See :py:class:`Lib.FunctionData.FunctionData`.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="n">fdata</span><span class="p">:</span> <span class="n">FunctionData</span>
|
||||||
|
|
||||||
|
<span class="n">_listIns</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">CodeStatement</span><span class="p">]</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_listIns</span> <span class="o">=</span> <span class="p">[]</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">fdata</span> <span class="o">=</span> <span class="n">FunctionData</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="LinearCode.add_instruction"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode.add_instruction">[docs]</a> <span class="k">def</span> <span class="nf">add_instruction</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">i</span><span class="p">:</span> <span class="n">CodeStatement</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""</span>
|
||||||
|
<span class="sd"> Utility function to add an instruction in the program.</span>
|
||||||
|
|
||||||
|
<span class="sd"> See also :py:mod:`Lib.RiscV` to generate relevant instructions.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_listIns</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">i</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="LinearCode.iter_statements"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode.iter_statements">[docs]</a> <span class="k">def</span> <span class="nf">iter_statements</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Iterate over instructions.</span>
|
||||||
|
<span class="sd"> For each real instruction i (not label or comment), replace it</span>
|
||||||
|
<span class="sd"> with the list of instructions given by f(i).</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="n">new_list_ins</span> <span class="o">=</span> <span class="n">_iter_statements</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_listIns</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span>
|
||||||
|
<span class="c1"># TODO: we shoudn't need to split this assignment with intermediate</span>
|
||||||
|
<span class="c1"># variable new_list_ins, but at least pyright pyright 1.1.293</span>
|
||||||
|
<span class="c1"># raises an error here if we don't.</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_listIns</span> <span class="o">=</span> <span class="n">new_list_ins</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="LinearCode.get_instructions"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode.get_instructions">[docs]</a> <span class="k">def</span> <span class="nf">get_instructions</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">CodeStatement</span><span class="p">]:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Return the list of instructions of the program."""</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_listIns</span></div>
|
||||||
|
|
||||||
|
<span class="c1"># each instruction has its own "add in list" version</span>
|
||||||
|
<div class="viewcode-block" id="LinearCode.add_label"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode.add_label">[docs]</a> <span class="k">def</span> <span class="nf">add_label</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">s</span><span class="p">:</span> <span class="n">Label</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Add a label in the program."""</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">add_instruction</span><span class="p">(</span><span class="n">s</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="LinearCode.add_comment"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode.add_comment">[docs]</a> <span class="k">def</span> <span class="nf">add_comment</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">s</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Add a comment in the program."""</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">add_instruction</span><span class="p">(</span><span class="n">Comment</span><span class="p">(</span><span class="n">s</span><span class="p">))</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="LinearCode.add_instruction_PRINTLN_INT"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode.add_instruction_PRINTLN_INT">[docs]</a> <span class="k">def</span> <span class="nf">add_instruction_PRINTLN_INT</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">reg</span><span class="p">:</span> <span class="n">DataLocation</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Print integer value, with newline. (see Expand)"""</span>
|
||||||
|
<span class="c1"># A print instruction generates the temp it prints.</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">add_instruction</span><span class="p">(</span><span class="n">mv</span><span class="p">(</span><span class="n">A0</span><span class="p">,</span> <span class="n">reg</span><span class="p">))</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">add_instruction</span><span class="p">(</span><span class="n">call</span><span class="p">(</span><span class="n">Function</span><span class="p">(</span><span class="s1">'println_int'</span><span class="p">)))</span></div>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="s1">'</span><span class="se">\n</span><span class="s1">'</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_listIns</span><span class="p">))</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="LinearCode.print_code"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode.print_code">[docs]</a> <span class="k">def</span> <span class="nf">print_code</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">comment</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Outputs the RiscV program as text to a file at the given path."""</span>
|
||||||
|
<span class="n">_print_code</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_listIns</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">fdata</span><span class="p">,</span> <span class="n">output</span><span class="p">,</span> <span class="n">init_label</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span>
|
||||||
|
<span class="n">fin_label</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fin_div0</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">comment</span><span class="o">=</span><span class="n">comment</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="LinearCode.print_dot"><a class="viewcode-back" href="../../api/Lib.LinearCode.html#Lib.LinearCode.LinearCode.print_dot">[docs]</a> <span class="k">def</span> <span class="nf">print_dot</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">filename</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">DF</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">view</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span> <span class="c1"># pragma: no cover</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Outputs the RiscV program as graph to a file at the given path."""</span>
|
||||||
|
<span class="c1"># import graphviz here so that students who don't have it can still work on lab4</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">graphviz</span> <span class="kn">import</span> <span class="n">Digraph</span>
|
||||||
|
<span class="n">graph</span> <span class="o">=</span> <span class="n">Digraph</span><span class="p">()</span>
|
||||||
|
<span class="c1"># nodes</span>
|
||||||
|
<span class="n">content</span> <span class="o">=</span> <span class="s2">""</span>
|
||||||
|
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_listIns</span><span class="p">:</span>
|
||||||
|
<span class="n">content</span> <span class="o">+=</span> <span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="o">+</span> <span class="s2">"</span><span class="se">\\</span><span class="s2">l"</span>
|
||||||
|
<span class="n">graph</span><span class="o">.</span><span class="n">node</span><span class="p">(</span><span class="s2">"Code"</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="n">content</span><span class="p">,</span> <span class="n">shape</span><span class="o">=</span><span class="s1">'rectangle'</span><span class="p">)</span>
|
||||||
|
<span class="c1"># no edges</span>
|
||||||
|
<span class="n">graph</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">view</span><span class="o">=</span><span class="n">view</span><span class="p">)</span></div></div>
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div role="contentinfo">
|
||||||
|
<p>© Copyright 2023, compil-lyon.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||||
|
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||||
|
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||||
|
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
jQuery(function () {
|
||||||
|
SphinxRtdTheme.Navigation.enable(true);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
385
docs/html/_modules/Lib/Operands.html
Normal file
385
docs/html/_modules/Lib/Operands.html
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="writer-html5" lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Lib.Operands — MiniC documentation</title>
|
||||||
|
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="../../_static/css/theme.css" type="text/css" />
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="../../_static/js/html5shiv.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
||||||
|
<script src="../../_static/doctools.js"></script>
|
||||||
|
<script src="../../_static/sphinx_highlight.js"></script>
|
||||||
|
<script src="../../_static/js/theme.js"></script>
|
||||||
|
<link rel="index" title="Index" href="../../genindex.html" />
|
||||||
|
<link rel="search" title="Search" href="../../search.html" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="wy-body-for-nav">
|
||||||
|
<div class="wy-grid-for-nav">
|
||||||
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||||
|
<div class="wy-side-scroll">
|
||||||
|
<div class="wy-side-nav-search" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../../index.html" class="icon icon-home">
|
||||||
|
MiniC
|
||||||
|
</a>
|
||||||
|
<div role="search">
|
||||||
|
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
|
||||||
|
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||||
|
<input type="hidden" name="check_keywords" value="yes" />
|
||||||
|
<input type="hidden" name="area" value="default" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||||
|
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Errors.html">Base library - Errors</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Statement.html">Base library - Statement</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.RiscV.html">Base library - RISC-V instructions</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Operands.html">Base library - Operands</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.FunctionData.html">Base library - Function data</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.LinearCode.html">Linear intermediate representation</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Allocator.html">Temporary allocation</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||||
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||||
|
<a href="../../index.html">MiniC</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="wy-nav-content">
|
||||||
|
<div class="rst-content">
|
||||||
|
<div role="navigation" aria-label="Page navigation">
|
||||||
|
<ul class="wy-breadcrumbs">
|
||||||
|
<li><a href="../../index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="../index.html">Module code</a></li>
|
||||||
|
<li class="breadcrumb-item active">Lib.Operands</li>
|
||||||
|
<li class="wy-breadcrumbs-aside">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||||
|
<div itemprop="articleBody">
|
||||||
|
|
||||||
|
<h1>Source code for Lib.Operands</h1><div class="highlight"><pre>
|
||||||
|
<span></span><span class="sd">"""</span>
|
||||||
|
<span class="sd">This file defines the base class :py:class:`Operand`</span>
|
||||||
|
<span class="sd">and its subclasses for different operands: :py:class:`Condition`,</span>
|
||||||
|
<span class="sd">:py:class:`DataLocation` and :py:class:`Function`.</span>
|
||||||
|
|
||||||
|
<span class="sd">The class :py:class:`DataLocation` itself has subclasses:</span>
|
||||||
|
<span class="sd">:py:class:`Register`, :py:class:`Offset` for address in memory,</span>
|
||||||
|
<span class="sd">:py:class:`Immediate` for constants and :py:class:`Temporary`</span>
|
||||||
|
<span class="sd">for location not yet allocated.</span>
|
||||||
|
|
||||||
|
<span class="sd">This file also define shortcuts for registers in RISCV.</span>
|
||||||
|
<span class="sd">"""</span>
|
||||||
|
|
||||||
|
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">List</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">MiniCParser</span> <span class="kn">import</span> <span class="n">MiniCParser</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Errors</span> <span class="kn">import</span> <span class="n">MiniCInternalError</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Operand"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Operand">[docs]</a><span class="k">class</span> <span class="nc">Operand</span><span class="p">():</span>
|
||||||
|
|
||||||
|
<span class="k">pass</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="c1"># signed version for riscv</span>
|
||||||
|
<span class="n">all_ops</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'blt'</span><span class="p">,</span> <span class="s1">'bgt'</span><span class="p">,</span> <span class="s1">'beq'</span><span class="p">,</span> <span class="s1">'bne'</span><span class="p">,</span> <span class="s1">'ble'</span><span class="p">,</span> <span class="s1">'bge'</span><span class="p">,</span> <span class="s1">'beqz'</span><span class="p">,</span> <span class="s1">'bnez'</span><span class="p">]</span>
|
||||||
|
<span class="n">opdict</span> <span class="o">=</span> <span class="p">{</span><span class="n">MiniCParser</span><span class="o">.</span><span class="n">LT</span><span class="p">:</span> <span class="s1">'blt'</span><span class="p">,</span> <span class="n">MiniCParser</span><span class="o">.</span><span class="n">GT</span><span class="p">:</span> <span class="s1">'bgt'</span><span class="p">,</span>
|
||||||
|
<span class="n">MiniCParser</span><span class="o">.</span><span class="n">LTEQ</span><span class="p">:</span> <span class="s1">'ble'</span><span class="p">,</span> <span class="n">MiniCParser</span><span class="o">.</span><span class="n">GTEQ</span><span class="p">:</span> <span class="s1">'bge'</span><span class="p">,</span>
|
||||||
|
<span class="n">MiniCParser</span><span class="o">.</span><span class="n">NEQ</span><span class="p">:</span> <span class="s1">'bne'</span><span class="p">,</span> <span class="n">MiniCParser</span><span class="o">.</span><span class="n">EQ</span><span class="p">:</span> <span class="s1">'beq'</span><span class="p">}</span>
|
||||||
|
<span class="n">opnot_dict</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'bgt'</span><span class="p">:</span> <span class="s1">'ble'</span><span class="p">,</span>
|
||||||
|
<span class="s1">'bge'</span><span class="p">:</span> <span class="s1">'blt'</span><span class="p">,</span>
|
||||||
|
<span class="s1">'blt'</span><span class="p">:</span> <span class="s1">'bge'</span><span class="p">,</span>
|
||||||
|
<span class="s1">'ble'</span><span class="p">:</span> <span class="s1">'bgt'</span><span class="p">,</span>
|
||||||
|
<span class="s1">'beq'</span><span class="p">:</span> <span class="s1">'bne'</span><span class="p">,</span>
|
||||||
|
<span class="s1">'bne'</span><span class="p">:</span> <span class="s1">'beq'</span><span class="p">,</span>
|
||||||
|
<span class="s1">'beqz'</span><span class="p">:</span> <span class="s1">'bnez'</span><span class="p">,</span>
|
||||||
|
<span class="s1">'bnez'</span><span class="p">:</span> <span class="s1">'beqz'</span><span class="p">}</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Condition"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Condition">[docs]</a><span class="k">class</span> <span class="nc">Condition</span><span class="p">(</span><span class="n">Operand</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Condition, i.e. comparison operand for a CondJump.</span>
|
||||||
|
|
||||||
|
<span class="sd"> Example usage :</span>
|
||||||
|
|
||||||
|
<span class="sd"> - Condition('beq') = branch if equal.</span>
|
||||||
|
<span class="sd"> - Condition(MiniCParser.LT) = branch if lower than.</span>
|
||||||
|
<span class="sd"> - ...</span>
|
||||||
|
|
||||||
|
<span class="sd"> The constructor's argument shall be a string in the list all_ops, or a</span>
|
||||||
|
<span class="sd"> comparison operator in MiniCParser.LT, MiniCParser.GT, ... (one of the keys</span>
|
||||||
|
<span class="sd"> in opdict).</span>
|
||||||
|
|
||||||
|
<span class="sd"> A 'negate' method allows getting the negation of this condition.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
|
||||||
|
<span class="n">_op</span><span class="p">:</span> <span class="nb">str</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">optype</span><span class="p">):</span>
|
||||||
|
<span class="k">if</span> <span class="n">optype</span> <span class="ow">in</span> <span class="n">opdict</span><span class="p">:</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_op</span> <span class="o">=</span> <span class="n">opdict</span><span class="p">[</span><span class="n">optype</span><span class="p">]</span>
|
||||||
|
<span class="k">elif</span> <span class="nb">str</span><span class="p">(</span><span class="n">optype</span><span class="p">)</span> <span class="ow">in</span> <span class="n">all_ops</span><span class="p">:</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_op</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">optype</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="k">raise</span> <span class="n">MiniCInternalError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Unsupported comparison operator </span><span class="si">{</span><span class="n">optype</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Condition.negate"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Condition.negate">[docs]</a> <span class="k">def</span> <span class="nf">negate</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="s1">'Condition'</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Return the opposite condition."""</span>
|
||||||
|
<span class="k">return</span> <span class="n">Condition</span><span class="p">(</span><span class="n">opnot_dict</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_op</span><span class="p">])</span></div>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_op</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Function"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Function">[docs]</a><span class="k">class</span> <span class="nc">Function</span><span class="p">(</span><span class="n">Operand</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Operand for build-in function call."""</span>
|
||||||
|
|
||||||
|
<span class="n">_name</span><span class="p">:</span> <span class="nb">str</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_name</span> <span class="o">=</span> <span class="n">name</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_name</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="DataLocation"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.DataLocation">[docs]</a><span class="k">class</span> <span class="nc">DataLocation</span><span class="p">(</span><span class="n">Operand</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">""" A Data Location is either a register, a temporary</span>
|
||||||
|
<span class="sd"> or a place in memory (offset).</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
|
||||||
|
<span class="k">pass</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="c1"># map for register shortcuts</span>
|
||||||
|
<span class="n">reg_map</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">([(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">'zero'</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'ra'</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">'sp'</span><span class="p">)]</span> <span class="o">+</span> <span class="c1"># no (3, 'gp') nor (4, 'tp')</span>
|
||||||
|
<span class="p">[(</span><span class="n">i</span><span class="o">+</span><span class="mi">5</span><span class="p">,</span> <span class="s1">'t'</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">)]</span> <span class="o">+</span>
|
||||||
|
<span class="p">[(</span><span class="mi">8</span><span class="p">,</span> <span class="s1">'fp'</span><span class="p">),</span> <span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="s1">'s1'</span><span class="p">)]</span> <span class="o">+</span>
|
||||||
|
<span class="p">[(</span><span class="n">i</span><span class="o">+</span><span class="mi">10</span><span class="p">,</span> <span class="s1">'a'</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">)]</span> <span class="o">+</span>
|
||||||
|
<span class="p">[(</span><span class="n">i</span><span class="o">+</span><span class="mi">18</span><span class="p">,</span> <span class="s1">'s'</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">2</span><span class="p">))</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)]</span> <span class="o">+</span>
|
||||||
|
<span class="p">[(</span><span class="n">i</span><span class="o">+</span><span class="mi">28</span><span class="p">,</span> <span class="s1">'t'</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">3</span><span class="p">))</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">)])</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Register"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Register">[docs]</a><span class="k">class</span> <span class="nc">Register</span><span class="p">(</span><span class="n">DataLocation</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">""" A (physical) register."""</span>
|
||||||
|
|
||||||
|
<span class="n">_number</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">number</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_number</span> <span class="o">=</span> <span class="n">number</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">_number</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">reg_map</span><span class="p">:</span>
|
||||||
|
<span class="k">raise</span> <span class="n">MiniCInternalError</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Register number </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">_number</span><span class="si">}</span><span class="s2"> should not be used"</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">reg_map</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">_number</span><span class="p">]))</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__eq__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">other</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">other</span><span class="p">,</span> <span class="n">Register</span><span class="p">)</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">_number</span> <span class="o">==</span> <span class="n">other</span><span class="o">.</span><span class="n">_number</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__hash__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_number</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="c1"># Shortcuts for registers in RISCV</span>
|
||||||
|
<span class="c1"># Only integer registers</span>
|
||||||
|
|
||||||
|
<span class="c1">#: Zero register</span>
|
||||||
|
<span class="n">ZERO</span> <span class="o">=</span> <span class="n">Register</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
|
||||||
|
<span class="c1">#:</span>
|
||||||
|
<span class="n">RA</span> <span class="o">=</span> <span class="n">Register</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||||||
|
<span class="c1">#:</span>
|
||||||
|
<span class="n">SP</span> <span class="o">=</span> <span class="n">Register</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
|
||||||
|
<span class="c1">#: Register not used for this course</span>
|
||||||
|
<span class="n">GP</span> <span class="o">=</span> <span class="n">Register</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
|
||||||
|
<span class="c1">#: Register not used for this course</span>
|
||||||
|
<span class="n">TP</span> <span class="o">=</span> <span class="n">Register</span><span class="p">(</span><span class="mi">4</span><span class="p">)</span>
|
||||||
|
<span class="c1">#:</span>
|
||||||
|
<span class="n">A</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">Register</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">10</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">))</span>
|
||||||
|
<span class="c1">#:</span>
|
||||||
|
<span class="n">S</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">Register</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">8</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">))</span> <span class="o">+</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">Register</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">18</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>
|
||||||
|
<span class="c1">#:</span>
|
||||||
|
<span class="n">T</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">Register</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">5</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">3</span><span class="p">))</span> <span class="o">+</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">Register</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">28</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">4</span><span class="p">))</span>
|
||||||
|
|
||||||
|
|
||||||
|
<span class="c1">#:</span>
|
||||||
|
<span class="n">A0</span> <span class="o">=</span> <span class="n">A</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># function args/return Values: A0, A1</span>
|
||||||
|
<span class="c1">#:</span>
|
||||||
|
<span class="n">A1</span> <span class="o">=</span> <span class="n">A</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
|
||||||
|
<span class="c1">#: Frame Pointer = Saved register 0</span>
|
||||||
|
<span class="n">FP</span> <span class="o">=</span> <span class="n">S</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||||||
|
|
||||||
|
<span class="c1">#: General purpose registers, usable for the allocator</span>
|
||||||
|
<span class="n">GP_REGS</span> <span class="o">=</span> <span class="n">S</span><span class="p">[</span><span class="mi">4</span><span class="p">:]</span> <span class="o">+</span> <span class="n">T</span> <span class="c1"># s0, s1, s2 and s3 are special</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Offset"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Offset">[docs]</a><span class="k">class</span> <span class="nc">Offset</span><span class="p">(</span><span class="n">DataLocation</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">""" Offset = address in memory computed with base + offset."""</span>
|
||||||
|
|
||||||
|
<span class="n">_basereg</span><span class="p">:</span> <span class="n">Register</span>
|
||||||
|
<span class="n">_offset</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">basereg</span><span class="p">:</span> <span class="n">Register</span><span class="p">,</span> <span class="n">offset</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_basereg</span> <span class="o">=</span> <span class="n">basereg</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_offset</span> <span class="o">=</span> <span class="n">offset</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="p">(</span><span class="s2">"</span><span class="si">{}</span><span class="s2">(</span><span class="si">{}</span><span class="s2">)"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_offset</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_basereg</span><span class="p">))</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Offset.get_offset"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Offset.get_offset">[docs]</a> <span class="k">def</span> <span class="nf">get_offset</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Return the value of the offset."""</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_offset</span></div></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Immediate"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Immediate">[docs]</a><span class="k">class</span> <span class="nc">Immediate</span><span class="p">(</span><span class="n">DataLocation</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Immediate operand (integer)."""</span>
|
||||||
|
|
||||||
|
<span class="n">_val</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">val</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_val</span> <span class="o">=</span> <span class="n">val</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_val</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Temporary"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Temporary">[docs]</a><span class="k">class</span> <span class="nc">Temporary</span><span class="p">(</span><span class="n">DataLocation</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Temporary, a location that has not been allocated yet.</span>
|
||||||
|
<span class="sd"> It will later be mapped to a physical register (Register) or to a memory location (Offset).</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
|
||||||
|
<span class="n">_number</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
|
<span class="n">_pool</span><span class="p">:</span> <span class="s1">'TemporaryPool'</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">number</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">pool</span><span class="p">:</span> <span class="s1">'TemporaryPool'</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_number</span> <span class="o">=</span> <span class="n">number</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_pool</span> <span class="o">=</span> <span class="n">pool</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="p">(</span><span class="s2">"temp_</span><span class="si">{}</span><span class="s2">"</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_number</span><span class="p">)))</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Temporary.get_alloced_loc"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Temporary.get_alloced_loc">[docs]</a> <span class="k">def</span> <span class="nf">get_alloced_loc</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">DataLocation</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Return the DataLocation allocated to this Temporary."""</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">get_alloced_loc</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span></div></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="TemporaryPool"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.TemporaryPool">[docs]</a><span class="k">class</span> <span class="nc">TemporaryPool</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Manage a pool of temporaries."""</span>
|
||||||
|
|
||||||
|
<span class="n">_all_temps</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Temporary</span><span class="p">]</span>
|
||||||
|
<span class="n">_current_num</span><span class="p">:</span> <span class="nb">int</span>
|
||||||
|
<span class="n">_allocation</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="n">Temporary</span><span class="p">,</span> <span class="n">DataLocation</span><span class="p">]</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_all_temps</span> <span class="o">=</span> <span class="p">[]</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_current_num</span> <span class="o">=</span> <span class="mi">0</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_allocation</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="TemporaryPool.get_all_temps"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.TemporaryPool.get_all_temps">[docs]</a> <span class="k">def</span> <span class="nf">get_all_temps</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">List</span><span class="p">[</span><span class="n">Temporary</span><span class="p">]:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Return all the temporaries of the pool."""</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_all_temps</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="TemporaryPool.get_alloced_loc"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.TemporaryPool.get_alloced_loc">[docs]</a> <span class="k">def</span> <span class="nf">get_alloced_loc</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">:</span> <span class="n">Temporary</span><span class="p">)</span> <span class="o">-></span> <span class="n">DataLocation</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Get the actual DataLocation allocated for the temporary t."""</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_allocation</span><span class="p">[</span><span class="n">t</span><span class="p">]</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="TemporaryPool.add_tmp"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.TemporaryPool.add_tmp">[docs]</a> <span class="k">def</span> <span class="nf">add_tmp</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">:</span> <span class="n">Temporary</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Add a temporary to the pool."""</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_all_temps</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_allocation</span><span class="p">[</span><span class="n">t</span><span class="p">]</span> <span class="o">=</span> <span class="n">t</span> <span class="c1"># While no allocation, return the temporary itself</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="TemporaryPool.set_temp_allocation"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.TemporaryPool.set_temp_allocation">[docs]</a> <span class="k">def</span> <span class="nf">set_temp_allocation</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">allocation</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="n">Temporary</span><span class="p">,</span> <span class="n">DataLocation</span><span class="p">])</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Give a mapping from temporaries to actual registers.</span>
|
||||||
|
<span class="sd"> The argument allocation must be a dict from Temporary to</span>
|
||||||
|
<span class="sd"> DataLocation other than Temporary (typically Register or Offset).</span>
|
||||||
|
<span class="sd"> Typing enforces that keys are Temporary and values are Datalocation.</span>
|
||||||
|
<span class="sd"> We check the values are indeed not Temporary.</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">allocation</span><span class="o">.</span><span class="n">values</span><span class="p">():</span>
|
||||||
|
<span class="k">assert</span> <span class="ow">not</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">Temporary</span><span class="p">),</span> <span class="p">(</span>
|
||||||
|
<span class="s2">"Incorrect allocation scheme: value "</span> <span class="o">+</span>
|
||||||
|
<span class="nb">str</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="o">+</span> <span class="s2">" is a Temporary."</span><span class="p">)</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_allocation</span> <span class="o">=</span> <span class="n">allocation</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="TemporaryPool.fresh_tmp"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.TemporaryPool.fresh_tmp">[docs]</a> <span class="k">def</span> <span class="nf">fresh_tmp</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Temporary</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Give a new fresh Temporary and add it to the pool."""</span>
|
||||||
|
<span class="n">t</span> <span class="o">=</span> <span class="n">Temporary</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_current_num</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_current_num</span> <span class="o">+=</span> <span class="mi">1</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">add_tmp</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
|
||||||
|
<span class="k">return</span> <span class="n">t</span></div></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Renamer"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Renamer">[docs]</a><span class="k">class</span> <span class="nc">Renamer</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Manage a renaming of temporaries."""</span>
|
||||||
|
|
||||||
|
<span class="n">_pool</span><span class="p">:</span> <span class="n">TemporaryPool</span>
|
||||||
|
<span class="n">_env</span><span class="p">:</span> <span class="n">Dict</span><span class="p">[</span><span class="n">Temporary</span><span class="p">,</span> <span class="n">Temporary</span><span class="p">]</span>
|
||||||
|
|
||||||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">pool</span><span class="p">:</span> <span class="n">TemporaryPool</span><span class="p">):</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_pool</span> <span class="o">=</span> <span class="n">pool</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_env</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">()</span>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Renamer.fresh"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Renamer.fresh">[docs]</a> <span class="k">def</span> <span class="nf">fresh</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">:</span> <span class="n">Temporary</span><span class="p">)</span> <span class="o">-></span> <span class="n">Temporary</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Give a fresh rename for a Temporary."""</span>
|
||||||
|
<span class="n">new_t</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">fresh_tmp</span><span class="p">()</span>
|
||||||
|
<span class="bp">self</span><span class="o">.</span><span class="n">_env</span><span class="p">[</span><span class="n">t</span><span class="p">]</span> <span class="o">=</span> <span class="n">new_t</span>
|
||||||
|
<span class="k">return</span> <span class="n">new_t</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Renamer.replace"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Renamer.replace">[docs]</a> <span class="k">def</span> <span class="nf">replace</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">:</span> <span class="n">Temporary</span><span class="p">)</span> <span class="o">-></span> <span class="n">Temporary</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Give the rename for a Temporary (which is itself if it is not renamed)."""</span>
|
||||||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">_env</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Renamer.defined"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Renamer.defined">[docs]</a> <span class="k">def</span> <span class="nf">defined</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">t</span><span class="p">:</span> <span class="n">Temporary</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""True if the Temporary is renamed."""</span>
|
||||||
|
<span class="k">return</span> <span class="n">t</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">_env</span></div>
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="Renamer.copy"><a class="viewcode-back" href="../../api/Lib.Operands.html#Lib.Operands.Renamer.copy">[docs]</a> <span class="k">def</span> <span class="nf">copy</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Give a copy of the Renamer."""</span>
|
||||||
|
<span class="n">r</span> <span class="o">=</span> <span class="n">Renamer</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_pool</span><span class="p">)</span>
|
||||||
|
<span class="n">r</span><span class="o">.</span><span class="n">_env</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_env</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span>
|
||||||
|
<span class="k">return</span> <span class="n">r</span></div></div>
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div role="contentinfo">
|
||||||
|
<p>© Copyright 2023, compil-lyon.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||||
|
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||||
|
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||||
|
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
jQuery(function () {
|
||||||
|
SphinxRtdTheme.Navigation.enable(true);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
198
docs/html/_modules/Lib/RiscV.html
Normal file
198
docs/html/_modules/Lib/RiscV.html
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html class="writer-html5" lang="en" >
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Lib.RiscV — MiniC documentation</title>
|
||||||
|
<link rel="stylesheet" href="../../_static/pygments.css" type="text/css" />
|
||||||
|
<link rel="stylesheet" href="../../_static/css/theme.css" type="text/css" />
|
||||||
|
<!--[if lt IE 9]>
|
||||||
|
<script src="../../_static/js/html5shiv.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
|
||||||
|
<script data-url_root="../../" id="documentation_options" src="../../_static/documentation_options.js"></script>
|
||||||
|
<script src="../../_static/doctools.js"></script>
|
||||||
|
<script src="../../_static/sphinx_highlight.js"></script>
|
||||||
|
<script src="../../_static/js/theme.js"></script>
|
||||||
|
<link rel="index" title="Index" href="../../genindex.html" />
|
||||||
|
<link rel="search" title="Search" href="../../search.html" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="wy-body-for-nav">
|
||||||
|
<div class="wy-grid-for-nav">
|
||||||
|
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
|
||||||
|
<div class="wy-side-scroll">
|
||||||
|
<div class="wy-side-nav-search" >
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a href="../../index.html" class="icon icon-home">
|
||||||
|
MiniC
|
||||||
|
</a>
|
||||||
|
<div role="search">
|
||||||
|
<form id="rtd-search-form" class="wy-form" action="../../search.html" method="get">
|
||||||
|
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
|
||||||
|
<input type="hidden" name="check_keywords" value="yes" />
|
||||||
|
<input type="hidden" name="area" value="default" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
|
||||||
|
<p class="caption" role="heading"><span class="caption-text">Contents:</span></p>
|
||||||
|
<ul>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Errors.html">Base library - Errors</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Statement.html">Base library - Statement</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.RiscV.html">Base library - RISC-V instructions</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Operands.html">Base library - Operands</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.FunctionData.html">Base library - Function data</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.LinearCode.html">Linear intermediate representation</a></li>
|
||||||
|
<li class="toctree-l1"><a class="reference internal" href="../../api/Lib.Allocator.html">Temporary allocation</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
|
||||||
|
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
|
||||||
|
<a href="../../index.html">MiniC</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="wy-nav-content">
|
||||||
|
<div class="rst-content">
|
||||||
|
<div role="navigation" aria-label="Page navigation">
|
||||||
|
<ul class="wy-breadcrumbs">
|
||||||
|
<li><a href="../../index.html" class="icon icon-home" aria-label="Home"></a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="../index.html">Module code</a></li>
|
||||||
|
<li class="breadcrumb-item active">Lib.RiscV</li>
|
||||||
|
<li class="wy-breadcrumbs-aside">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<hr/>
|
||||||
|
</div>
|
||||||
|
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
|
||||||
|
<div itemprop="articleBody">
|
||||||
|
|
||||||
|
<h1>Source code for Lib.RiscV</h1><div class="highlight"><pre>
|
||||||
|
<span></span><span class="sd">"""</span>
|
||||||
|
<span class="sd">MIF08, CAP, CodeGeneration, RiscV API</span>
|
||||||
|
<span class="sd">Functions to define instructions.</span>
|
||||||
|
<span class="sd">"""</span>
|
||||||
|
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Errors</span> <span class="kn">import</span> <span class="n">MiniCInternalError</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Operands</span> <span class="kn">import</span> <span class="p">(</span><span class="n">Condition</span><span class="p">,</span> <span class="n">Immediate</span><span class="p">,</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">Function</span><span class="p">)</span>
|
||||||
|
<span class="kn">from</span> <span class="nn">Lib.Statement</span> <span class="kn">import</span> <span class="p">(</span><span class="n">Instru3A</span><span class="p">,</span> <span class="n">AbsoluteJump</span><span class="p">,</span> <span class="n">ConditionalJump</span><span class="p">,</span> <span class="n">Label</span><span class="p">)</span>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="call"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.call">[docs]</a><span class="k">def</span> <span class="nf">call</span><span class="p">(</span><span class="n">function</span><span class="p">:</span> <span class="n">Function</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Function call."""</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s1">'call'</span><span class="p">,</span> <span class="n">function</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="jump"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.jump">[docs]</a><span class="k">def</span> <span class="nf">jump</span><span class="p">(</span><span class="n">label</span><span class="p">:</span> <span class="n">Label</span><span class="p">)</span> <span class="o">-></span> <span class="n">AbsoluteJump</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Unconditional jump to label."""</span>
|
||||||
|
<span class="k">return</span> <span class="n">AbsoluteJump</span><span class="p">(</span><span class="n">label</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="conditional_jump"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.conditional_jump">[docs]</a><span class="k">def</span> <span class="nf">conditional_jump</span><span class="p">(</span><span class="n">label</span><span class="p">:</span> <span class="n">Label</span><span class="p">,</span> <span class="n">op1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">cond</span><span class="p">:</span> <span class="n">Condition</span><span class="p">,</span> <span class="n">op2</span><span class="p">:</span> <span class="n">Operand</span><span class="p">):</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Add a conditional jump to the code.</span>
|
||||||
|
<span class="sd"> This is a wrapper around bge, bgt, beq, ... c is a Condition, like</span>
|
||||||
|
<span class="sd"> Condition('bgt'), Condition(MiniCParser.EQ), ...</span>
|
||||||
|
<span class="sd"> """</span>
|
||||||
|
<span class="k">return</span> <span class="n">ConditionalJump</span><span class="p">(</span><span class="n">cond</span><span class="o">=</span><span class="n">cond</span><span class="p">,</span> <span class="n">op1</span><span class="o">=</span><span class="n">op1</span><span class="p">,</span> <span class="n">op2</span><span class="o">=</span><span class="n">op2</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="n">label</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="add"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.add">[docs]</a><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">sr2orimm7</span><span class="p">,</span> <span class="n">Immediate</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"addi"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"add"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="mul"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.mul">[docs]</a><span class="k">def</span> <span class="nf">mul</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">sr2orimm7</span><span class="p">,</span> <span class="n">Immediate</span><span class="p">):</span>
|
||||||
|
<span class="k">raise</span> <span class="n">MiniCInternalError</span><span class="p">(</span><span class="s2">"Cant multiply by an immediate"</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"mul"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="div"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.div">[docs]</a><span class="k">def</span> <span class="nf">div</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">sr2orimm7</span><span class="p">,</span> <span class="n">Immediate</span><span class="p">):</span>
|
||||||
|
<span class="k">raise</span> <span class="n">MiniCInternalError</span><span class="p">(</span><span class="s2">"Cant divide by an immediate"</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"div"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="rem"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.rem">[docs]</a><span class="k">def</span> <span class="nf">rem</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">sr2orimm7</span><span class="p">,</span> <span class="n">Immediate</span><span class="p">):</span>
|
||||||
|
<span class="k">raise</span> <span class="n">MiniCInternalError</span><span class="p">(</span><span class="s2">"Cant divide by an immediate"</span><span class="p">)</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"rem"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="sub"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.sub">[docs]</a><span class="k">def</span> <span class="nf">sub</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">sr2orimm7</span><span class="p">,</span> <span class="n">Immediate</span><span class="p">):</span>
|
||||||
|
<span class="k">raise</span> <span class="n">MiniCInternalError</span><span class="p">(</span><span class="s2">"Cant substract by an immediate"</span><span class="p">)</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"sub"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="land"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.land">[docs]</a><span class="k">def</span> <span class="nf">land</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""And instruction (cannot be called `and` due to Python and)."""</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"and"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="lor"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.lor">[docs]</a><span class="k">def</span> <span class="nf">lor</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="w"> </span><span class="sd">"""Or instruction (cannot be called `or` due to Python or)."""</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"or"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="xor"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.xor">[docs]</a><span class="k">def</span> <span class="nf">xor</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr1</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span> <span class="c1"># pragma: no cover</span>
|
||||||
|
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">sr2orimm7</span><span class="p">,</span> <span class="n">Immediate</span><span class="p">):</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"xori"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span>
|
||||||
|
<span class="k">else</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"xor"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr1</span><span class="p">,</span> <span class="n">sr2orimm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="li"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.li">[docs]</a><span class="k">def</span> <span class="nf">li</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">imm7</span><span class="p">:</span> <span class="n">Immediate</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"li"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">imm7</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="mv"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.mv">[docs]</a><span class="k">def</span> <span class="nf">mv</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">sr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"mv"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">sr</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="ld"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.ld">[docs]</a><span class="k">def</span> <span class="nf">ld</span><span class="p">(</span><span class="n">dr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">mem</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"ld"</span><span class="p">,</span> <span class="n">dr</span><span class="p">,</span> <span class="n">mem</span><span class="p">)</span></div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="viewcode-block" id="sd"><a class="viewcode-back" href="../../api/Lib.RiscV.html#Lib.RiscV.sd">[docs]</a><span class="k">def</span> <span class="nf">sd</span><span class="p">(</span><span class="n">sr</span><span class="p">:</span> <span class="n">Operand</span><span class="p">,</span> <span class="n">mem</span><span class="p">:</span> <span class="n">Operand</span><span class="p">)</span> <span class="o">-></span> <span class="n">Instru3A</span><span class="p">:</span>
|
||||||
|
<span class="k">return</span> <span class="n">Instru3A</span><span class="p">(</span><span class="s2">"sd"</span><span class="p">,</span> <span class="n">sr</span><span class="p">,</span> <span class="n">mem</span><span class="p">)</span></div>
|
||||||
|
</pre></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<div role="contentinfo">
|
||||||
|
<p>© Copyright 2023, compil-lyon.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
|
||||||
|
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
|
||||||
|
provided by <a href="https://readthedocs.org">Read the Docs</a>.
|
||||||
|
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
jQuery(function () {
|
||||||
|
SphinxRtdTheme.Navigation.enable(true);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user