//----------------------------------------------------------------------------- // // Copyright (C) Microsoft Corporation. All Rights Reserved. // //----------------------------------------------------------------------------- namespace Microsoft.AbstractInterpretationFramework { using Microsoft.Contracts; using System.Collections; using G = System.Collections.Generic; using System.Diagnostics; using Microsoft.AbstractInterpretationFramework.Collections; using Microsoft.Boogie; using IMutableSet = Microsoft.Boogie.Set; using ISet = Microsoft.Boogie.Set; using HashSet = Microsoft.Boogie.Set; using ArraySet = Microsoft.Boogie.Set; /// /// Specifies the operations (e.g., join) on a mathematical lattice that depend /// only on the elements of the lattice. /// public abstract class MathematicalLattice { /// /// An element of the lattice. This class should be derived from in any /// implementation of MathematicalLattice. /// public abstract class Element : System.ICloneable { /// /// Print out a debug-useful representation of the internal data structure of the lattice element. /// public virtual void Dump(string! msg) { System.Console.WriteLine("Dump({0}) = {1}", msg, this); } public abstract Element! Clone(); object! System.ICloneable.Clone() { return this.Clone(); } public abstract G.ICollection! FreeVariables() ensures result.IsReadOnly; } public abstract Element! Top { get; } public abstract Element! Bottom { get; } public abstract bool IsTop(Element! e); public abstract bool IsBottom(Element! e); /// /// Returns true if a <= this. /// protected abstract bool AtMost(Element! a, Element! b) /* The following cases are handled elsewhere and need not be considered in subclass. */ // requires a.GetType() == b.GetType(); // requires ! a.IsTop; // requires ! a.IsBottom; // requires ! b.IsTop; // requires ! b.IsBottom; ; protected Answer TrivialLowerThan(Element! a, Element! b) { if (a.GetType() != b.GetType()) { throw new System.InvalidOperationException( "operands to <= must be of same Element type" ); } if (IsBottom(a)) { return Answer.Yes; } if (IsTop(b)) { return Answer.Yes; } if (IsTop(a)) { return Answer.No; } if (IsBottom(b)) { return Answer.No; } return Answer.Maybe; } // Is 'a' better information than 'b'? // public bool LowerThan(Element! a, Element! b) { Answer ans = TrivialLowerThan(a,b); return ans != Answer.Maybe ? ans == Answer.Yes : AtMost(a, b); } // Is 'a' worse information than 'b'? // public bool HigherThan(Element! a, Element! b) { return LowerThan(b, a); } // Are 'a' and 'b' equivalent? // public bool Equivalent(Element! a, Element! b) { return LowerThan(a, b) && LowerThan(b, a); } public abstract Element! NontrivialJoin(Element! a, Element! b) /* The following cases are handled elsewhere and need not be considered in subclass. */ // requires a.GetType() == b.GetType(); // requires ! a.IsTop; // requires ! a.IsBottom; // requires ! b.IsTop; // requires ! b.IsBottom; ; protected Element/*?*/ TrivialJoin(Element! a, Element! b) { if (a.GetType() != b.GetType()) { throw new System.InvalidOperationException( "operands to Join must be of same Lattice.Element type" ); } if (IsTop(a)) { return a; } if (IsTop(b)) { return b; } if (IsBottom(a)) { return b; } if (IsBottom(b)) { return a; } return null; } public Element! Join(Element! a, Element! b) { Element/*?*/ r = TrivialJoin(a,b); return r != null ? r : NontrivialJoin(a, b); } public abstract Element! NontrivialMeet(Element! a, Element! b) /* The following cases are handled elsewhere and need not be considered in subclass. */ // requires a.GetType() == b.GetType(); // requires ! a.IsTop; // requires ! a.IsBottom; // requires ! b.IsTop; // requires ! b.IsBottom; ; protected Element/*?*/ TrivialMeet(Element! a, Element! b) { if (a.GetType() != b.GetType()) { throw new System.InvalidOperationException( "operands to Meet must be of same Lattice.Element type" ); } if (IsTop(a)) { return b; } if (IsTop(b)) { return a; } if (IsBottom(a)) { return a; } if (IsBottom(b)) { return b; } return null; } public Element! Meet(Element! a, Element! b) { Element/*?*/ r = TrivialMeet(a,b); return r != null ? r : NontrivialMeet(a, b); } public abstract Element! Widen(Element! a, Element! b); public virtual void Validate() { Debug.Assert(IsTop(Top)); Debug.Assert(IsBottom(Bottom)); Debug.Assert(!IsBottom(Top)); Debug.Assert(!IsTop(Bottom)); Debug.Assert(LowerThan(Top, Top)); Debug.Assert(LowerThan(Bottom, Top)); Debug.Assert(LowerThan(Bottom, Bottom)); Debug.Assert(IsTop(Join(Top, Top))); Debug.Assert(IsBottom(Join(Bottom, Bottom))); } } /// /// Provides an abstract interface for the operations of a lattice specific /// to abstract interpretation (i.e., that deals with the expression language). /// public abstract class Lattice : MathematicalLattice { internal readonly IValueExprFactory! valueExprFactory; public Lattice(IValueExprFactory! valueExprFactory) { this.valueExprFactory = valueExprFactory; // base(); } #region Primitives that commands translate into public abstract Element! Eliminate(Element! e, IVariable! variable); public abstract Element! Rename(Element! e, IVariable! oldName, IVariable! newName); public abstract Element! Constrain(Element! e, IExpr! expr); #endregion // TODO keep this? // public Element! Eliminate(Element! e, VariableSeq! variables) // { // Lattice.Element result = e; // foreach (IVariable var in variables) // { // result = this.Eliminate(result, var); // } // return result; // } //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Note! // // Concrete classes that implement Lattice must implement one of the AtMost // overloads. We provide here a default implementation for one given a "real" // implementation of the other. Otherwise, there will be an infinite loop! //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! protected override bool AtMost(Element! a, Element! b) { return AtMost(a, IdentityCombineNameMap.Map, b, IdentityCombineNameMap.Map); } protected virtual bool AtMost(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult) { return AtMost(ApplyCombineNameMap(a,aToResult), ApplyCombineNameMap(b,bToResult)); } public bool LowerThan(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult) { Answer ans = TrivialLowerThan(a,b); return ans != Answer.Maybe ? ans == Answer.Yes : AtMost(a, aToResult, b, bToResult); } public bool HigherThan(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult) { return LowerThan(b, bToResult, a, aToResult); } //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Note! // // Concrete classes that implement Lattice must implement one of the NontrivialJoin // overloads. We provide here a default implementation for one given a "real" // implementation of the other. Otherwise, there will be an infinite loop! //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! public override Element! NontrivialJoin(Element! a, Element! b) { return NontrivialJoin(a, IdentityCombineNameMap.Map, b, IdentityCombineNameMap.Map); } public virtual Element! NontrivialJoin(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult) { return NontrivialJoin(ApplyCombineNameMap(a,aToResult), ApplyCombineNameMap(b,bToResult)); } public Element! Join(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult) { Element/*?*/ r = TrivialJoin(a,b); return r != null ? r : NontrivialJoin(a, aToResult, b, bToResult); } //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Note! // // Concrete classes that implement Lattice must implement one of the Widen // overloads. We provide here a default implementation for one given a "real" // implementation of the other. Otherwise, there will be an infinite loop! //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! public override Element! Widen(Element! a, Element! b) { return Widen(a, IdentityCombineNameMap.Map, b, IdentityCombineNameMap.Map); } public virtual Element! Widen(Element! a, ICombineNameMap! aToResult, Element! b, ICombineNameMap! bToResult) { return Widen(ApplyCombineNameMap(a,aToResult), ApplyCombineNameMap(b,bToResult)); } /// /// Returns the predicate that corresponds to the given lattice element. /// public abstract IExpr! ToPredicate(Element! e); /// /// Allows the lattice to specify whether it understands a particular function symbol. /// /// The lattice is always allowed to return "true" even when it really can't do anything /// with such functions; however, it is advantageous to say "false" when possible to /// avoid being called to do certain things. /// /// The arguments to a function are provided for context so that the lattice can say /// true or false for the same function symbol in different situations. For example, /// a lattice may understand the multiplication of a variable and a constant but not /// of two variables. The implementation of a lattice should not hold on to the /// arguments. /// /// The function symbol. /// The argument context. /// True if it may understand f, false if it does not understand f. public abstract bool Understands(IFunctionSymbol! f, IList/**/! args); /// /// Return an expression that is equivalent to the given expression that does not /// contain the given variable according to the lattice element and queryable. /// /// The lattice element. /// A queryable for asking addtional information. /// The expression to find an equivalent expression. /// The variable to eliminate. /// The set of variables that can't be used in the resulting expression. /// /// An equivalent expression to without /// or null if not possible. /// public abstract IExpr/*?*/ EquivalentExpr(Element! e, IQueryable! q, IExpr! expr, IVariable! var, Set/**/! prohibitedVars); /// /// Answers a query about whether the given predicate holds given the lattice element. /// /// The lattice element. /// The predicate. /// Yes, No, or Maybe. public abstract Answer CheckPredicate(Element! e, IExpr! pred); /// /// Answers a disequality about two variables. The same information could be obtained /// by asking CheckPredicate, but a different implementation may be simpler and more /// efficient. /// /// The lattice element. /// The first variable. /// The second variable. /// Yes, No, or Maybe. public abstract Answer CheckVariableDisequality(Element! e, IVariable! var1, IVariable! var2); /// /// A default implementation of the given /// the appropriate expression factories by calling CheckPredicate. /// protected Answer DefaultCheckVariableDisequality( IPropExprFactory! propExprFactory, IValueExprFactory! valExprFactory, Element! e, IVariable! var1, IVariable! var2) { return this.CheckPredicate(e, propExprFactory.Not(valExprFactory.Eq(var1, var2))); } private Element! ApplyCombineNameMap(Element! e, ICombineNameMap! eToResult) { Element! result = e; foreach (G.KeyValuePair*/!> entry in eToResult.GetSourceToResult()) { IVariable! sourceName = entry.Key; ISet/**/!>! emptyDictionary1 = new G.Dictionary*/!>(); private static readonly G.Dictionary! emptyDictionary2 = new G.Dictionary(); public ISet/**//*?*/ GetResultNames(IVariable! srcname) { ArraySet a = new ArraySet(); a.Add(srcname); return a; } public IVariable/*?*/ GetSourceName(IVariable! resname) { return resname; } //TODO: uncomment when works in compiler //public G.IEnumerable*/!>> GetSourceToResult() public IEnumerable! GetSourceToResult() { return emptyDictionary1; } //public G.IEnumerable> GetResultToSource() public IEnumerable! GetResultToSource() { return emptyDictionary2; } private IdentityCombineNameMap() { } } public abstract string! ToString(Element! e); // for debugging #region Support for MultiLattice to uniquely number every subclass of Lattice private static Hashtable/**/! indexMap = new Hashtable(); private static Hashtable/**/! reverseIndexMap = new Hashtable(); private static int globalCount = 0; protected virtual object! UniqueId { get { return (!)this.GetType(); } } public int Index { get { object unique = this.UniqueId; if (indexMap.ContainsKey(unique)) { object index = indexMap[unique]; assert index != null; // this does nothing for nonnull analysis if (index != null) { return (int)index; } return 0; } else { int myIndex = globalCount++; indexMap[unique] = myIndex; reverseIndexMap[myIndex] = this; return myIndex; } } } public static Lattice GetGlobalLattice(int i) { return reverseIndexMap[i] as Lattice; } #endregion public static bool LogSwitch = false; } /// /// Defines the relation between names used in the respective input lattice elements to the /// various combination operators (Join,Widen,Meet,AtMost) and the names that should be used /// in the resulting lattice element. /// public interface ICombineNameMap { ISet/**//*?*/ GetResultNames(IVariable! srcname); IVariable/*?*/ GetSourceName(IVariable! resname); //TODO: uncommet when works in compiler //G.IEnumerable*/!>> GetSourceToResult(); IEnumerable! GetSourceToResult(); //G.IEnumerable> GetResultToSource(); IEnumerable! GetResultToSource(); } /// /// Provides statistics on the number of times an operation is performed /// and forwards the real operations to the given lattice in the constructor. /// public class StatisticsLattice : Lattice { readonly Lattice! lattice; int eliminateCount; int renameCount; int constrainCount; int toPredicateCount; int atMostCount; int topCount; int bottomCount; int isTopCount; int isBottomCount; int joinCount; int meetCount; int widenCount; int understandsCount; int equivalentExprCount; int checkPredicateCount; int checkVariableDisequalityCount; public StatisticsLattice(Lattice! lattice) : base(lattice.valueExprFactory) { this.lattice = lattice; // base(lattice.valueExprFactory); } public override Element! Eliminate(Element! e, IVariable! variable) { eliminateCount++; return lattice.Eliminate(e, variable); } public override Element! Rename(Element! e, IVariable! oldName, IVariable! newName) { renameCount++; return lattice.Rename(e, oldName, newName); } public override Element! Constrain(Element! e, IExpr! expr) { constrainCount++; return lattice.Constrain(e, expr); } public override bool Understands(IFunctionSymbol! f, IList! args) { understandsCount++; return lattice.Understands(f, args); } public override IExpr/*?*/ EquivalentExpr(Element! e, IQueryable! q, IExpr! expr, IVariable! var, ISet/**/! prohibitedVars) { equivalentExprCount++; return lattice.EquivalentExpr(e, q, expr, var, prohibitedVars); } public override Answer CheckPredicate(Element! e, IExpr! pred) { checkPredicateCount++; return lattice.CheckPredicate(e, pred); } public override Answer CheckVariableDisequality(Element! e, IVariable! var1, IVariable! var2) { checkVariableDisequalityCount++; return lattice.CheckVariableDisequality(e, var1, var2); } public override IExpr! ToPredicate(Element! e) { toPredicateCount++; return lattice.ToPredicate(e); } public override string! ToString(Element! e) { return lattice.ToString(e); } [Pure][Reads(ReadsAttribute.Reads.Owned)] public override string! ToString() { return string.Format( "StatisticsLattice: #Eliminate={0} #Rename={1} #Constrain={2} #ToPredicate={3} " + "#Understands={4} #EquivalentExpr={5} #CheckPredicate={6} #CheckVariableDisequality={7} " + "#AtMost={8} #Top={9} #Bottom={9} #IsTop={10} #IsBottom={11} " + "#NonTrivialJoin={12} #NonTrivialMeet={13} #Widen={14}", eliminateCount, renameCount, constrainCount, toPredicateCount, understandsCount, equivalentExprCount, checkPredicateCount, checkVariableDisequalityCount, atMostCount, topCount, bottomCount, isTopCount, isBottomCount, joinCount, meetCount, widenCount); } protected override bool AtMost(Element! a, Element! b) { atMostCount++; return lattice.LowerThan(a, b); } public override Element! Top { get { topCount++; return lattice.Top; } } public override Element! Bottom { get { bottomCount++; return lattice.Bottom; } } public override bool IsTop(Element! e) { isTopCount++; return lattice.IsTop(e); } public override bool IsBottom(Element! e) { isBottomCount++; return lattice.IsBottom(e); } public override Element! NontrivialJoin(Element! a, Element! b) { joinCount++; return lattice.NontrivialJoin(a, b); } public override Element! NontrivialMeet(Element! a, Element! b) { meetCount++; return lattice.NontrivialMeet(a, b); } public override Element! Widen(Element! a, Element! b) { widenCount++; return lattice.Widen(a, b); } public override void Validate() { base.Validate(); lattice.Validate(); } protected override object! UniqueId { get { // use the base id, not the underlying-lattice id (is that the right thing to do?) return base.UniqueId; } } } public sealed class LatticeQueryable : IQueryable { private Lattice! lattice; private Lattice.Element! element; public LatticeQueryable(Lattice! lattice, Lattice.Element! element) { this.lattice = lattice; this.element = element; // base(); } public Answer CheckPredicate(IExpr! pred) { return lattice.CheckPredicate(element, pred); } public Answer CheckVariableDisequality(IVariable! var1, IVariable! var2) { return lattice.CheckVariableDisequality(element, var1, var2); } } }