summaryrefslogtreecommitdiff
path: root/Source
diff options
context:
space:
mode:
authorGravatar Dan Rosén <danr@chalmers.se>2014-08-01 09:57:25 -0700
committerGravatar Dan Rosén <danr@chalmers.se>2014-08-01 09:57:25 -0700
commita32ac3bff9a24b812d133883fb9f8df5341b7bb9 (patch)
tree917d9933ee9b8b126d02b5c09ee0f36efe6052bc /Source
parent315922109c235044f985ca19e1bfbe5b95d1873c (diff)
Add alpha equivalence check for Expr, and use it when lambda lifting
Diffstat (limited to 'Source')
-rw-r--r--Source/Core/AbsyExpr.cs16
-rw-r--r--Source/Core/AbsyQuant.cs75
-rw-r--r--Source/Core/AlphaEquality.cs162
-rw-r--r--Source/Core/Core.csproj1
-rw-r--r--Source/Core/LambdaHelper.cs85
-rw-r--r--Source/Core/Util.cs12
6 files changed, 308 insertions, 43 deletions
diff --git a/Source/Core/AbsyExpr.cs b/Source/Core/AbsyExpr.cs
index bc603ea5..7e71453b 100644
--- a/Source/Core/AbsyExpr.cs
+++ b/Source/Core/AbsyExpr.cs
@@ -1893,7 +1893,19 @@ namespace Microsoft.Boogie {
this.Type = type;
}
- public string/*!*/ FunctionName {
+ public override bool Equals(object obj) {
+ TypeCoercion other = obj as TypeCoercion;
+ if (other == null) {
+ return false;
+ } else {
+ return object.Equals(Type, other.Type);
+ }
+ }
+
+
+
+ public
+ string/*!*/ FunctionName {
get {
Contract.Ensures(Contract.Result<string>() != null);
@@ -2110,7 +2122,7 @@ namespace Microsoft.Boogie {
return false;
NAryExpr other = (NAryExpr)obj;
- return object.Equals(this.Fun, other.Fun) && object.Equals(this.Args, other.Args);
+ return this.Args.ListEquals(other.Args) && object.Equals(this.Fun, other.Fun);
}
[Pure]
public override int GetHashCode() {
diff --git a/Source/Core/AbsyQuant.cs b/Source/Core/AbsyQuant.cs
index c9e8f210..9425f698 100644
--- a/Source/Core/AbsyQuant.cs
+++ b/Source/Core/AbsyQuant.cs
@@ -57,7 +57,6 @@ namespace Microsoft.Boogie {
Contract.Invariant(Body != null);
}
-
public BinderExpr(IToken/*!*/ tok, List<TypeVariable>/*!*/ typeParameters,
List<Variable>/*!*/ dummies, QKeyValue kv, Expr/*!*/ body)
: base(tok)
@@ -77,26 +76,45 @@ namespace Microsoft.Boogie {
get;
}
+ protected static bool CompareAttributesAndTriggers = false;
+
+ public static bool EqualWithAttributesAndTriggers(object a, object b) {
+ CompareAttributesAndTriggers = true;
+ var res = object.Equals(a, b);
+ Contract.Assert(CompareAttributesAndTriggers);
+ CompareAttributesAndTriggers = false;
+ return res;
+ }
+
[Pure]
[Reads(ReadsAttribute.Reads.Nothing)]
public override bool Equals(object obj) {
- if (obj == null)
+ return BinderEquals(obj);
+ }
+
+ public bool BinderEquals(object obj) {
+ if (obj == null) {
return false;
+ }
if (!(obj is BinderExpr) ||
- this.Kind != ((BinderExpr)obj).Kind)
+ this.Kind != ((BinderExpr) obj).Kind) {
return false;
+ }
- BinderExpr other = (BinderExpr)obj;
- // Note, we consider quantifiers equal modulo the Triggers.
- return object.Equals(this.TypeParameters, other.TypeParameters)
- && object.Equals(this.Dummies, other.Dummies)
- && object.Equals(this.Body, other.Body);
+ var other = (BinderExpr) obj;
+
+ var a = this.TypeParameters.ListEquals(other.TypeParameters);
+ var b = this.Dummies.ListEquals(other.Dummies);
+ var c = !CompareAttributesAndTriggers || object.Equals(this.Attributes, other.Attributes);
+ var d = object.Equals(this.Body, other.Body);
+
+ return a && b && c && d;
}
[Pure]
public override int GetHashCode() {
int h = this.Dummies.GetHashCode();
- // Note, we consider quantifiers equal modulo the Triggers.
+ // Note, we don't hash triggers and attributes
h ^= this.Body.GetHashCode();
h = h * 5 + this.TypeParameters.GetHashCode();
h *= ((int)Kind + 1);
@@ -335,6 +353,22 @@ namespace Microsoft.Boogie {
public override Absy StdDispatch(StandardVisitor visitor) {
return visitor.VisitQKeyValue(this);
}
+
+ public override bool Equals(object obj) {
+ var other = obj as QKeyValue;
+ if (other == null) {
+ return false;
+ } else {
+ return Key == other.Key && object.Equals(Params, other.Params) &&
+ (Next == null
+ ? other.Next == null
+ : object.Equals(Next, other.Next));
+ }
+ }
+
+ public override int GetHashCode() {
+ throw new NotImplementedException();
+ }
}
public class Trigger : Absy {
@@ -432,6 +466,20 @@ namespace Microsoft.Boogie {
Contract.Ensures(Contract.Result<Absy>() != null);
return visitor.VisitTrigger(this);
}
+
+ public override bool Equals(object obj) {
+ var other = obj as Trigger;
+ if (other == null) {
+ return false;
+ } else {
+ return this.Tr.ListEquals(other.Tr) &&
+ (Next == null ? other.Next == null : object.Equals(Next, other.Next));
+ }
+ }
+
+ public override int GetHashCode() {
+ throw new NotImplementedException();
+ }
}
public class ForallExpr : QuantifierExpr {
@@ -720,6 +768,15 @@ namespace Microsoft.Boogie {
}
}
+ public override bool Equals(object obj) {
+ var other = obj as QuantifierExpr;
+ if (other == null) {
+ return false;
+ } else {
+ return this.BinderEquals(obj) &&
+ (!CompareAttributesAndTriggers || object.Equals(Triggers, other.Triggers));
+ }
+ }
}
diff --git a/Source/Core/AlphaEquality.cs b/Source/Core/AlphaEquality.cs
new file mode 100644
index 00000000..1d4a1d95
--- /dev/null
+++ b/Source/Core/AlphaEquality.cs
@@ -0,0 +1,162 @@
+//-----------------------------------------------------------------------------
+//
+// Copyright (C) Microsoft Corporation. All Rights Reserved.
+//
+//-----------------------------------------------------------------------------
+
+using System.ComponentModel;
+
+namespace Microsoft.Boogie
+{
+
+ using System;
+ using System.IO;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Diagnostics.Contracts;
+
+ public class AlphaEquality : IEqualityComparer<Expr>
+ {
+ private readonly DeBruijnRenamer deBruijn = new DeBruijnRenamer();
+
+ bool IEqualityComparer<Expr>.Equals(Expr x, Expr y) {
+ var nx = deBruijn.Rename(x);
+ var ny = deBruijn.Rename(y);
+ return BinderExpr.EqualWithAttributesAndTriggers(nx, ny);
+ }
+
+ int IEqualityComparer<Expr>.GetHashCode(Expr obj) {
+ return 0;
+ // Best we can do because GetHashCode for Expression don't respect its equality.
+ // When it does, we can instead use:
+ // return deBruijn.Rename(obj).GetHashCode();
+ }
+
+ // Renames expressions into deBruijn indicies, such as
+ // (lambda x : int :: x + a)
+ // into
+ // (lambda bv#0 : int :: bv#0 + fv#0)
+ // It does not handle type variables yet, but it could be added.
+ //
+ // This class could be made public, but it is not since the Rename method
+ // could then leak FreeVariables out of here.
+ private class DeBruijnRenamer : Duplicator
+ {
+
+ // Maps from index positions and types to new variables
+ private readonly TypeDict<BoundVariable> boundVars =
+ new TypeDict<BoundVariable>("bv", ti => new BoundVariable(Token.NoToken, ti));
+
+ private readonly TypeDict<FreeVariable> freeVars =
+ new TypeDict<FreeVariable>("fv", ti => new FreeVariable(ti));
+
+ // These three variables are reset at the beginning of every renaming
+ private int boundVarCount, freeVarCount;
+ private Dictionary<Variable, FreeVariable> freeVarMap;
+
+ // Cached, previous results
+ private readonly Dictionary<Expr, Expr> cache = new Dictionary<Expr, Expr>();
+
+ public Expr Rename(Expr e) {
+ Expr ne;
+ if (!cache.TryGetValue(e, out ne)) {
+ boundVarCount = 0;
+ freeVarCount = 0;
+ freeVarMap = new Dictionary<Variable, FreeVariable>();
+
+ ne = VisitExpr(e);
+ cache[e] = ne;
+#if DEBUG_ALPHA_RENAMING
+ var wr = new TokenTextWriter("<console>", Console.Out, true);
+ Console.Write("nm( ");
+ e.Emit(wr);
+ Console.WriteLine(" )");
+ Console.Write(" = ");
+ ne.Emit(wr);
+ Console.WriteLine("");
+ Console.WriteLine("h = " + ne.GetHashCode());
+#endif
+ }
+ return ne;
+ }
+
+ public override BinderExpr VisitBinderExpr(BinderExpr node) {
+ var subst = new Dictionary<Variable, Expr>();
+ var newBound = new List<Variable>();
+ foreach (var bv in node.Dummies) {
+ var bvNew = boundVars[boundVarCount++, bv.TypedIdent.Type];
+ newBound.Add(bvNew);
+ subst[bv] = new IdentifierExpr(Token.NoToken, bvNew);
+ }
+ node.Dummies = this.VisitVariableSeq(newBound);
+ node.Body = this.VisitExpr(Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), node.Body));
+ return node;
+ }
+
+ public override Variable VisitVariable(Variable node) {
+ FreeVariable fv;
+ var bv = node as BoundVariable;
+ if (boundVars.ContainsValue(bv)) {
+ return node;
+ } else if (freeVarMap.TryGetValue(node, out fv)) {
+ return fv;
+ } else {
+ return freeVarMap[node] = freeVars[freeVarCount++, node.TypedIdent.Type];
+ }
+ }
+
+ public override Expr VisitIdentifierExpr(IdentifierExpr node) {
+ var ie = (IdentifierExpr) base.VisitIdentifierExpr(node);
+ // Need to fix up the name, since IdentifierExpr's equality also checks the name
+ ie.Name = ie.Decl.TypedIdent.Name;
+ return ie;
+ }
+
+ private class TypeDict<A>
+ {
+ private readonly Dictionary<Tuple<int, Type>, A> vars = new Dictionary<Tuple<int, Type>, A>();
+
+ private readonly string Prefix; // either "bv" or "fv"
+ private readonly Func<TypedIdent, A> Mk; // either new BoundVar or new FreeVar
+
+ public TypeDict(string prefix, Func<TypedIdent, A> mk) {
+ Prefix = prefix;
+ Mk = mk;
+ }
+
+ // For debugging purposes, we create unique names when types differ, but the index are the same.
+ private int created = 0;
+
+ // Make sure that this index and this type is always mapped to the same variable
+ public A this[int i, Type t] {
+ get {
+ A v;
+ if (!vars.TryGetValue(Tuple.Create(i, t), out v)) {
+ v = Mk(new TypedIdent(Token.NoToken, Prefix + i + "#" + created++, t));
+ vars[Tuple.Create(i, t)] = v;
+ }
+ return v;
+ }
+ }
+
+ public bool ContainsValue(A a) {
+ return vars.ContainsValue(a);
+ }
+ }
+
+ private class FreeVariable : Variable
+ {
+ public FreeVariable(TypedIdent ti) : base(Token.NoToken, ti) {}
+
+ public override bool IsMutable {
+ get { throw new cce.UnreachableException(); }
+ }
+
+ public override void Register(ResolutionContext rc) {
+ throw new cce.UnreachableException();
+ }
+ }
+ }
+ }
+}
diff --git a/Source/Core/Core.csproj b/Source/Core/Core.csproj
index 881daa7f..27f4eea7 100644
--- a/Source/Core/Core.csproj
+++ b/Source/Core/Core.csproj
@@ -164,6 +164,7 @@
<Compile Include="AbsyExpr.cs" />
<Compile Include="AbsyQuant.cs" />
<Compile Include="AbsyType.cs" />
+ <Compile Include="AlphaEquality.cs" />
<Compile Include="InterProceduralReachabilityGraph.cs" />
<Compile Include="CommandLineOptions.cs" />
<Compile Include="DeadVarElim.cs" />
diff --git a/Source/Core/LambdaHelper.cs b/Source/Core/LambdaHelper.cs
index c884be3c..45c68fdd 100644
--- a/Source/Core/LambdaHelper.cs
+++ b/Source/Core/LambdaHelper.cs
@@ -52,6 +52,9 @@ namespace Microsoft.Boogie {
}
private class LambdaVisitor : StandardVisitor {
+ private readonly Dictionary<Expr, FunctionCall> liftedLambdas =
+ new Dictionary<Expr, FunctionCall>(new AlphaEquality());
+
internal List<Expr/*!*/>/*!*/ lambdaAxioms = new List<Expr/*!*/>();
internal List<Function/*!*/>/*!*/ lambdaFunctions = new List<Function/*!*/>();
[ContractInvariantMethod]
@@ -148,43 +151,61 @@ namespace Microsoft.Boogie {
}
}
+ var sw = new System.IO.StringWriter();
+ var wr = new TokenTextWriter(sw, true);
+ lambda.Emit(wr);
+ string lam_str = sw.ToString();
+
+ FunctionCall fcall;
IToken tok = lambda.tok;
Formal res = new Formal(tok, new TypedIdent(tok, TypedIdent.NoName, cce.NonNull(lambda.Type)), false);
- Function fn = new Function(tok, "lambda@" + lambdaid++, freshTypeVars, formals, res, "auto-generated lambda function",
- Substituter.Apply(Substituter.SubstitutionFromHashtable(substFnAttrs), lambdaAttrs));
- lambdaFunctions.Add(fn);
- FunctionCall fcall = new FunctionCall(new IdentifierExpr(tok, fn.Name));
- fcall.Func = fn; // resolve here
+ if (liftedLambdas.TryGetValue(lambda, out fcall)) {
+ if (CommandLineOptions.Clo.TraceVerify) {
+ Console.WriteLine("Old lambda: {0}", lam_str);
+ }
+ } else {
+ if (CommandLineOptions.Clo.TraceVerify) {
+ Console.WriteLine("New lambda: {0}", lam_str);
+ }
+ Function fn = new Function(tok, "lambda#" + lambdaid++, freshTypeVars, formals, res, "auto-generated lambda function",
+ Substituter.Apply(Substituter.SubstitutionFromHashtable(substFnAttrs), lambdaAttrs));
+
+ fcall = new FunctionCall(new IdentifierExpr(tok, fn.Name));
+ fcall.Func = fn; // resolve here
+ liftedLambdas[lambda] = fcall;
- List<Expr/*!*/> selectArgs = new List<Expr/*!*/>();
- foreach (Variable/*!*/ v in lambda.Dummies) {
- Contract.Assert(v != null);
- selectArgs.Add(new IdentifierExpr(v.tok, v));
- }
- NAryExpr axcall = new NAryExpr(tok, fcall, axCallArgs);
- axcall.Type = res.TypedIdent.Type;
- axcall.TypeParameters = SimpleTypeParamInstantiation.From(freeTypeVars, fnTypeVarActuals);
- NAryExpr select = Expr.Select(axcall, selectArgs);
- select.Type = lambdaBody.Type;
- List<Type/*!*/> selectTypeParamActuals = new List<Type/*!*/>();
- List<TypeVariable> forallTypeVariables = new List<TypeVariable>();
- foreach (TypeVariable/*!*/ tp in lambda.TypeParameters) {
- Contract.Assert(tp != null);
- selectTypeParamActuals.Add(tp);
- forallTypeVariables.Add(tp);
+ List<Expr/*!*/> selectArgs = new List<Expr/*!*/>();
+ foreach (Variable/*!*/ v in lambda.Dummies) {
+ Contract.Assert(v != null);
+ selectArgs.Add(new IdentifierExpr(v.tok, v));
+ }
+ NAryExpr axcall = new NAryExpr(tok, fcall, axCallArgs);
+ axcall.Type = res.TypedIdent.Type;
+ axcall.TypeParameters = SimpleTypeParamInstantiation.From(freeTypeVars, fnTypeVarActuals);
+ NAryExpr select = Expr.Select(axcall, selectArgs);
+ select.Type = lambdaBody.Type;
+ List<Type/*!*/> selectTypeParamActuals = new List<Type/*!*/>();
+ List<TypeVariable> forallTypeVariables = new List<TypeVariable>();
+ foreach (TypeVariable/*!*/ tp in lambda.TypeParameters) {
+ Contract.Assert(tp != null);
+ selectTypeParamActuals.Add(tp);
+ forallTypeVariables.Add(tp);
+ }
+ forallTypeVariables.AddRange(freeTypeVars);
+ select.TypeParameters = SimpleTypeParamInstantiation.From(lambda.TypeParameters, selectTypeParamActuals);
+
+ Expr bb = Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), lambdaBody);
+ NAryExpr body = Expr.Eq(select, bb);
+ body.Type = Type.Bool;
+ body.TypeParameters = SimpleTypeParamInstantiation.EMPTY;
+ Trigger trig = new Trigger(select.tok, true, new List<Expr> { select });
+
+ lambdaFunctions.Add(fn);
+ lambdaAxioms.Add(new ForallExpr(tok, forallTypeVariables, dummies,
+ Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), lambdaAttrs),
+ trig, body));
}
- forallTypeVariables.AddRange(freeTypeVars);
- select.TypeParameters = SimpleTypeParamInstantiation.From(lambda.TypeParameters, selectTypeParamActuals);
-
- Expr bb = Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), lambdaBody);
- NAryExpr body = Expr.Eq(select, bb);
- body.Type = Type.Bool;
- body.TypeParameters = SimpleTypeParamInstantiation.EMPTY;
- Trigger trig = new Trigger(select.tok, true, new List<Expr> { select });
- lambdaAxioms.Add(new ForallExpr(tok, forallTypeVariables, dummies,
- Substituter.Apply(Substituter.SubstitutionFromHashtable(subst), lambdaAttrs),
- trig, body));
NAryExpr call = new NAryExpr(tok, fcall, callArgs);
call.Type = res.TypedIdent.Type;
diff --git a/Source/Core/Util.cs b/Source/Core/Util.cs
index 75cb25aa..1b8353e1 100644
--- a/Source/Core/Util.cs
+++ b/Source/Core/Util.cs
@@ -66,6 +66,18 @@ namespace Microsoft.Boogie {
{
return source1.Zip(source2, (e1, e2) => new Tuple<TSource1, TSource2>(e1, e2));
}
+
+ public static bool ListEquals<A>(this List<A> xs, List<A> ys) {
+ var equal = xs.Count == ys.Count;
+ for (int i = 0; equal && i < xs.Count; i++) {
+ equal = object.Equals(xs[i], ys[i]);
+ }
+ return equal;
+ }
+
+ public static int ListHash<A>(this List<A> xs) {
+ return xs.Aggregate(xs.Count, (current, x) => current ^ xs.GetHashCode());
+ }
}
public class TokenTextWriter : IDisposable {