Skip to content
Snippets Groups Projects
Commit 71873050 authored by Ettayeb Yassine's avatar Ettayeb Yassine
Browse files

TP3: Ajout de l'élargissement

parent 90b8acfc
Branches
No related tags found
No related merge requests found
...@@ -17,6 +17,7 @@ class Label: ...@@ -17,6 +17,7 @@ class Label:
@dataclass @dataclass
class LSkip(Label): class LSkip(Label):
"""label skip (does nothing)""" """label skip (does nothing)"""
is_back_edge: bool
def __str__(self): def __str__(self):
return "skip" return "skip"
...@@ -78,47 +79,41 @@ class Graph: ...@@ -78,47 +79,41 @@ class Graph:
self.nodes += [n2] self.nodes += [n2]
n1.succs += [(n2,l)] n1.succs += [(n2,l)]
def cfg_aux(g:Graph,e:Node,i:Node,instr:Instr) -> Node: def cfg_aux(g: "Cfg", e: Node, i: Node, instr: Instr) -> Node:
"""auxiliary function to build a CfG
takes as argument a graph, the error node, the node starting
the current instruction, and the instruction itself, returns
the final node of current instruction
"""
match instr: match instr:
case ISkip(): case ISkip():
f = g.new_node()
g.new_edge(i,f,LSkip())
return f
case ISet(var,expr):
f = g.new_node() f = g.new_node()
g.new_edge(i,f,LSet(var,expr)) g.new_edge(i, f, LSkip(is_back_edge=False))
g.new_edge(i,e,LErr(expr))
return f return f
case ISeq(first,second):
m = cfg_aux(g,e,i,first) case ISet(v, expr):
return cfg_aux(g,e,m,second)
case ICond(cond,true_branch,false_branch):
t = g.new_node()
f = g.new_node()
g.new_edge(i,t,LTest(cond))
g.new_edge(i,f,LTest(BENeg(cond)))
g.new_edge(i,e,LErr(cond))
tf = cfg_aux(g,e,t,true_branch)
ff = cfg_aux(g,e,f,false_branch)
f = g.new_node() f = g.new_node()
g.new_edge(tf,f,LSkip()) g.new_edge(i, f, LSet(v, expr))
g.new_edge(ff,f,LSkip()) g.new_edge(i, e, LErr(expr))
return f return f
case ILoop(cond,body):
case ISeq(i1, i2):
m = cfg_aux(g, e, i, i1)
return cfg_aux(g, e, m, i2)
case ICond(cond, i1, i2):
f = g.new_node()
g.new_edge(i, f, LTest(BENeg(cond)))
g.new_edge(i, e, LErr(cond))
t = cfg_aux(g, e, f, i1)
g.new_edge(i, f, LTest(cond))
return cfg_aux(g, e, t, i2)
case ILoop(cond, body):
ib = g.new_node() ib = g.new_node()
f = g.new_node() f = g.new_node()
g.new_edge(i,ib,LTest(cond)) g.new_edge(i, ib, LTest(cond)) # vers boucle si cond vrai
g.new_edge(i,f,LTest(BENeg(cond))) g.new_edge(i, f, LTest(BENeg(cond))) # sortie si cond faux
g.new_edge(i,e,LErr(cond)) g.new_edge(i, e, LErr(cond)) # erreur si test échoue
fb = cfg_aux(g,e,ib,body) fb = cfg_aux(g, e, ib, body)
g.new_edge(fb,i,LSkip()) g.new_edge(fb, i, LSkip(is_back_edge=True)) # <-- arc de retour
return f return f
case _: assert False
class Cfg: class Cfg:
"""describes a CfG""" """describes a CfG"""
......
...@@ -10,6 +10,11 @@ from typing import Protocol ...@@ -10,6 +10,11 @@ from typing import Protocol
class Transfer[S](Protocol): class Transfer[S](Protocol):
"""lists the functions that must be implemented for a static analysis over Lattice S""" """lists the functions that must be implemented for a static analysis over Lattice S"""
def widen(self, s1: S, s2: S) -> S:
"""widening operator used on back edges (default: join)"""
return self.join(s1, s2)
def bottom(self) -> S: def bottom(self) -> S:
"""the empty state, associated to unvisited nodes""" """the empty state, associated to unvisited nodes"""
... ...
...@@ -86,7 +91,7 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]: ...@@ -86,7 +91,7 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]:
output maps each node to the computed state output maps each node to the computed state
""" """
# initialisation # initialisation des états
mapping: dict[Node, T] = {} mapping: dict[Node, T] = {}
for node in cfg.g.nodes: for node in cfg.g.nodes:
if node == cfg.init_node: if node == cfg.init_node:
...@@ -94,17 +99,17 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]: ...@@ -94,17 +99,17 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]:
else: else:
mapping[node] = transfer.bottom() mapping[node] = transfer.bottom()
# initialisation de la worklist avec les successeurs du nœud initial # initialisation de la worklist avc les successeurs du noeud initial
worklist: list[tuple[Node, Node, Label]] = [] worklist: list[tuple[Node, Node, Label]] = []
for (dst, label) in cfg.init_node.succs: for (dst, label) in cfg.init_node.succs:
worklist.append((cfg.init_node, dst, label)) worklist.append((cfg.init_node, dst, label))
# itération principale # iter principale
while worklist: while worklist:
src, dst, label = worklist.pop(0) src, dst, label = worklist.pop(0)
src_state = mapping[src] src_state = mapping[src]
# applique la fonction de transfert selon le label # applique la fonction de transfert en fonction du label
match label: match label:
case LSkip(): case LSkip():
new_state = transfer.tr_skip(src_state) new_state = transfer.tr_skip(src_state)
...@@ -117,13 +122,15 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]: ...@@ -117,13 +122,15 @@ def fixpoint_iteration[T](transfer: Transfer[T], cfg: Cfg) -> dict[Node,T]:
case _: case _:
continue # ou raise NotImplementedError continue # ou raise NotImplementedError
# joindre avc l'ancien état # appliquer le widen sur un arc de retour, sinon un simple join
joined_state = transfer.join(mapping[dst], new_state) if isinstance(label, LSkip) and label.is_back_edge:
joined_state = transfer.widen(mapping[dst], new_state)
else:
joined_state = transfer.join(mapping[dst], new_state)
# si nouvel état apporte de l'information, on met à jour # Màj si l'état évolue
if not transfer.included(joined_state, mapping[dst]): if not transfer.included(joined_state, mapping[dst]):
mapping[dst] = joined_state mapping[dst] = joined_state
# + on ajoute les successeurs à la worklist
for (succ, lab) in dst.succs: for (succ, lab) in dst.succs:
worklist.append((dst, succ, lab)) worklist.append((dst, succ, lab))
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment