Skip to content

Commit 8d13ab9

Browse files
committed
Cumulative updates
1 parent 823a25d commit 8d13ab9

15 files changed

+495
-371
lines changed

src/tip/Tip.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ object Tip extends App {
117117
| -constprop enable constant propagation analysis
118118
| -interval enable interval analysis
119119
| -copyconstprop enable copy constant propagation analysis
120+
| -uninitvars enable possibly-uninitialized variables analysis
120121
|
121122
| For the dataflow analyses, the choice of fixpoint solver can be chosen by these modifiers
122123
| immediately after the analysis name (default: use the simple fixpoint solver):
@@ -212,7 +213,7 @@ object Tip extends App {
212213
// run the analysis
213214
log.verb(s"Performing ${an.getClass.getSimpleName}")
214215
val res = an.analyze().asInstanceOf[Map[CfgNode, _]]
215-
Output.output(file, DataFlowOutput(s), wcfg.toDot(Output.labeler(res), Output.dotIder), options.out)
216+
Output.output(file, DataFlowOutput(s), wcfg.toDot(Output.labeler(res, an.stateAfterNode), Output.dotIder), options.out)
216217
}
217218
}
218219
}
@@ -248,7 +249,7 @@ object Tip extends App {
248249
Output.transform(res.asInstanceOf[Map[(CallContext, CfgNode), _]])
249250
else
250251
res.asInstanceOf[Map[CfgNode, _]]
251-
Output.output(file, DataFlowOutput(s), wcfg.toDot(Output.labeler(res2), Output.dotIder), options.out)
252+
Output.output(file, DataFlowOutput(s), wcfg.toDot(Output.labeler(res2, an.stateAfterNode), Output.dotIder), options.out)
252253
}
253254
}
254255
}
@@ -333,7 +334,7 @@ object Tip extends App {
333334
options.andersen = true
334335
case "-steensgaard" =>
335336
options.steensgaard = true
336-
case "-sign" | "-livevars" | "-available" | "-vbusy" | "-reaching" | "-constprop" | "-interval" | "-copyconstprop" =>
337+
case "-sign" | "-livevars" | "-available" | "-vbusy" | "-reaching" | "-constprop" | "-interval" | "-copyconstprop" | "-uninitvars" =>
337338
options.dfAnalysis += dfa.withName(args(i).drop(1)) -> {
338339
if (i + 1 < args.length && dfo.values.map(_.toString()).contains(args(i + 1))) {
339340
i = i + 1

src/tip/analysis/AvailableExpAnalysis.scala

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,25 @@ import tip.lattices.{MapLattice, ReversePowersetLattice}
66
import tip.solvers.{SimpleMapLatticeFixpointSolver, SimpleWorklistFixpointSolver}
77
import tip.ast.AstNodeData.DeclarationData
88

9+
import scala.collection.immutable.Set
10+
911
/**
1012
* Base class for available expressions analysis.
1113
*/
12-
abstract class AvailableExpAnalysis(cfg: IntraproceduralProgramCfg)(implicit declData: DeclarationData) extends FlowSensitiveAnalysis[CfgNode](cfg) {
14+
abstract class AvailableExpAnalysis(cfg: IntraproceduralProgramCfg)(implicit declData: DeclarationData) extends FlowSensitiveAnalysis(true) {
1315

1416
import tip.cfg.CfgOps._
1517
import tip.ast.AstOps._
1618

1719
val allExps: Set[UnlabelledNode[AExpr]] = cfg.nodes.flatMap(_.appearingNonInputExpressions.map(UnlabelledNode[AExpr]))
1820

21+
val lattice: MapLattice[CfgNode, ReversePowersetLattice[UnlabelledNode[AExpr]]] = new MapLattice(new ReversePowersetLattice(allExps))
22+
23+
val domain: Set[CfgNode] = cfg.nodes
24+
1925
NoPointers.assertContainsProgram(cfg.prog)
2026
NoRecords.assertContainsProgram(cfg.prog)
2127

22-
val lattice = new MapLattice(cfg.nodes, new ReversePowersetLattice(allExps))
23-
2428
def transfer(n: CfgNode, s: lattice.sublattice.Element): lattice.sublattice.Element =
2529
n match {
2630
case _: CfgFunEntryNode => Set()

src/tip/analysis/ControlFlowAnalysis.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ import tip.ast.AstNodeData.{AstNodeWithDeclaration, DeclarationData}
77

88
import scala.language.implicitConversions
99

10+
/**
11+
* Control flow analysis.
12+
*/
1013
class ControlFlowAnalysis(program: AProgram)(implicit declData: DeclarationData)
1114
extends DepthFirstAstVisitor[Unit]
1215
with Analysis[Map[AstNode, Set[AFunDeclaration]]] {
@@ -18,7 +21,7 @@ class ControlFlowAnalysis(program: AProgram)(implicit declData: DeclarationData)
1821
}
1922

2023
case class AstVariable(n: AstNode) {
21-
override def toString = n match {
24+
override def toString: String = n match {
2225
case fun: AFunDeclaration => s"${fun.name}:${fun.loc}"
2326
case _ => n.toString
2427
}
@@ -46,7 +49,7 @@ class ControlFlowAnalysis(program: AProgram)(implicit declData: DeclarationData)
4649
* @param node the node for which it generates the constraints
4750
* @param arg unused for this visitor
4851
*/
49-
def visit(node: AstNode, arg: Unit) = {
52+
def visit(node: AstNode, arg: Unit): Unit = {
5053

5154
/**
5255
* Get the declaration if the supplied AstNode is an identifier,

src/tip/analysis/CopyConstantPropagationAnalysis.scala

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,44 @@ import tip.ast._
44
import tip.cfg._
55
import tip.lattices._
66
import tip.solvers._
7+
import tip.ast.AstNodeData.{AstNodeWithDeclaration, DeclarationData}
78

89
import scala.collection.mutable
9-
import tip.ast.AstNodeData.{AstNodeWithDeclaration, DeclarationData}
1010

1111
/**
1212
* Micro-transfer-functions for copy-constant-propagation analysis.
1313
*/
1414
trait CopyConstantPropagationAnalysisFunctions extends IDEAnalysis[ADeclaration, FlatLattice[Int]] {
1515

16+
NoPointers.assertContainsProgram(cfg.prog)
17+
NoRecords.assertContainsProgram(cfg.prog)
18+
1619
implicit val declData: DeclarationData
1720

18-
lazy val valuelattice = new FlatLattice[Int]()
21+
val valuelattice = new FlatLattice[Int]()
1922

20-
lazy val edgelattice: EdgeLattice[valuelattice.type] = new EdgeLattice(valuelattice)
23+
val edgelattice: EdgeFunctionLattice[valuelattice.type] = new EdgeFunctionLattice(valuelattice)
2124

2225
import cfg._
23-
import edgelattice.{ConstEdge, Edge, IdEdge}
24-
import edgelattice.valuelattice.{FlatEl, Top}
26+
import edgelattice._
27+
import edgelattice.valuelattice._
2528

26-
def edgesCallToEntry(d: DL, call: CfgCallNode, entry: CfgFunEntryNode): List[(DL, edgelattice.Edge)] =
27-
entry.data.params.zip(call.invocation.args).foldLeft(List[(DL, edgelattice.Edge)]()) {
29+
def edgesCallToEntry(d: DL, call: CfgCallNode, entry: CfgFunEntryNode): List[(DL, edgelattice.EdgeFunction)] =
30+
entry.data.params.zip(call.invocation.args).foldLeft(List[(DL, edgelattice.EdgeFunction)]()) {
2831
case (acc, (id, exp)) =>
2932
acc ++ assign(d, id, exp)
3033
}
3134

32-
def edgesExitToAfterCall(d: DL, exit: CfgFunExitNode, aftercall: CfgAfterCallNode): List[(DL, edgelattice.Edge)] =
35+
def edgesExitToAfterCall(d: DL, exit: CfgFunExitNode, aftercall: CfgAfterCallNode): List[(DL, edgelattice.EdgeFunction)] =
3336
assign(d, aftercall.targetIdentifier.declaration, AstOps.returnId)
3437

35-
def edgesCallToAfterCall(d2: DL, call: CfgCallNode, aftercall: CfgAfterCallNode): List[(DL, edgelattice.Edge)] =
38+
def edgesCallToAfterCall(d2: DL, call: CfgCallNode, aftercall: CfgAfterCallNode): List[(DL, edgelattice.EdgeFunction)] =
3639
d2 match {
3740
case Right(_) => List((d2, IdEdge()))
3841
case Left(a) => if (a == aftercall.targetIdentifier.declaration) List() else List((d2, IdEdge()))
3942
}
4043

41-
def edgesOther(d: DL, n: CfgNode): List[(DL, edgelattice.Edge)] =
44+
def edgesOther(d: DL, n: CfgNode): List[(DL, edgelattice.EdgeFunction)] =
4245
n match {
4346
case r: CfgStmtNode =>
4447
r.data match {
@@ -47,7 +50,7 @@ trait CopyConstantPropagationAnalysisFunctions extends IDEAnalysis[ADeclaration,
4750
case varr: AVarStmt =>
4851
d match {
4952
case Right(_) =>
50-
varr.declIds.foldLeft(List((d, IdEdge())): List[(DL, Edge)]) { (ps, id) => // identity edge from lambda to lambda
53+
varr.declIds.foldLeft(List((d, IdEdge())): List[(DL, EdgeFunction)]) { (ps, id) => // identity edge from lambda to lambda
5154
ps :+ (Left(id), ConstEdge(Top)) // top edge from lambda to each variable being declared
5255
}
5356
case Left(a) =>
@@ -84,8 +87,8 @@ trait CopyConstantPropagationAnalysisFunctions extends IDEAnalysis[ADeclaration,
8487
/**
8588
* Micro-transfer-functions for assigning an expression to an identifier.
8689
*/
87-
private def assign(d: DL, id: ADeclaration, exp: AExprOrIdentifierDeclaration): List[(DL, edgelattice.Edge)] = {
88-
val edges = mutable.ListBuffer[(DL, Edge)]()
90+
private def assign(d: DL, id: ADeclaration, exp: AExprOrIdentifierDeclaration): List[(DL, edgelattice.EdgeFunction)] = {
91+
val edges = mutable.ListBuffer[(DL, EdgeFunction)]()
8992
d match {
9093
case Right(_) =>
9194
edges += ((d, IdEdge())) // identity edge from lambda to lambda
@@ -116,22 +119,6 @@ trait CopyConstantPropagationAnalysisFunctions extends IDEAnalysis[ADeclaration,
116119
/**
117120
* Copy-constant-propagation analysis using IDE solver.
118121
*/
119-
class CopyConstantPropagationIDEAnalysis(val cfg: InterproceduralProgramCfg)(implicit val declData: DeclarationData)
120-
extends FlowSensitiveAnalysis[CfgNode](cfg) {
121-
122-
import tip.cfg.CfgOps._
123-
124-
val phase1 = new IDEPhase1Analysis[ADeclaration, FlatLattice[Int]](cfg) with CopyConstantPropagationAnalysisFunctions
125-
val phase2 = new IDEPhase2Analysis[ADeclaration, FlatLattice[Int]](cfg, phase1) with CopyConstantPropagationAnalysisFunctions
126-
127-
val declaredVars: Set[ADeclaration] = domain.flatMap(_.declaredVarsAndParams)
128-
129-
val lattice: MapLattice[CfgNode, MapLattice[ADeclaration, FlatLattice[Int]]] = phase2.restructedlattice
130-
131-
def analyze(): lattice.Element = {
132-
FixpointSolvers.log.verb(s"IDE phase 1")
133-
phase1.analyze()
134-
FixpointSolvers.log.verb(s"IDE phase 2")
135-
phase2.restructure(phase2.analyze()).asInstanceOf[lattice.Element] // FIXME: avoid this asInstanceOf
136-
}
137-
}
122+
class CopyConstantPropagationIDEAnalysis(cfg: InterproceduralProgramCfg)(implicit val declData: DeclarationData)
123+
extends IDESolver[ADeclaration, FlatLattice[Int]](cfg)
124+
with CopyConstantPropagationAnalysisFunctions

src/tip/analysis/FlowSensitiveAnalysis.scala

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,22 @@
11
package tip.analysis
22

33
import tip.cfg._
4-
import tip.lattices.{Lattice, MapLattice}
54
import tip.ast.AstNodeData.DeclarationData
65

76
/**
87
* A flow-sensitive analysis.
8+
* @param stateAfterNode true if the abstract state of a CFG node represents the program point <em>after</em> the node,
9+
* false if represents the program point <em>before</em> the node
10+
* (used when outputting analysis results)
911
*/
10-
abstract class FlowSensitiveAnalysis[N](cfg: FragmentCfg) extends Analysis[Any] {
11-
12-
/**
13-
* The lattice used by the analysis.
14-
*/
15-
val lattice: MapLattice[N, Lattice]
16-
17-
/**
18-
* The domain of the map lattice.
19-
*/
20-
val domain: Set[CfgNode] = cfg.nodes
21-
22-
/**
23-
* @inheritdoc
24-
*/
25-
def analyze(): lattice.Element
26-
}
12+
abstract class FlowSensitiveAnalysis(val stateAfterNode: Boolean) extends Analysis[Any]
2713

2814
/**
2915
* A factory to create a specific flow-sensitive analysis that matches the options.
3016
*/
3117
object FlowSensitiveAnalysis {
3218

33-
def select(kind: Analysis.Value, options: AnalysisOption.Value, cfg: FragmentCfg)(implicit declData: DeclarationData): Option[FlowSensitiveAnalysis[_]] = {
19+
def select(kind: Analysis.Value, options: AnalysisOption.Value, cfg: FragmentCfg)(implicit declData: DeclarationData): Option[FlowSensitiveAnalysis] = {
3420

3521
val typedCfg = options match {
3622
case AnalysisOption.`iwlr` | AnalysisOption.`iwlrp` | AnalysisOption.`csiwlrp` | AnalysisOption.`cfiwlrp` | AnalysisOption.`ide` =>
@@ -116,6 +102,7 @@ object FlowSensitiveAnalysis {
116102
case AnalysisOption.`ide` =>
117103
Some(kind match {
118104
case Analysis.copyconstprop => new CopyConstantPropagationIDEAnalysis(typedCfg.right.get)
105+
case Analysis.uninitvars => new PossiblyUninitializedVarsIDEAnalysis(typedCfg.right.get)
119106
case _ => throw new RuntimeException(s"Unsupported solver option `$options` for the analysis $kind")
120107
})
121108
}
@@ -168,6 +155,6 @@ object FlowSensitiveAnalysis {
168155
* A flow sensitive analysis kind
169156
*/
170157
object Analysis extends Enumeration {
171-
val sign, livevars, available, vbusy, reaching, constprop, interval, copyconstprop = Value
158+
val sign, livevars, available, vbusy, reaching, constprop, interval, copyconstprop, uninitvars = Value
172159
}
173160
}

src/tip/analysis/LiveVarsAnalysis.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
package tip.analysis
22

33
import tip.ast._
4-
import tip.cfg.CfgOps._
54
import tip.lattices._
65
import tip.ast.AstNodeData.DeclarationData
7-
86
import tip.solvers._
97
import tip.cfg._
108

9+
import scala.collection.immutable.Set
10+
1111
/**
1212
* Base class for live variables analysis.
1313
*/
14-
abstract class LiveVarsAnalysis(cfg: IntraproceduralProgramCfg)(implicit declData: DeclarationData) extends FlowSensitiveAnalysis[CfgNode](cfg) {
14+
abstract class LiveVarsAnalysis(cfg: IntraproceduralProgramCfg)(implicit declData: DeclarationData) extends FlowSensitiveAnalysis(false) {
1515

16-
val allVars: Set[ADeclaration] = cfg.nodes.flatMap(_.appearingIds)
16+
val lattice: MapLattice[CfgNode, PowersetLattice[ADeclaration]] = new MapLattice(new PowersetLattice())
1717

18-
val lattice = new MapLattice(cfg.nodes, new PowersetLattice(allVars))
18+
val domain: Set[CfgNode] = cfg.nodes
1919

2020
NoPointers.assertContainsProgram(cfg.prog)
2121
NoRecords.assertContainsProgram(cfg.prog)
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package tip.analysis
2+
3+
import tip.ast._
4+
import tip.cfg._
5+
import tip.lattices._
6+
import tip.solvers._
7+
import tip.ast.AstNodeData.{AstNodeWithDeclaration, DeclarationData}
8+
import tip.ast.AstOps.AstOp
9+
10+
import scala.collection.mutable
11+
12+
/**
13+
* Micro-transfer-functions for possibly-uninitialized variables analysis.
14+
*/
15+
trait PossiblyUninitializedVarsAnalysisFunctions extends IDEAnalysis[ADeclaration, TwoElementLattice] {
16+
17+
NoPointers.assertContainsProgram(cfg.prog)
18+
NoRecords.assertContainsProgram(cfg.prog)
19+
20+
implicit val declData: DeclarationData
21+
22+
val valuelattice = new TwoElementLattice()
23+
24+
val edgelattice: EdgeFunctionLattice[valuelattice.type] = new EdgeFunctionLattice(valuelattice)
25+
26+
import cfg._
27+
import edgelattice._
28+
import edgelattice.valuelattice._
29+
30+
def edgesCallToEntry(d: DL, call: CfgCallNode, entry: CfgFunEntryNode): List[(DL, edgelattice.EdgeFunction)] =
31+
entry.data.params.zip(call.invocation.args).foldLeft(List[(DL, edgelattice.EdgeFunction)]()) {
32+
case (acc, (id, exp)) =>
33+
acc ++ assign(d, id, exp)
34+
}
35+
36+
def edgesExitToAfterCall(d: DL, exit: CfgFunExitNode, aftercall: CfgAfterCallNode): List[(DL, edgelattice.EdgeFunction)] =
37+
assign(d, aftercall.targetIdentifier.declaration, AstOps.returnId)
38+
39+
def edgesCallToAfterCall(d2: DL, call: CfgCallNode, aftercall: CfgAfterCallNode): List[(DL, edgelattice.EdgeFunction)] =
40+
d2 match {
41+
case Right(_) => List((d2, IdEdge()))
42+
case Left(a) => if (a == aftercall.targetIdentifier.declaration) List() else List((d2, IdEdge()))
43+
}
44+
45+
def edgesOther(d: DL, n: CfgNode): List[(DL, edgelattice.EdgeFunction)] =
46+
n match {
47+
case r: CfgStmtNode =>
48+
r.data match {
49+
50+
// var declarations
51+
case varr: AVarStmt =>
52+
d match {
53+
case Right(_) =>
54+
varr.declIds.foldLeft(List((d, IdEdge())): List[(DL, EdgeFunction)]) { (ps, id) => // identity edge from lambda to lambda
55+
ps :+ (Left(id), ConstEdge(Top)) // top edge from lambda to each variable being declared
56+
}
57+
case Left(a) =>
58+
if (varr.declIds.contains(a))
59+
List() // no edges from the variables being declared
60+
else
61+
List((d, IdEdge())) // identity edge from all other variables to themselves
62+
}
63+
64+
// assignments
65+
case as: AAssignStmt =>
66+
as match {
67+
case AAssignStmt(id: AIdentifier, right, _) =>
68+
val edges = assign(d, id.declaration, right)
69+
d match {
70+
case Left(a) if id.declaration != a =>
71+
edges :+ ((d, IdEdge())) // not at the variable being written to, so add identity edge
72+
case _ =>
73+
edges
74+
}
75+
case AAssignStmt(_, _, _) => NoPointers.LanguageRestrictionViolation(s"$as not allowed", as.loc)
76+
}
77+
78+
// return statement
79+
case ret: AReturnStmt => assign(d, AstOps.returnId, ret.exp)
80+
81+
// all other kinds of statements: like no-ops
82+
case _ => List((d, IdEdge()))
83+
}
84+
// all other kinds of nodes: like no-ops
85+
case _ => List((d, IdEdge()))
86+
}
87+
88+
/**
89+
* Micro-transfer-functions for assigning an expression to an identifier.
90+
*/
91+
private def assign(d: DL, id: ADeclaration, exp: AExprOrIdentifierDeclaration): List[(DL, edgelattice.EdgeFunction)] = {
92+
val edges = mutable.ListBuffer[(DL, EdgeFunction)]()
93+
d match {
94+
case Right(_) =>
95+
edges += ((d, IdEdge())) // identity edge from lambda to lambda
96+
case Left(a) =>
97+
// identity edge from d to the variable being assigned to if d appears in exp
98+
if (exp.appearingIds.contains(a))
99+
edges += ((Left(id), IdEdge()))
100+
}
101+
edges.toList
102+
}
103+
}
104+
105+
/**
106+
* Possibly-uninitialized variables analysis using IDE solver.
107+
*/
108+
class PossiblyUninitializedVarsIDEAnalysis(cfg: InterproceduralProgramCfg)(implicit val declData: DeclarationData)
109+
extends IDESolver[ADeclaration, TwoElementLattice](cfg)
110+
with PossiblyUninitializedVarsAnalysisFunctions

0 commit comments

Comments
 (0)