//-----------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation. All Rights Reserved.
//
//-----------------------------------------------------------------------------
namespace Microsoft.Boogie.AbstractInterpretation {
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using Microsoft.Boogie;
using Cci = System.Compiler;
using AI = Microsoft.AbstractInterpretationFramework;
///
/// Defines invariant propagation methods over ASTs for an abstract interpretation policy.
///
public class AbstractionEngine {
private AI.Lattice lattice;
private Queue procWorkItems; //PM: changed to generic queue
private Queue/**/ callReturnWorkItems;
private Cci.IRelation/**/ procedureImplementations;
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(lattice != null);
Contract.Invariant(procWorkItems != null);
Contract.Invariant(callReturnWorkItems != null);
}
private class ProcedureWorkItem {
[Rep] // KRML: this doesn't seem like the right designation to me; but I'm not sure what is
public Procedure Proc;
public int Index; // pre state is Impl.Summary[Index]
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(Proc != null);
Contract.Invariant(0 <= Index && Index < Proc.Summary.Count);
Contract.Invariant(log != null);
}
public ProcedureWorkItem([Captured] Procedure p, AI.Lattice.Element v, AI.Lattice lattice) {
Contract.Requires(p != null);
Contract.Requires(v != null);
Contract.Requires(lattice != null);
Contract.Ensures(p == Proc);
this.Proc = p;
p.Summary.Add(new ProcedureSummaryEntry(lattice, v));
this.Index = p.Summary.Count - 1;
// KRML: axioms are now in place: assume 0 <= Index && Index < Proc.Summary.Count; //PM: Should not be necessary once axioms for pure methods are there
}
}
private readonly static AI.Logger log = new AI.Logger("Engine");
public AbstractionEngine(AI.Lattice lattice) {
Contract.Requires(lattice != null);
Contract.Assume(cce.IsExposable(log)); //TODO: use the extension method syntax //PM: One would need static class invariants to prove this property
cce.BeginExpose(log);
log.Enabled = AI.Lattice.LogSwitch;
cce.EndExpose();
this.lattice = lattice;
this.procWorkItems = new Queue();
this.callReturnWorkItems = new Queue();
}
private Cci.IGraphNavigator ComputeCallGraph(Program program) {
Contract.Requires(program != null);
Contract.Ensures(this.procedureImplementations != null);
// Since implementations call procedures (impl. signatures)
// rather than directly calling other implementations, we first
// need to compute which implementations implement which
// procedures and remember which implementations call which
// procedures.
Cci.IMutableRelation/**/ callsProcedure = new Cci.Relation();
Cci.IMutableRelation/**/ implementsProcedure = new Cci.Relation();
this.procedureImplementations = implementsProcedure;
// ArrayList publics = new ArrayList();
// publicImpls = publics;
foreach (Declaration member in program.TopLevelDeclarations) {
Implementation impl = member as Implementation;
if (impl != null) {
implementsProcedure.Add(impl.Proc, impl);
// if (impl.IsPublic) { publics.Add(impl); } // PR: what does "IsPublic" stand for?
foreach (Block block in impl.Blocks) {
foreach (Cmd cmd in block.Cmds) {
CallCmd call = cmd as CallCmd;
if (call != null) {
callsProcedure.Add(impl, call.Proc);
}
}
}
}
}
// Now compute a graph from implementations to implementations.
Cci.GraphBuilder callGraph = new Cci.GraphBuilder();
IEnumerable callerImpls = callsProcedure.GetKeys();
Contract.Assume(callerImpls != null); // because of non-generic collection
foreach (Implementation caller in callerImpls) {
IEnumerable callerProcs = callsProcedure.GetValues(caller);
Contract.Assume(callerProcs != null); // because of non-generic collection
foreach (Procedure callee in callerProcs) {
IEnumerable calleeImpls = implementsProcedure.GetValues(callee);
Contract.Assume(calleeImpls != null); // because of non-generic collection
foreach (Implementation calleeImpl in calleeImpls) {
callGraph.AddEdge(caller, calleeImpl);
}
}
}
return callGraph;
}
#if OLDCODE
public void ComputeImplementationInvariants (Implementation impl)
{
// process each procedure implementation by recursively processing the first (entry) block...
ComputeBlockInvariants(impl.Blocks[0], lattice.Top);
// compute the procedure invariant by joining all terminal block invariants...
AI.Lattice.Element post = lattice.Bottom;
foreach (Block block in impl.Blocks)
{
if (block.TransferCmd is ReturnCmd)
{
AI.Lattice.Element postValue = block.PostInvariantBuckets[invariantIndex];
Debug.Assert(postValue != null);
post = post.Join(postValue);
}
}
impl.PostInvariant = post;
// Now update the procedure to reflect the new properties
// of this implementation.
if (impl.Proc.PreInvariant <= impl.PreInvariant)
{
// Strengthen the precondition
impl.Proc.PreInvariant = impl.PreInvariant;
foreach (Implementation otherImpl in this.procedureImplementations.GetValues(impl.Proc))
{
if (otherImpl == impl) { continue; }
if (otherImpl.PreInvariant <= impl.Proc.PreInvariant)
{
// If another implementation of the same procedure has
// a weaker precondition, re-do it with the stronger
// precondition.
otherImpl.PreInvariant = impl.Proc.PreInvariant;
this.implWorkItems.Enqueue(otherImpl);
}
}
}
}
#endif
#if NOTYET
public void ComputeSccInvariants (IEnumerable/**/ implementations)
{
Debug.Assert(this.implWorkItems.Count == 0); // no work left over from last SCC
foreach (Implementation impl in implementations)
{
impl.AbstractFunction = AbstractFunction.Empty.WithPair(this.lattice.Bottom, this.lattice.Bottom);
this.implWorkItems.Enqueue(impl);
}
while (this.implWorkItems.Count > 0) // until fixed point reached
{
Implementation impl = (Implementation)this.implWorkItems.Dequeue();
}
}
#endif
public AI.Lattice.Element ApplyProcedureSummary(CallCmd call, Implementation caller, AI.Lattice.Element knownAtCallSite, CallSite callSite) {
Contract.Requires(call.Proc != null);
Contract.Requires(call != null);
Contract.Requires(caller != null);
Contract.Requires(knownAtCallSite != null);
Contract.Requires(callSite != null);
Contract.Ensures(Contract.Result() != null);
Procedure proc = call.Proc;//Precondition required that call.Proc !=null, therefore no assert necessarry.
// NOTE: Here, we count on the fact that an implementation's variables
// are distinct from an implementation's procedure's variables. So, even for
// a recursive implementation, we're free to use the implementation's
// procedure's input parameters as though they were temporary local variables.
//
// Hence, in the program
// procedure Foo (i:int); implementation Foo (i':int) { ...call Foo(i'+1)... }
// we can treat the recursive call as
// i:=i'+1; call Foo(i);
// where the notation i' means a variable with the same (string) name as i,
// but a different identity.
AI.Lattice.Element relevantToCall = knownAtCallSite; //Precondition of the method implies that this can never be null, therefore no need for an assert.
for (int i = 0; i < proc.InParams.Length; i++) {
// "Assign" the actual expressions to the corresponding formal variables.
Contract.Assume(proc.InParams[i] != null); //PM: this can be fixed once VariableSeq is replaced by List;
Contract.Assume(call.Ins[i] != null); //PM: this can be fixed once VariableSeq is replaced by List;
Expr equality = Expr.Eq(Expr.Ident(cce.NonNull(proc.InParams[i])), cce.NonNull(call.Ins[i]));
relevantToCall = lattice.Constrain(relevantToCall, equality.IExpr);
}
foreach (Variable var in caller.LocVars) {
Contract.Assert(var != null);
relevantToCall = this.lattice.Eliminate(relevantToCall, var);
}
ProcedureSummary summary = proc.Summary;
Contract.Assert(summary != null);
ProcedureSummaryEntry applicableEntry = null;
for (int i = 0; i < summary.Count; i++) {
ProcedureSummaryEntry current = cce.NonNull(summary[i]);
if (lattice.Equivalent(current.OnEntry, relevantToCall)) {
applicableEntry = current;
break;
}
}
// Not found in current map, so add new entry.
if (applicableEntry == null) {
ProcedureWorkItem newWorkItem = new ProcedureWorkItem(proc, relevantToCall, lattice);
Contract.Assert(newWorkItem != null);
this.procWorkItems.Enqueue(newWorkItem);
applicableEntry = cce.NonNull(proc.Summary[newWorkItem.Index]);
}
applicableEntry.ReturnPoints.Add(callSite);
AI.Lattice.Element atReturn = applicableEntry.OnExit;
for (int i = 0; i < call.Outs.Count; i++) {
atReturn = this.lattice.Rename(atReturn, cce.NonNull(call.Proc.OutParams[i]), cce.NonNull(cce.NonNull(call.Outs[i]).Decl));
knownAtCallSite = this.lattice.Eliminate(knownAtCallSite, cce.NonNull(cce.NonNull(call.Outs[i]).Decl));
}
return this.lattice.Meet(atReturn, knownAtCallSite);
}
private Cci.IGraphNavigator callGraph;
public Cci.IGraphNavigator CallGraph {
get {
return this.callGraph;
}
}
///
/// Compute the invariants for the program using the underlying abstract domain
///
public void ComputeProgramInvariants(Program program) {
Contract.Requires(program != null);
#if NOT_YET
Cci.IGraphNavigator callGraph =
#endif
callGraph = this.ComputeCallGraph(program);
Contract.Assert(this.procedureImplementations != null);
Cci.IRelation procedureImplementations = this.procedureImplementations;//Non-nullability was already asserted in
//the line above, ergo there is no need for
//an assert after this statement to maintain
//the non-null type.
#if NOT_YET
IEnumerable/**/ sccs =
StronglyConnectedComponent.ConstructSCCs(publicImpls, callGraph);
IList/**/ sortedSccs =
GraphUtil.TopologicallySortComponentGraph(sccs);
// Go bottom-up through the SCCs of the call graph
foreach (IStronglyConnectedComponent scc in sortedSccs)
{
this.ComputeSccInvariants(scc.Nodes);
}
#endif
AI.Lattice.Element initialElement = this.lattice.Top;
Contract.Assert(initialElement != null);
// Gather all the axioms to create the initial lattice element
// Differently stated, it is the \alpha from axioms (i.e. first order formulae) to the underlyng abstract domain
foreach (Declaration decl in program.TopLevelDeclarations) {
Axiom ax = decl as Axiom;
if (ax != null) {
initialElement = this.lattice.Constrain(initialElement, ax.Expr.IExpr);
}
}
// propagate over all procedures...
foreach (Declaration decl in program.TopLevelDeclarations) {
Procedure proc = decl as Procedure;
if (proc != null) {
this.procWorkItems.Enqueue(new ProcedureWorkItem(proc, initialElement, this.lattice));
}
}
// analyze all the procedures...
while (this.procWorkItems.Count + this.callReturnWorkItems.Count > 0) {
while (this.procWorkItems.Count > 0) {
ProcedureWorkItem workItem = this.procWorkItems.Dequeue();
ProcedureSummaryEntry summaryEntry = cce.NonNull(workItem.Proc.Summary[workItem.Index]);
if (cce.NonNull(procedureImplementations.GetValues(workItem.Proc)).Count == 0) {
// This procedure has no given implementations. We therefore treat the procedure
// according to its specification only.
if (!CommandLineOptions.Clo.IntraproceduralInfer) {
AI.Lattice.Element post = summaryEntry.OnEntry;
// BUGBUG. Here, we should process "post" according to the requires, modifies, ensures
// specification of the procedure, including any OLD expressions in the postcondition.
AI.Lattice.Element atReturn = post;
if (!this.lattice.LowerThan(atReturn, summaryEntry.OnExit)) {
// If the results of this analysis are strictly worse than
// what we previous knew for the same input assumptions,
// update the summary and re-do the call sites.
summaryEntry.OnExit = this.lattice.Join(summaryEntry.OnExit, atReturn);
foreach (CallSite callSite in summaryEntry.ReturnPoints) {
this.callReturnWorkItems.Enqueue(callSite);
}
}
}
} else {
// There are implementations, so do inference based on those implementations
if (!CommandLineOptions.Clo.IntraproceduralInfer) {
summaryEntry.OnExit = lattice.Bottom;
}
// For each implementation in the procedure...
foreach (Implementation impl in cce.NonNull(procedureImplementations.GetValues(workItem.Proc))) {
// process each procedure implementation by recursively processing the first (entry) block...
cce.NonNull(impl.Blocks[0]).Lattice = lattice;
ComputeBlockInvariants(impl, cce.NonNull(impl.Blocks[0]), summaryEntry.OnEntry, summaryEntry);
AdjustProcedureSummary(impl, summaryEntry);
}
}
}
while (this.callReturnWorkItems.Count > 0) {
CallSite callSite = cce.NonNull((CallSite)this.callReturnWorkItems.Dequeue());
PropagateStartingAtStatement(callSite.Impl, callSite.Block, callSite.Statement, callSite.KnownBeforeCall, callSite.SummaryEntry);
AdjustProcedureSummary(callSite.Impl, callSite.SummaryEntry);
}
} // both queues
}
void AdjustProcedureSummary(Implementation impl, ProcedureSummaryEntry summaryEntry) {
Contract.Requires(impl != null);
Contract.Requires(summaryEntry != null);
if (CommandLineOptions.Clo.IntraproceduralInfer) {
return; // no summary to adjust
}
// compute the procedure invariant by joining all terminal block invariants...
AI.Lattice.Element post = lattice.Bottom;
foreach (Block block in impl.Blocks) {
if (block.TransferCmd is ReturnCmd) {
// note: if program control cannot reach this block, then postValue will be null
if (block.PostInvariant != null) {
post = (AI.Lattice.Element)lattice.Join(post, block.PostInvariant);
}
}
}
AI.Lattice.Element atReturn = post;
foreach (Variable var in impl.LocVars) {
Contract.Assert(var != null);
atReturn = this.lattice.Eliminate(atReturn, var);
}
foreach (Variable var in impl.InParams) {
Contract.Assert(var != null);
atReturn = this.lattice.Eliminate(atReturn, var);
}
if (!this.lattice.LowerThan(atReturn, summaryEntry.OnExit)) {
// If the results of this analysis are strictly worse than
// what we previous knew for the same input assumptions,
// update the summary and re-do the call sites.
summaryEntry.OnExit = this.lattice.Join(summaryEntry.OnExit, atReturn);
foreach (CallSite callSite in summaryEntry.ReturnPoints) {
Contract.Assert(callSite != null);
this.callReturnWorkItems.Enqueue(callSite);
}
}
}
private static int freshVarId = 0;
private static Variable FreshVar(Boogie.Type ty) {
Contract.Requires(ty != null);
Contract.Ensures(Contract.Result() != null);
Variable fresh = new LocalVariable(Token.NoToken, new TypedIdent(Token.NoToken, "fresh" + freshVarId, ty));
freshVarId++;
return fresh;
}
private delegate CallSite/*!*/ MarkCallSite(AI.Lattice.Element/*!*/ currentValue);//TODO: Think this is an invariant, but not sure how2 deal with it
///
/// Given a basic block, it propagates the abstract state at the entry point through the exit point of the block
/// The implementation that owns the block
/// The from where we propagate
///
/// The initial value
///
private void PropagateStartingAtStatement(Implementation/*!*/ impl, Block/*!*/ block, int statementIndex, AI.Lattice.Element/*!*/ currentValue,
ProcedureSummaryEntry/*!*/ summaryEntry) {
Contract.Requires(impl != null);
Contract.Requires(block != null);
Contract.Requires(currentValue != null);
Contract.Requires(summaryEntry != null);
Contract.Assume(cce.IsPeerConsistent(log));
log.DbgMsg(string.Format("{0}:", block.Label));
log.DbgMsgIndent();
#region Apply the abstract transition relation to the statements in the block
for (int cmdIndex = statementIndex; cmdIndex < block.Cmds.Length; cmdIndex++) {
Cmd cmd = cce.NonNull(block.Cmds[cmdIndex]); // Fetch the command
currentValue = Step(cmd, currentValue, impl, // Apply the transition function
delegate(AI.Lattice.Element cv) {
Contract.Requires(cv != null);
return new CallSite(impl, block, cmdIndex, cv, summaryEntry);
}
);
}
block.PostInvariant = currentValue; // The invariant at the exit point of the block is that of the last statement
log.DbgMsg(string.Format("pre {0}", cce.NonNull(block.PreInvariant).ToString()));
log.DbgMsg(string.Format("post {0}", (block.PostInvariant).ToString()));
log.DbgMsgUnindent();
#endregion
#region Propagate the post-condition to the successor nodes
GotoCmd @goto = block.TransferCmd as GotoCmd;
if (@goto != null) {
// labelTargets is non-null after calling Resolve in a prior phase.
Contract.Assume(@goto.labelTargets != null);
// For all the successors of this block, propagate the abstract state
foreach (Block succ in @goto.labelTargets) {
Contract.Assert(succ != null);
if (impl.Blocks.Contains(succ)) {
succ.Lattice = block.Lattice; // The lattice is the same
// Propagate the post-abstract state of this block to the successor
ComputeBlockInvariants(impl, succ, block.PostInvariant, summaryEntry);
}
}
}
#endregion
}
///
/// The abstract transition relation.
///
private AI.Lattice.Element Step(Cmd cmd, AI.Lattice.Element pre, Implementation impl, MarkCallSite/*?*/ callSiteMarker) {
Contract.Requires(cmd != null);
Contract.Requires(pre != null);
Contract.Requires(impl != null);
Contract.Ensures(Contract.Result() != null);
Contract.Assume(cce.IsPeerConsistent(log));
log.DbgMsg(string.Format("{0}", cmd));
log.DbgMsgIndent();
AI.Lattice.Element currentValue = pre;//Nonnullability was a precondition
// Case split...
#region AssignCmd
if (cmd is AssignCmd) { // parallel assignment
// we first eliminate map assignments
AssignCmd assmt = cce.NonNull((AssignCmd)cmd).AsSimpleAssignCmd;
//PM: Assume variables have been resolved
Contract.Assume(Contract.ForAll(assmt.Lhss, lhs => lhs.DeepAssignedVariable != null));//TODO: Check my work, please, Mike.
List!*/> freshLhs = new List();
foreach (AssignLhs lhs in assmt.Lhss) {
Contract.Assert(lhs != null);
freshLhs.Add(Expr.Ident(FreshVar(cce.NonNull(lhs.DeepAssignedVariable)
.TypedIdent.Type)));
}
for (int i = 0; i < freshLhs.Count; ++i)
currentValue =
this.lattice.Constrain(currentValue,
Expr.Eq(freshLhs[i], assmt.Rhss[i]).IExpr);
foreach (AssignLhs lhs in assmt.Lhss) {
Contract.Assert(lhs != null);
currentValue =
this.lattice.Eliminate(currentValue, cce.NonNull(lhs.DeepAssignedVariable));
}
for (int i = 0; i < freshLhs.Count; ++i)
currentValue =
this.lattice.Rename(currentValue, cce.NonNull(freshLhs[i].Decl),
cce.NonNull(assmt.Lhss[i].DeepAssignedVariable));
}
/*
if (cmd is SimpleAssignCmd)
{
SimpleAssignCmd! assmt = (SimpleAssignCmd)cmd;
assume assmt.Lhs.Decl != null; //PM: Assume variables have been resolved
Variable! dest = assmt.Lhs.Decl;
Variable! fresh = FreshVar(dest.TypedIdent.Type);
IdentifierExpr newLhs = Expr.Ident(fresh);
Expr equality = Expr.Eq(newLhs, assmt.Rhs);
currentValue = this.lattice.Constrain(currentValue, equality.IExpr);
currentValue = this.lattice.Eliminate(currentValue, dest);
currentValue = this.lattice.Rename(currentValue, fresh, dest);
}
#endregion
#region ArrayAssignCmd
else if (cmd is ArrayAssignCmd)
{
ArrayAssignCmd assmt = (ArrayAssignCmd)cmd;
assume assmt.Array.Type != null; //PM: assume that type checker has run
ArrayType! arrayType = (ArrayType)assmt.Array.Type;
Variable newHeapVar = FreshVar(arrayType);
IdentifierExpr newHeap = Expr.Ident(newHeapVar);
IdentifierExpr oldHeap = assmt.Array;
assume oldHeap.Decl != null; //PM: assume that variable has been resolved
// For now, we only know how to handle heaps
if (arrayType.IndexType0.IsRef && arrayType.IndexType1 != null && arrayType.IndexType1.IsName)
{
//PM: The following assertion follows from a nontrivial invariant of ArrayAssignCmd,
//PM: which we do not have yet. Therefore, we put in an assume fo now.
assume assmt.Index1 != null;
assert assmt.Index1 != null;
// heap succession predicate
Expr heapsucc = Expr.HeapSucc(oldHeap, newHeap, assmt.Index0, assmt.Index1);
currentValue = this.lattice.Constrain(currentValue, heapsucc.IExpr);
}
else
{
// TODO: We can do this case as well if the heap succession array can handle non-heap arrays
}
// new select expression
IndexedExpr newLhs = new IndexedExpr(Token.NoToken, newHeap, assmt.Index0, assmt.Index1);
Expr equality = Expr.Eq(newLhs, assmt.Rhs);
currentValue = this.lattice.Constrain(currentValue, equality.IExpr);
currentValue = this.lattice.Eliminate(currentValue, oldHeap.Decl);
currentValue = this.lattice.Rename(currentValue, newHeapVar, oldHeap.Decl);
} */
#endregion
#region Havoc
else if (cmd is HavocCmd) {
HavocCmd havoc = (HavocCmd)cmd;
foreach (IdentifierExpr id in havoc.Vars) {
Contract.Assert(id != null);
currentValue = this.lattice.Eliminate(currentValue, cce.NonNull(id.Decl));
}
}
#endregion
#region PredicateCmd
else if (cmd is PredicateCmd) {
//System.Console.WriteLine("Abstract State BEFORE " + ((PredicateCmd) cmd).Expr + " : " +this.lattice.ToPredicate(currentValue));
Expr embeddedExpr = cce.NonNull((PredicateCmd)cmd).Expr;
List!*/> conjuncts = flatConjunction(embeddedExpr); // Handle "assume P && Q" as if it was "assume P; assume Q"
Contract.Assert(conjuncts != null);
foreach (Expr c in conjuncts) {
Contract.Assert(c != null);
currentValue = this.lattice.Constrain(currentValue, c.IExpr);
}
//System.Console.WriteLine("Abstract State AFTER assert/assume "+ this.lattice.ToPredicate(currentValue));
}
#endregion
#region CallCmd
else if (cmd is CallCmd) {
CallCmd call = (CallCmd)cmd;
if (!CommandLineOptions.Clo.IntraproceduralInfer) {
// Interprocedural analysis
if (callSiteMarker == null) {
throw new System.InvalidOperationException("INTERNAL ERROR: Context does not allow CallCmd.");
}
CallSite here = callSiteMarker(currentValue);
currentValue = ApplyProcedureSummary(call, impl, currentValue, here);
} else {
// Intraprocedural analysis
StateCmd statecmd = call.Desugaring as StateCmd;
if (statecmd != null) {
// Iterate the abstract transition on all the commands in the desugaring of the call
foreach (Cmd callDesug in statecmd.Cmds) {
Contract.Assert(callDesug != null);
currentValue = Step(callDesug, currentValue, impl, null);
}
// Now, project out the local variables
foreach (Variable local in statecmd.Locals) {
Contract.Assert(local != null);
currentValue = this.lattice.Eliminate(currentValue, local);
}
} else
throw new System.InvalidOperationException("INTERNAL ERROR: CallCmd does not desugar to StateCmd.");
}
}
#endregion
#region CommentCmd
else if (cmd is CommentCmd) {
// skip
}
#endregion
else if (cmd is SugaredCmd) {
// other sugared commands are treated like their desugaring
SugaredCmd sugar = (SugaredCmd)cmd;
Cmd desugaring = sugar.Desugaring;
if (desugaring is StateCmd) {
StateCmd statecmd = (StateCmd)desugaring;
// Iterate the abstract transition on all the commands in the desugaring of the call
foreach (Cmd callDesug in statecmd.Cmds) {
Contract.Assert(callDesug != null);
currentValue = Step(callDesug, currentValue, impl, null);
}
// Now, project out the local variables
foreach (Variable local in statecmd.Locals) {
Contract.Assert(local != null);
currentValue = this.lattice.Eliminate(currentValue, local);
}
} else {
currentValue = Step(desugaring, currentValue, impl, null);
}
} else {
Contract.Assert(false); // unknown command
throw new cce.UnreachableException();
}
log.DbgMsgUnindent();
return currentValue;
}
///
/// Flat an expresion in the form P AND Q ... AND R into a list [P, Q, ..., R]
///
private List!*/> flatConjunction(Expr embeddedExpr) {
Contract.Requires(embeddedExpr != null);
Contract.Ensures(cce.NonNullElements(Contract.Result>()));
List!*/> retValue = new List();//No asserts necessarry. It is the return value, and thus will be subject to the postcondition
NAryExpr e = embeddedExpr as NAryExpr;
if (e != null && e.Fun.FunctionName.CompareTo("&&") == 0) { // if it is a conjunction
foreach (Expr arg in e.Args) {
Contract.Assert(arg != null);
List/*!*/ newConjuncts = flatConjunction(arg);
Contract.Assert(cce.NonNullElements(newConjuncts));
retValue.AddRange(newConjuncts);
}
} else {
retValue.Add(embeddedExpr);
}
return retValue;
}
///
/// Compute the invariants for a basic block
/// The implementation the block belongs to
/// The block for which we compute the invariants
/// The "init" abstract state for the block
///
private void ComputeBlockInvariants(Implementation impl, Block block, AI.Lattice.Element incomingValue, ProcedureSummaryEntry summaryEntry) {
Contract.Requires(impl != null);
Contract.Requires(block != null);
Contract.Requires(incomingValue != null);
Contract.Requires(summaryEntry != null);
if (block.PreInvariant == null) // block has not yet been processed
{
Contract.Assert(block.PostInvariant == null);
// To a first approximation the block is unreachable
block.PreInvariant = this.lattice.Bottom;
block.PostInvariant = this.lattice.Bottom;
}
Contract.Assert(block.PreInvariant != null);
Contract.Assert(block.PostInvariant != null);
#region Check if we have reached a postfixpoint
if (lattice.LowerThan(incomingValue, block.PreInvariant)) {
// We have reached a post-fixpoint, so we are done...
#if DEBUG_PRINT
System.Console.WriteLine("@@ Compared for block {0}:", block.Label);
System.Console.WriteLine("@@ {0}", lattice.ToPredicate(incomingValue));
System.Console.WriteLine("@@ {0}", lattice.ToPredicate(block.PreInvariant));
System.Console.WriteLine("@@ result = True");
System.Console.WriteLine("@@ end Compare");
#endif
return;
}
#if DEBUG_PRINT
// Compute the free variables in incoming and block.PreInvariant
FreeVariablesVisitor freeVarsVisitorForA = new FreeVariablesVisitor();
FreeVariablesVisitor freeVarsVisitorForB = new FreeVariablesVisitor();
lattice.ToPredicate(incomingValue).DoVisit(freeVarsVisitorForA);
lattice.ToPredicate(block.PreInvariant).DoVisit(freeVarsVisitorForB);
List! freeVarsOfA = freeVarsVisitorForA.FreeVariables;
List! freeVarsOfB = freeVarsVisitorForB.FreeVariables;
System.Console.WriteLine("@@ Compared for block {0}:", block.Label);
System.Console.WriteLine("@@ Incoming: {0}", lattice.ToPredicate((!) incomingValue));
System.Console.WriteLine("@@ Free Variables : {0}", ToString(freeVarsOfA));
System.Console.WriteLine("@@ Previous: {0}", lattice.ToPredicate(block.PreInvariant));
System.Console.WriteLine("@@ Free Variables : {0}", ToString(freeVarsOfB));
System.Console.WriteLine("@@ result = False");
System.Console.WriteLine("@@ end Compare");
string operation = "";
#endif
#endregion
#region If it is not the case, then join or widen the incoming abstract state with the previous one
if (block.widenBlock) // If the considered block is the entry point of a loop
{
if (block.iterations <= CommandLineOptions.Clo.StepsBeforeWidening + 1) {
#if DEBUG_PRINT
operation = "join";
#endif
block.PreInvariant = (AI.Lattice.Element)lattice.Join(block.PreInvariant, incomingValue);
} else {
#if DEBUG_PRINT
operation = "widening";
#endif
// The default is to have have a widening that perform a (approximation of) the closure of the operands, so to improve the precision
// block.PreInvariant = WideningWithClosure.MorePreciseWiden(lattice, (!) block.PreInvariant, incomingValue);
block.PreInvariant = (AI.Lattice.Element)lattice.Widen(block.PreInvariant, incomingValue);
}
block.iterations++;
} else {
#if DEBUG_PRINT
operation = "join";
#endif
block.PreInvariant = (AI.Lattice.Element)lattice.Join(block.PreInvariant, incomingValue);
}
#if DEBUG_PRINT
System.Console.WriteLine("@@ {0} for block {1}:", operation, block.Label);
System.Console.WriteLine("@@ {0}", lattice.ToPredicate(block.PreInvariant));
System.Console.WriteLine("@@ end");
#endif
#endregion
#region Propagate the entry abstract state through the method
PropagateStartingAtStatement(impl, block, 0, cce.NonNull(block.PreInvariant.Clone()), summaryEntry);
#endregion
}
#if DEBUG_PRINT
private string! ToString(List! vars)
{
string s = "";
foreach(AI.IVariable! v in vars)
{
s += v.Name +" ";
}
return s;
}
#endif
} // class
///
/// Defines a class for building the abstract domain according to the parameters switch
///
public class AbstractDomainBuilder {
private AbstractDomainBuilder() { /* do nothing */
}
///
/// Return a fresh instance of the abstract domain of intervals
///
static public AbstractAlgebra BuildIntervalsAbstractDomain() {
Contract.Ensures(Contract.Result() != null);
AI.IQuantPropExprFactory propfactory = new BoogiePropFactory();
AI.ILinearExprFactory linearfactory = new BoogieLinearFactory();
AI.IValueExprFactory valuefactory = new BoogieValueFactory();
IComparer variableComparer = new VariableComparer();
AbstractAlgebra retAlgebra;
AI.Lattice intervals = new AI.VariableMapLattice(propfactory, valuefactory, new AI.IntervalLattice(linearfactory), variableComparer);
Contract.Assert(intervals != null);
retAlgebra = new AbstractAlgebra(intervals, propfactory, linearfactory, null, valuefactory, null, variableComparer);
return retAlgebra;
}
///
/// Return a fresh abstract domain, according to the parameters specified by the command line
///
static public AbstractAlgebra BuildAbstractDomain() {
Contract.Ensures(Contract.Result() != null);
AbstractAlgebra retAlgebra;
AI.Lattice returnLattice;
AI.IQuantPropExprFactory propfactory = new BoogiePropFactory();
AI.ILinearExprFactory linearfactory = new BoogieLinearFactory();
AI.IIntExprFactory intfactory = new BoogieIntFactory();
AI.IValueExprFactory valuefactory = new BoogieValueFactory();
AI.INullnessFactory nullfactory = new BoogieNullnessFactory();
IComparer variableComparer = new VariableComparer();
AI.MultiLattice multilattice = new AI.MultiLattice(propfactory, valuefactory);
if (CommandLineOptions.Clo.Ai.Intervals) // Intervals
{
multilattice.AddLattice(new AI.VariableMapLattice(propfactory, valuefactory,
new AI.IntervalLattice(linearfactory),
variableComparer));
}
if (CommandLineOptions.Clo.Ai.Constant) // Constant propagation
{
multilattice.AddLattice(new AI.VariableMapLattice(propfactory, valuefactory,
new AI.ConstantLattice(intfactory),
variableComparer));
}
if (CommandLineOptions.Clo.Ai.DynamicType) // Class types
{
BoogieTypeFactory typeFactory = new BoogieTypeFactory();
multilattice.AddLattice(new AI.VariableMapLattice(propfactory, valuefactory,
new AI.DynamicTypeLattice(typeFactory, propfactory),
variableComparer));
}
if (CommandLineOptions.Clo.Ai.Nullness) // Nullness
{
multilattice.AddLattice(new AI.VariableMapLattice(propfactory, valuefactory,
new AI.NullnessLattice(nullfactory),
variableComparer));
}
if (CommandLineOptions.Clo.Ai.Polyhedra) // Polyhedra
{
multilattice.AddLattice(new AI.PolyhedraLattice(linearfactory, propfactory));
}
returnLattice = multilattice;
if (CommandLineOptions.Clo.Ai.DebugStatistics) {
returnLattice = new AI.StatisticsLattice(returnLattice);
}
returnLattice.Validate();
retAlgebra = new AbstractAlgebra(returnLattice, propfactory, linearfactory, intfactory, valuefactory, nullfactory,
variableComparer);
return retAlgebra;
}
}
///
/// An Abstract Algebra is a tuple made of a Lattice and several factories
///
public class AbstractAlgebra {
[Peer]
private AI.Lattice lattice;
[Peer]
private AI.IQuantPropExprFactory propFactory;
[Peer]
private AI.ILinearExprFactory linearFactory;
[Peer]
private AI.IIntExprFactory intFactory;
[Peer]
private AI.IValueExprFactory valueFactory;
[Peer]
private AI.INullnessFactory nullFactory;
[Peer]
private IComparer variableComparer;
[ContractInvariantMethod]
void ObjectInvariant() {
Contract.Invariant(lattice != null);
}
public AI.Lattice Lattice {
get {
Contract.Ensures(Contract.Result() != null);
return lattice;
}
}
public AI.IQuantPropExprFactory PropositionFactory {
get {
return this.propFactory;
}
}
public AI.ILinearExprFactory LinearExprFactory {
get {
return this.linearFactory;
}
}
public AI.IIntExprFactory IntExprFactory {
get {
return this.intFactory;
}
}
public AI.IValueExprFactory ValueFactory {
get {
return this.valueFactory;
}
}
public AI.INullnessFactory NullFactory {
get {
return this.nullFactory;
}
}
public IComparer VariableComparer {
get {
return this.variableComparer;
}
}
[Captured]
public AbstractAlgebra(AI.Lattice lattice,
AI.IQuantPropExprFactory propFactory,
AI.ILinearExprFactory linearFactory,
AI.IIntExprFactory intFactory,
AI.IValueExprFactory valueFactory,
AI.INullnessFactory nullFactory,
IComparer variableComparer) {
Contract.Requires(propFactory == null || cce.Owner.Same(lattice, propFactory));//TODO: Owner is Microsoft.Contracts (mscorlib.Contracts).Owner
Contract.Requires(linearFactory == null || cce.Owner.Same(lattice, linearFactory));
Contract.Requires(intFactory == null || cce.Owner.Same(lattice, intFactory));
Contract.Requires(valueFactory == null || cce.Owner.Same(lattice, valueFactory));
Contract.Requires(nullFactory == null || cce.Owner.Same(lattice, nullFactory));
Contract.Requires(variableComparer == null || cce.Owner.Same(lattice, variableComparer));
// ensures Owner.Same(this, lattice); // KRML:
Contract.Requires(lattice != null);
this.lattice = lattice;
this.propFactory = propFactory;
this.linearFactory = linearFactory;
this.intFactory = intFactory;
this.valueFactory = valueFactory;
this.nullFactory = nullFactory;
this.variableComparer = variableComparer;
}
}
public class AbstractInterpretation {
///
/// Run the abstract interpretation.
/// It has two entry points. One is the RunBoogie method. The other is the CCI PlugIn
///
public static void RunAbstractInterpretation(Program program) {
Contract.Requires(program != null);
Helpers.ExtraTraceInformation("Starting abstract interpretation");
if (CommandLineOptions.Clo.UseAbstractInterpretation) {
DateTime start = new DateTime(); // to please compiler's definite assignment rules
if (CommandLineOptions.Clo.Trace) {
Console.WriteLine();
Console.WriteLine("Running abstract interpretation...");
start = DateTime.Now;
}
WidenPoints.Compute(program);
if (CommandLineOptions.Clo.Ai.AnySet) // if there is some user defined domain we override the default (= intervals)
{
AI.Lattice lattice = AbstractDomainBuilder.BuildAbstractDomain().Lattice;
ApplyAbstractInterpretation(program, lattice);
if (CommandLineOptions.Clo.Ai.DebugStatistics) {
Console.Error.WriteLine(lattice);
}
} else // Otherwise the default is the use of the abstract domain of intervals (useful for simple for loops)
{
AI.Lattice lattice = AbstractDomainBuilder.BuildIntervalsAbstractDomain().Lattice;
Contract.Assert(lattice != null);
ApplyAbstractInterpretation(program, lattice);
}
program.InstrumentWithInvariants();
if (CommandLineOptions.Clo.Trace) {
DateTime end = DateTime.Now;
TimeSpan elapsed = end - start;
Console.WriteLine(" [{0} s]", elapsed.TotalSeconds);
Console.Out.Flush();
}
}
}
static void ApplyAbstractInterpretation(Program program, AI.Lattice lattice) {
Contract.Requires(program != null);
Contract.Requires(lattice != null);
AbstractionEngine engine = new AbstractionEngine(lattice);
engine.ComputeProgramInvariants(program);
callGraph = engine.CallGraph;
}
private static Cci.IGraphNavigator callGraph;
public static Cci.IGraphNavigator CallGraph {
get {
return callGraph;
}
}
}
} // namespace