Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
"""
|
||||
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_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 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:
|
||||
print(str(name), blk._label)
|
||||
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)
|
||||
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
@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 __hash__(self):
|
||||
return hash(super)
|
||||
|
||||
|
||||
Terminator = Return | AbsoluteJump | BranchingTerminator
|
||||
|
||||
|
||||
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 None:
|
||||
if next_label:
|
||||
return AbsoluteJump(next_label)
|
||||
else:
|
||||
return Return()
|
||||
+8
-1
@@ -216,7 +216,14 @@ liveness file not found for {}.".format(form))
|
||||
s = "{}.{}.exitssa.dot".format(basename, code.fdata.get_name())
|
||||
print("CFG after SSA:", s)
|
||||
code.print_dot(s, view=True)
|
||||
code.print_code(output, comment=comment)
|
||||
from Lib.LinearCode import LinearCode # type: ignore[import]
|
||||
if isinstance(code, LinearCode):
|
||||
code.print_code(output, comment=comment)
|
||||
else:
|
||||
from Lib.CFG import CFG # type: ignore[import]
|
||||
from TP04.LinearizeCFG import linearize # type: ignore[import]
|
||||
assert (isinstance(code, CFG))
|
||||
code.print_code(output, linearize=linearize, comment=comment)
|
||||
if debug:
|
||||
visitor3.printSymbolTable()
|
||||
|
||||
|
||||
@@ -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 (Lab4b, Exercise 3)
|
||||
# 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 (Lab4b, Exercise 3)
|
||||
# Extract the last instruction from inner_statements if it is a jump, or do nothing
|
||||
raise NotImplementedError() # TODO (Lab4b, Exercise 3)
|
||||
# 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
|
||||
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
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
|
||||
|
||||
|
||||
def ordered_blocks_list(cfg) -> List[Block]:
|
||||
"""
|
||||
Compute a list of blocks with optimized ordering for linearization.
|
||||
"""
|
||||
# TODO (Lab4b, Extension)
|
||||
return cfg.get_blocks()
|
||||
|
||||
|
||||
def linearize(cfg) -> List[Statement]:
|
||||
"""
|
||||
Linearize the given control flow graph as a list of instructions.
|
||||
"""
|
||||
# TODO (Lab 4b, Exercise 5)
|
||||
l: List[Statement] = [] # Linearized CFG
|
||||
blocks: List[Block] = ordered_blocks_list(cfg)
|
||||
for j, 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
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "printlib.h"
|
||||
|
||||
int main() {
|
||||
|
||||
int n,u;
|
||||
n=6;
|
||||
println_int(n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// EXPECTED
|
||||
// 6
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "printlib.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
int x;
|
||||
x = 0;
|
||||
while (x < 4)
|
||||
{
|
||||
x = x + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// EXPECTED
|
||||
Binary file not shown.
Reference in New Issue
Block a user