917 lines
27 KiB
C#
917 lines
27 KiB
C#
|
//
|
||
|
// SymGraph.cs
|
||
|
//
|
||
|
// Authors:
|
||
|
// Alexander Chebaturkin (chebaturkin@gmail.com)
|
||
|
//
|
||
|
// Copyright (C) 2011 Alexander Chebaturkin
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||
|
// a copy of this software and associated documentation files (the
|
||
|
// "Software"), to deal in the Software without restriction, including
|
||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||
|
// the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be
|
||
|
// included in all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
//
|
||
|
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.IO;
|
||
|
using System.Linq;
|
||
|
using Mono.CodeContracts.Static.DataStructures;
|
||
|
using Mono.CodeContracts.Static.Extensions;
|
||
|
|
||
|
namespace Mono.CodeContracts.Static.Analysis.HeapAnalysis.SymbolicGraph {
|
||
|
class SymGraph<TFunc, TADomain> : ISymGraph<TFunc, TADomain, SymGraph<TFunc, TADomain>>
|
||
|
where TFunc : IEquatable<TFunc>, IConstantInfo
|
||
|
where TADomain : IAbstractDomainForEGraph<TADomain>, IEquatable<TADomain> {
|
||
|
public const bool DoIncrementalJoin = false;
|
||
|
private static int egraphIdGenerator;
|
||
|
private static SymGraph<TFunc, TADomain> BottomValue;
|
||
|
public readonly SymValue BottomPlaceHolder;
|
||
|
public readonly SymGraph<TFunc, TADomain> Parent;
|
||
|
public readonly TADomain UnderlyingTopValue;
|
||
|
|
||
|
private readonly SymValue const_root;
|
||
|
private readonly int egraph_id;
|
||
|
private readonly int history_size;
|
||
|
|
||
|
private readonly SymGraph<TFunc, TADomain> root_graph;
|
||
|
|
||
|
private readonly TADomain underlying_bottom_value;
|
||
|
private IImmutableMap<SymValue, TADomain> abs_map;
|
||
|
private IImmutableMap<SymValue, SymValue> forw_map;
|
||
|
private bool is_immutable;
|
||
|
|
||
|
public SymGraph (TADomain topValue, TADomain bottomValue)
|
||
|
: this (topValue, bottomValue, false)
|
||
|
{
|
||
|
if (BottomValue != null)
|
||
|
return;
|
||
|
BottomValue = new SymGraph<TFunc, TADomain> (topValue, bottomValue, false);
|
||
|
}
|
||
|
|
||
|
private SymGraph (TADomain topValue, TADomain bottomValue, bool _)
|
||
|
{
|
||
|
this.egraph_id = egraphIdGenerator++;
|
||
|
this.const_root = FreshSymbol ();
|
||
|
|
||
|
TermMap = DoubleImmutableMap<SymValue, TFunc, SymValue>.Empty (SymValue.GetUniqueKey);
|
||
|
MultiEdgeMap = DoubleImmutableMap<SymValue, MultiEdge<TFunc, TADomain>, Sequence<SymValue>>.Empty (SymValue.GetUniqueKey);
|
||
|
this.abs_map = ImmutableIntKeyMap<SymValue, TADomain>.Empty (SymValue.GetUniqueKey);
|
||
|
this.forw_map = ImmutableIntKeyMap<SymValue, SymValue>.Empty (SymValue.GetUniqueKey);
|
||
|
EqualTermsMap = ImmutableIntKeyMap<SymValue, Sequence<SymGraphTerm<TFunc>>>.Empty (SymValue.GetUniqueKey);
|
||
|
EqualMultiTermsMap = ImmutableIntKeyMap<SymValue, SymGraphTerm<TFunc>>.Empty (SymValue.GetUniqueKey);
|
||
|
|
||
|
this.BottomPlaceHolder = FreshSymbol ();
|
||
|
this.abs_map = this.abs_map.Add (this.BottomPlaceHolder, bottomValue);
|
||
|
this.is_immutable = false;
|
||
|
this.history_size = 1;
|
||
|
this.Parent = null;
|
||
|
this.root_graph = this;
|
||
|
Updates = null;
|
||
|
this.UnderlyingTopValue = topValue;
|
||
|
this.underlying_bottom_value = bottomValue;
|
||
|
}
|
||
|
|
||
|
private SymGraph (SymGraph<TFunc, TADomain> from)
|
||
|
{
|
||
|
this.egraph_id = egraphIdGenerator++;
|
||
|
this.const_root = from.const_root;
|
||
|
this.BottomPlaceHolder = from.BottomPlaceHolder;
|
||
|
TermMap = from.TermMap;
|
||
|
MultiEdgeMap = from.MultiEdgeMap;
|
||
|
IdGenerator = from.IdGenerator;
|
||
|
this.abs_map = from.abs_map;
|
||
|
this.forw_map = from.forw_map;
|
||
|
EqualTermsMap = from.EqualTermsMap;
|
||
|
EqualMultiTermsMap = from.EqualMultiTermsMap;
|
||
|
this.UnderlyingTopValue = from.UnderlyingTopValue;
|
||
|
this.underlying_bottom_value = from.underlying_bottom_value;
|
||
|
Updates = from.Updates;
|
||
|
this.Parent = from;
|
||
|
this.root_graph = from.root_graph;
|
||
|
this.history_size = from.history_size + 1;
|
||
|
|
||
|
from.MarkAsImmutable ();
|
||
|
}
|
||
|
|
||
|
public IImmutableMap<SymValue, SymGraphTerm<TFunc>> EqualMultiTermsMap { get; private set; }
|
||
|
public IImmutableMap<SymValue, Sequence<SymGraphTerm<TFunc>>> EqualTermsMap { get; private set; }
|
||
|
public DoubleImmutableMap<SymValue, MultiEdge<TFunc, TADomain>, Sequence<SymValue>> MultiEdgeMap { get; private set; }
|
||
|
public DoubleImmutableMap<SymValue, TFunc, SymValue> TermMap { get; private set; }
|
||
|
public int IdGenerator { get; private set; }
|
||
|
public Sequence<Update<TFunc, TADomain>> Updates { get; private set; }
|
||
|
|
||
|
public bool IsImmutable
|
||
|
{
|
||
|
get { return this.is_immutable; }
|
||
|
}
|
||
|
|
||
|
private int LastSymbolId
|
||
|
{
|
||
|
get { return IdGenerator; }
|
||
|
}
|
||
|
|
||
|
public SymValue this [SymValue[] args, TFunc function]
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
int len = args.Length;
|
||
|
for (int i = 0; i < len; i++)
|
||
|
args [i] = Find (args [i]);
|
||
|
|
||
|
SymValue candidate = FindCandidate (args, function);
|
||
|
if (candidate != null)
|
||
|
return candidate;
|
||
|
candidate = FreshSymbol ();
|
||
|
for (int i = 0; i < len; i++) {
|
||
|
var edge = new MultiEdge<TFunc, TADomain> (function, i, len);
|
||
|
MultiEdgeMap = MultiEdgeMap.Add (args [i], edge, MultiEdgeMap [args [i], edge].Cons (candidate));
|
||
|
}
|
||
|
EqualMultiTermsMap = EqualMultiTermsMap.Add (candidate, new SymGraphTerm<TFunc> (function, args));
|
||
|
AddMultiEdgeUpdate (args, function);
|
||
|
return candidate;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
int len = args.Length;
|
||
|
for (int i = 0; i < len; i++)
|
||
|
args [i] = Find (args [i]);
|
||
|
|
||
|
bool isTermEqual = true;
|
||
|
SymGraphTerm<TFunc> term = EqualMultiTermsMap [value];
|
||
|
if (term.Args != null) {
|
||
|
for (int i = 0; i < len; i++) {
|
||
|
if (term.Args [i] != args [i]) {
|
||
|
isTermEqual = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < len; i++) {
|
||
|
var edge = new MultiEdge<TFunc, TADomain> (function, i, len);
|
||
|
Sequence<SymValue> list = MultiEdgeMap [args [i], edge];
|
||
|
if (isTermEqual && !Sequence<SymValue>.Contains (list, value))
|
||
|
isTermEqual = false;
|
||
|
if (!isTermEqual)
|
||
|
MultiEdgeMap = MultiEdgeMap.Add (args [i], edge, list.Cons (value));
|
||
|
}
|
||
|
if (isTermEqual)
|
||
|
return;
|
||
|
EqualMultiTermsMap = EqualMultiTermsMap.Add (value, new SymGraphTerm<TFunc> (function, args));
|
||
|
AddMultiEdgeUpdate (args, function);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public SymValue this [TFunc function, params SymValue[] args]
|
||
|
{
|
||
|
get { return this [args, function]; }
|
||
|
}
|
||
|
|
||
|
private SymValue this [SymValue source, TFunc function]
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
source = Find (source);
|
||
|
SymValue sv = TermMap [source, function];
|
||
|
SymValue key;
|
||
|
if (sv == null) {
|
||
|
key = FreshSymbol ();
|
||
|
TermMap = TermMap.Add (source, function, key);
|
||
|
EqualTermsMap = EqualTermsMap.Add (key, Sequence<SymGraphTerm<TFunc>>.Cons (new SymGraphTerm<TFunc> (function, source), null));
|
||
|
AddEdgeUpdate (source, function);
|
||
|
} else
|
||
|
key = Find (sv);
|
||
|
|
||
|
return key;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
source = Find (source);
|
||
|
value = Find (value);
|
||
|
|
||
|
TermMap = TermMap.Add (source, function, value);
|
||
|
Sequence<SymGraphTerm<TFunc>> rest = EqualTermsMap [value];
|
||
|
if (rest.IsEmpty () || (!rest.Head.Function.Equals (function) || rest.Head.Args [0] != source))
|
||
|
EqualTermsMap = EqualTermsMap.Add (value, rest.Cons (new SymGraphTerm<TFunc> (function, source)));
|
||
|
|
||
|
AddEdgeUpdate (source, function);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public IEnumerable<Pair<SymValue, SymGraphTerm<TFunc>>> ValidMultiTerms {
|
||
|
get
|
||
|
{
|
||
|
foreach (SymValue sv in EqualMultiTermsMap.Keys) {
|
||
|
SymGraphTerm<TFunc> term = EqualMultiTermsMap [sv];
|
||
|
if (IsValidMultiTerm (term))
|
||
|
yield return new Pair<SymValue, SymGraphTerm<TFunc>> (sv, term);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public SymValue ConstRoot
|
||
|
{
|
||
|
get { return this.const_root; }
|
||
|
}
|
||
|
|
||
|
#region ISymGraph<TFunc,TADomain,SymGraph<TFunc,TADomain>> Members
|
||
|
public TADomain this [SymValue symbol]
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
symbol = Find (symbol);
|
||
|
if (this.abs_map.ContainsKey (symbol))
|
||
|
return this.abs_map [symbol];
|
||
|
|
||
|
return this.UnderlyingTopValue;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
SymValue newSym = Find (symbol);
|
||
|
if (this [symbol].Equals (value))
|
||
|
return;
|
||
|
AddAbstractValueUpdate (newSym);
|
||
|
if (value.IsTop)
|
||
|
this.abs_map = this.abs_map.Remove (newSym);
|
||
|
else
|
||
|
this.abs_map = this.abs_map.Add (newSym, value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public SymValue this [TFunc function]
|
||
|
{
|
||
|
get { return this [this.const_root, function]; }
|
||
|
set { this [this.const_root, function] = value; }
|
||
|
}
|
||
|
|
||
|
public SymValue this [TFunc function, SymValue arg]
|
||
|
{
|
||
|
get { return this [arg, function]; }
|
||
|
set { this [arg, function] = value; }
|
||
|
}
|
||
|
|
||
|
public IEnumerable<TFunc> Constants
|
||
|
{
|
||
|
get { return TermMap.Keys2 (this.const_root); }
|
||
|
}
|
||
|
|
||
|
public IEnumerable<SymValue> Variables
|
||
|
{
|
||
|
get { return TermMap.Keys1; }
|
||
|
}
|
||
|
|
||
|
public SymGraph<TFunc, TADomain> Top
|
||
|
{
|
||
|
get { return new SymGraph<TFunc, TADomain> (this.UnderlyingTopValue, this.underlying_bottom_value); }
|
||
|
}
|
||
|
|
||
|
public SymGraph<TFunc, TADomain> Bottom
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (BottomValue == null) {
|
||
|
BottomValue = new SymGraph<TFunc, TADomain> (this.UnderlyingTopValue, this.underlying_bottom_value);
|
||
|
BottomValue.MarkAsImmutable ();
|
||
|
}
|
||
|
return BottomValue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsTop
|
||
|
{
|
||
|
get { return TermMap.Keys2Count (this.const_root) == 0; }
|
||
|
}
|
||
|
|
||
|
public bool IsBottom
|
||
|
{
|
||
|
get { return this == BottomValue; }
|
||
|
}
|
||
|
|
||
|
public SymValue FreshSymbol ()
|
||
|
{
|
||
|
return new SymValue (++IdGenerator);
|
||
|
}
|
||
|
|
||
|
public SymValue TryLookup (TFunc function)
|
||
|
{
|
||
|
return LookupWithoutManifesting (this.const_root, function);
|
||
|
}
|
||
|
|
||
|
public SymValue TryLookup (TFunc function, SymValue arg)
|
||
|
{
|
||
|
return LookupWithoutManifesting (arg, function);
|
||
|
}
|
||
|
|
||
|
public void Eliminate (TFunc function, SymValue arg)
|
||
|
{
|
||
|
SymValue value = Find (arg);
|
||
|
DoubleImmutableMap<SymValue, TFunc, SymValue> newTermMap = TermMap.Remove (value, function);
|
||
|
if (newTermMap == TermMap)
|
||
|
return;
|
||
|
TermMap = newTermMap;
|
||
|
AddEliminateEdgeUpdate (value, function);
|
||
|
}
|
||
|
|
||
|
public void Eliminate (TFunc function)
|
||
|
{
|
||
|
TermMap = TermMap.Remove (this.const_root, function);
|
||
|
AddEliminateEdgeUpdate (this.const_root, function);
|
||
|
}
|
||
|
|
||
|
public void EliminateAll (SymValue arg)
|
||
|
{
|
||
|
SymValue value = Find (arg);
|
||
|
AddEliminateAllUpdate (value);
|
||
|
TermMap = TermMap.RemoveAll (value);
|
||
|
this [arg] = this.UnderlyingTopValue;
|
||
|
}
|
||
|
|
||
|
public void AssumeEqual (SymValue v1, SymValue v2)
|
||
|
{
|
||
|
var workList = new WorkList<EqualityPair<TFunc, TADomain>> ();
|
||
|
SymValue sv1 = Find (v1);
|
||
|
SymValue sv2 = Find (v2);
|
||
|
|
||
|
if (TryPushEquality (workList, sv1, sv2))
|
||
|
AddEqualityUpdate (sv1, sv2);
|
||
|
|
||
|
DrainEqualityWorkList (workList);
|
||
|
}
|
||
|
|
||
|
public bool IsEqual (SymValue v1, SymValue v2)
|
||
|
{
|
||
|
return Find (v1) == Find (v2);
|
||
|
}
|
||
|
|
||
|
public IEnumerable<TFunc> Functions (SymValue sv)
|
||
|
{
|
||
|
return TermMap.Keys2 (Find (sv));
|
||
|
}
|
||
|
|
||
|
public IEnumerable<SymGraphTerm<TFunc>> EqTerms (SymValue sv)
|
||
|
{
|
||
|
foreach (var term in EqualTermsMap [Find (sv)].AsEnumerable ()) {
|
||
|
if (TryLookup (term.Function, term.Args) == sv)
|
||
|
yield return term;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public SymGraph<TFunc, TADomain> Clone ()
|
||
|
{
|
||
|
return new SymGraph<TFunc, TADomain> (this);
|
||
|
}
|
||
|
|
||
|
public SymGraph<TFunc, TADomain> Join(SymGraph<TFunc, TADomain> that)
|
||
|
{
|
||
|
throw new NotImplementedException();
|
||
|
}
|
||
|
|
||
|
public SymGraph<TFunc, TADomain> Join (SymGraph<TFunc, TADomain> that, bool widening, out bool weaker)
|
||
|
{
|
||
|
IMergeInfo info;
|
||
|
SymGraph<TFunc, TADomain> join = Join (that, out info, widening);
|
||
|
weaker = info.Changed;
|
||
|
return join;
|
||
|
}
|
||
|
|
||
|
public SymGraph<TFunc, TADomain> Widen(SymGraph<TFunc, TADomain> that)
|
||
|
{
|
||
|
throw new NotImplementedException();
|
||
|
}
|
||
|
|
||
|
public SymGraph<TFunc, TADomain> Join (SymGraph<TFunc, TADomain> that, out IMergeInfo mergeInfo, bool widen)
|
||
|
{
|
||
|
SymGraph<TFunc, TADomain> egraph = this;
|
||
|
int updateSize;
|
||
|
SymGraph<TFunc, TADomain> commonTail = ComputeCommonTail (egraph, that, out updateSize);
|
||
|
bool hasCommonTail = commonTail != null;
|
||
|
|
||
|
bool doingIncrementalJoin = hasCommonTail & commonTail != egraph.root_graph & !widen & DoIncrementalJoin;
|
||
|
|
||
|
//debug
|
||
|
|
||
|
if (DebugOptions.Debug)
|
||
|
{
|
||
|
Console.WriteLine ("SymGraph {0}", widen ? "widen" : "join");
|
||
|
if (commonTail != null)
|
||
|
Console.WriteLine ("Last common symbol: {0}", commonTail.LastSymbolId);
|
||
|
|
||
|
Console.WriteLine (" Doing {0}", doingIncrementalJoin ? "incremental join" : "full join");
|
||
|
}
|
||
|
|
||
|
SymGraph<TFunc, TADomain> result;
|
||
|
MergeInfo<TFunc, TADomain> mergeState;
|
||
|
if (doingIncrementalJoin) {
|
||
|
result = new SymGraph<TFunc, TADomain> (commonTail);
|
||
|
mergeState = new MergeInfo<TFunc, TADomain> (result, egraph, that, widen);
|
||
|
mergeState.Replay (commonTail);
|
||
|
mergeState.Commit ();
|
||
|
} else {
|
||
|
result = new SymGraph<TFunc, TADomain> (commonTail);
|
||
|
mergeState = new MergeInfo<TFunc, TADomain> (result, egraph, that, widen);
|
||
|
mergeState.ReplayEliminations (commonTail);
|
||
|
mergeState.AddMapping (egraph.const_root, that.const_root, result.const_root);
|
||
|
mergeState.JoinSymbolicValue (egraph.const_root, that.const_root, result.const_root);
|
||
|
mergeState.Commit ();
|
||
|
}
|
||
|
mergeInfo = mergeState;
|
||
|
|
||
|
if (DebugOptions.Debug)
|
||
|
{
|
||
|
Console.WriteLine (" Result update size {0}", result.Updates.Length ());
|
||
|
Console.WriteLine ("Done with Egraph join: changed = {0}", mergeInfo.Changed ? 1 : 0);
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public void Dump (TextWriter tw)
|
||
|
{
|
||
|
var set = new HashSet<SymValue> ();
|
||
|
var workList = new WorkList<SymValue> ();
|
||
|
IImmutableMap<SymValue, int> triggers = ImmutableIntKeyMap<SymValue, int>.Empty (SymValue.GetUniqueKey);
|
||
|
tw.WriteLine ("EGraphId: {0}", this.egraph_id);
|
||
|
tw.WriteLine ("LastSymbolId: {0}", LastSymbolId);
|
||
|
|
||
|
foreach (TFunc function in TermMap.Keys2 (this.const_root)) {
|
||
|
SymValue sv = this [this.const_root, function];
|
||
|
tw.WriteLine ("{0} = {1}", function, sv);
|
||
|
workList.Add (sv);
|
||
|
}
|
||
|
|
||
|
while (!workList.IsEmpty ()) {
|
||
|
SymValue sv = workList.Pull ();
|
||
|
if (!set.Add (sv))
|
||
|
continue;
|
||
|
|
||
|
foreach (TFunc function in TermMap.Keys2 (sv)) {
|
||
|
SymValue target = this [sv, function];
|
||
|
|
||
|
tw.WriteLine ("{0}({2}) = {1})", function, target, sv);
|
||
|
workList.Add (target);
|
||
|
}
|
||
|
foreach (var edge in MultiEdgeMap.Keys2 (sv)) {
|
||
|
foreach (SymValue target in MultiEdgeMap [sv, edge].AsEnumerable ()) {
|
||
|
if (!UpdateTrigger (target, edge, ref triggers))
|
||
|
continue;
|
||
|
SymGraphTerm<TFunc> term = EqualMultiTermsMap [target];
|
||
|
if (term.Args != null) {
|
||
|
tw.WriteLine ("{0}({1}) = {2}",
|
||
|
term.Function,
|
||
|
term.Args.ToString (", "), target);
|
||
|
workList.Add (target);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
tw.WriteLine ("**Abstract value map");
|
||
|
foreach (SymValue sv in set) {
|
||
|
TADomain abstractValue = this [sv];
|
||
|
if (!abstractValue.IsTop)
|
||
|
tw.WriteLine ("{0} -> {1}", sv, abstractValue);
|
||
|
}
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Implementation of IAbstractDomain<SymGraph<Constant,AbstractValue>>
|
||
|
public SymGraph<TFunc, TADomain> Meet (SymGraph<TFunc, TADomain> that)
|
||
|
{
|
||
|
if (this == that || IsBottom || that.IsTop)
|
||
|
return this;
|
||
|
if (that.IsBottom || IsTop)
|
||
|
return that;
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public bool LessEqual (SymGraph<TFunc, TADomain> that)
|
||
|
{
|
||
|
IImmutableMap<SymValue, Sequence<SymValue>> forwardMap;
|
||
|
IImmutableMap<SymValue, SymValue> backwardMap;
|
||
|
|
||
|
return LessEqual (that, out forwardMap, out backwardMap);
|
||
|
}
|
||
|
|
||
|
public SymGraph<TFunc, TADomain> ImmutableVersion ()
|
||
|
{
|
||
|
MarkAsImmutable ();
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public bool LessEqual (SymGraph<TFunc, TADomain> that,
|
||
|
out IImmutableMap<SymValue, Sequence<SymValue>> forward,
|
||
|
out IImmutableMap<SymValue, SymValue> backward)
|
||
|
{
|
||
|
if (!IsSameEGraph (that))
|
||
|
return InternalLessEqual (this, that, out forward, out backward);
|
||
|
|
||
|
forward = null;
|
||
|
backward = null;
|
||
|
return true;
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
public bool HasAllBottomFields (SymValue sv)
|
||
|
{
|
||
|
if (sv == null)
|
||
|
return false;
|
||
|
|
||
|
return this [sv].HasAllBottomFields;
|
||
|
}
|
||
|
|
||
|
public SymValue LookupOrManifest (TFunc function, SymValue arg, out bool fresh)
|
||
|
{
|
||
|
int oldCnt = IdGenerator;
|
||
|
SymValue result = this [function, arg];
|
||
|
|
||
|
fresh = oldCnt < IdGenerator;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
public SymValue TryLookup (TFunc function, params SymValue[] args)
|
||
|
{
|
||
|
if (args.Length == 0 || args.Length == 1)
|
||
|
return LookupWithoutManifesting (this.const_root, function);
|
||
|
return LookupWithoutManifesting (args, function);
|
||
|
}
|
||
|
|
||
|
public SymValue LookupWithoutManifesting (SymValue sv, TFunc function)
|
||
|
{
|
||
|
if (sv == null)
|
||
|
return null;
|
||
|
sv = Find (sv);
|
||
|
SymValue result = TermMap [sv, function];
|
||
|
|
||
|
if (result == null)
|
||
|
return null;
|
||
|
return Find (result);
|
||
|
}
|
||
|
|
||
|
public SymValue LookupWithoutManifesting (SymValue[] args, TFunc function)
|
||
|
{
|
||
|
int length = args.Length;
|
||
|
for (int i = 0; i < length; i++)
|
||
|
args [i] = Find (args [i]);
|
||
|
return FindCandidate (args, function);
|
||
|
}
|
||
|
|
||
|
public SymValue LookupOrBottomPlaceHolder (SymValue arg, TFunc function, out bool isPlaceHolder)
|
||
|
{
|
||
|
SymValue result = LookupWithoutManifesting (arg, function);
|
||
|
|
||
|
isPlaceHolder = result == null;
|
||
|
return isPlaceHolder ? this.BottomPlaceHolder : result;
|
||
|
}
|
||
|
|
||
|
private SymValue Find (SymValue v)
|
||
|
{
|
||
|
SymValue forw = this.forw_map [v];
|
||
|
if (forw == null)
|
||
|
return v;
|
||
|
|
||
|
return Find (forw);
|
||
|
}
|
||
|
|
||
|
private bool IsOldSymbol (SymValue sv)
|
||
|
{
|
||
|
if (this.Parent == null)
|
||
|
return false;
|
||
|
return sv.UniqueId <= this.Parent.LastSymbolId;
|
||
|
}
|
||
|
|
||
|
private SymValue FindCandidate (SymValue[] args, TFunc function)
|
||
|
{
|
||
|
int length = args.Length;
|
||
|
var multiEdge = new MultiEdge<TFunc, TADomain> (function, 0, length);
|
||
|
for (Sequence<SymValue> list = MultiEdgeMap [args [0], multiEdge]; list != null; list = list.Tail) {
|
||
|
SymGraphTerm<TFunc> term = EqualMultiTermsMap [list.Head];
|
||
|
if (term.Args.Length == length) {
|
||
|
bool found = true;
|
||
|
|
||
|
for (int i = 0; i < length; ++i) {
|
||
|
if (Find (term.Args [i]) != args [i]) {
|
||
|
found = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (found)
|
||
|
return list.Head;
|
||
|
}
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private bool TryPushEquality (WorkList<EqualityPair<TFunc, TADomain>> workList, SymValue sv1, SymValue sv2)
|
||
|
{
|
||
|
if (sv1 != sv2) {
|
||
|
workList.Add (new EqualityPair<TFunc, TADomain> (sv1, sv2));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private void DrainEqualityWorkList (WorkList<EqualityPair<TFunc, TADomain>> workList)
|
||
|
{
|
||
|
while (!workList.IsEmpty ()) {
|
||
|
EqualityPair<TFunc, TADomain> equalityPair = workList.Pull ();
|
||
|
SymValue sv1 = Find (equalityPair.Sv1);
|
||
|
SymValue sv2 = Find (equalityPair.Sv2);
|
||
|
if (sv1 != sv2) {
|
||
|
if (sv1.UniqueId < sv2.UniqueId) {
|
||
|
SymValue tmp = sv1;
|
||
|
sv1 = sv2;
|
||
|
sv2 = tmp;
|
||
|
}
|
||
|
|
||
|
foreach (TFunc function in Functions (sv1)) {
|
||
|
SymValue v2 = LookupWithoutManifesting (sv2, function);
|
||
|
if (v2 == null)
|
||
|
this [sv2, function] = this [sv1, function];
|
||
|
else
|
||
|
TryPushEquality (workList, this [sv1, function], v2);
|
||
|
}
|
||
|
TADomain thisValue = this [sv1];
|
||
|
TADomain thatValue = this [sv2];
|
||
|
foreach (var elem in EqualTermsMap [sv1].AsEnumerable ())
|
||
|
EqualTermsMap = EqualTermsMap.Add (sv2, EqualTermsMap [sv2].Cons (elem));
|
||
|
|
||
|
this.forw_map = this.forw_map.Add (sv1, sv2);
|
||
|
this [sv2] = thisValue.Meet (thatValue);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private IEnumerable<MultiEdge<TFunc, TADomain>> MultiEdges (SymValue sv)
|
||
|
{
|
||
|
return MultiEdgeMap.Keys2 (Find (sv));
|
||
|
}
|
||
|
|
||
|
public IEnumerable<SymGraphTerm<TFunc>> EqMultiTerms (SymValue sv)
|
||
|
{
|
||
|
SymGraphTerm<TFunc> term = EqualMultiTermsMap [sv];
|
||
|
if (term.Args != null && IsValidMultiTerm (term))
|
||
|
yield return term;
|
||
|
}
|
||
|
|
||
|
public bool IsValidSymbol (SymValue sv)
|
||
|
{
|
||
|
return EqualTermsMap.ContainsKey (sv);
|
||
|
}
|
||
|
|
||
|
private bool IsValidMultiTerm (SymGraphTerm<TFunc> term)
|
||
|
{
|
||
|
return LookupWithoutManifesting (term.Args, term.Function) != null;
|
||
|
}
|
||
|
|
||
|
private static SymGraph<TFunc, TADomain> ComputeCommonTail (SymGraph<TFunc, TADomain> g1, SymGraph<TFunc, TADomain> g2, out int updateSize)
|
||
|
{
|
||
|
SymGraph<TFunc, TADomain> graph1 = g1;
|
||
|
SymGraph<TFunc, TADomain> graph2 = g2;
|
||
|
while (graph1 != graph2) {
|
||
|
if (graph1 == null)
|
||
|
break;
|
||
|
if (graph2 == null) {
|
||
|
graph1 = null;
|
||
|
break;
|
||
|
}
|
||
|
if (graph1.history_size > graph2.history_size)
|
||
|
graph1 = graph1.Parent;
|
||
|
else if (graph2.history_size > graph1.history_size)
|
||
|
graph2 = graph2.Parent;
|
||
|
else {
|
||
|
graph1 = graph1.Parent;
|
||
|
graph2 = graph2.Parent;
|
||
|
}
|
||
|
}
|
||
|
SymGraph<TFunc, TADomain> tail = graph1;
|
||
|
int historySize = tail != null ? tail.history_size : 0;
|
||
|
updateSize = g1.history_size + g2.history_size - 2*historySize;
|
||
|
return tail;
|
||
|
}
|
||
|
|
||
|
private static bool InternalLessEqual (SymGraph<TFunc, TADomain> thisG, SymGraph<TFunc, TADomain> thatG,
|
||
|
out IImmutableMap<SymValue, Sequence<SymValue>> forward,
|
||
|
out IImmutableMap<SymValue, SymValue> backward)
|
||
|
{
|
||
|
int updateSize;
|
||
|
SymGraph<TFunc, TADomain> commonTail = ComputeCommonTail (thisG, thatG, out updateSize);
|
||
|
if (thisG.IsImmutable)
|
||
|
thisG = thisG.Clone ();
|
||
|
|
||
|
var workList = new WorkList<EqualityPair<TFunc, TADomain>> ();
|
||
|
workList.Add (new EqualityPair<TFunc, TADomain> (thisG.const_root, thatG.const_root));
|
||
|
IImmutableSet<SymValue> backwardManifested = ImmutableSet<SymValue>.Empty (SymValue.GetUniqueKey);
|
||
|
IImmutableMap<SymValue, SymValue> backwardMap = ImmutableIntKeyMap<SymValue, SymValue>.Empty (SymValue.GetUniqueKey);
|
||
|
IImmutableMap<SymValue, Sequence<SymValue>> forwardMap = ImmutableIntKeyMap<SymValue, Sequence<SymValue>>.Empty (SymValue.GetUniqueKey);
|
||
|
IImmutableMap<SymValue, int> triggers = ImmutableIntKeyMap<SymValue, int>.Empty (SymValue.GetUniqueKey);
|
||
|
|
||
|
while (!workList.IsEmpty ()) {
|
||
|
EqualityPair<TFunc, TADomain> equalityPair = workList.Pull ();
|
||
|
SymValue sv1 = equalityPair.Sv1;
|
||
|
SymValue sv2 = equalityPair.Sv2;
|
||
|
|
||
|
SymValue s;
|
||
|
if (VisitedBefore (sv2, backwardManifested, backwardMap, out s)) {
|
||
|
if (s != null && s == sv1)
|
||
|
continue;
|
||
|
|
||
|
if (DebugOptions.Debug)
|
||
|
Console.WriteLine ("---LessEqual fails due to pre-existing relation: {0} <- {1}", s, sv2);
|
||
|
forward = null;
|
||
|
backward = null;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
TADomain val1 = sv1 == null ? thisG.UnderlyingTopValue.ForManifestedField () : thisG [sv1];
|
||
|
TADomain val2 = thatG [sv2];
|
||
|
if (!val1.LessEqual (val2)) {
|
||
|
if (DebugOptions.Debug)
|
||
|
Console.WriteLine ("---LessEqual fails due to abstract values: !({0} <= {1})", val1, val2);
|
||
|
forward = null;
|
||
|
backward = null;
|
||
|
return false;
|
||
|
}
|
||
|
if (sv1 != null) {
|
||
|
backwardMap = backwardMap.Add (sv2, sv1);
|
||
|
forwardMap = forwardMap.Add (sv1, forwardMap [sv1].Cons (sv2));
|
||
|
} else
|
||
|
backwardManifested = backwardManifested.Add (sv2);
|
||
|
if (thisG.HasAllBottomFields (sv1))
|
||
|
continue;
|
||
|
if (thatG.HasAllBottomFields (sv2)) {
|
||
|
if (DebugOptions.Debug)
|
||
|
{
|
||
|
Console.WriteLine ("---LessEqual fails due to bottom field difference");
|
||
|
}
|
||
|
forward = null;
|
||
|
backward = null;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
foreach (TFunc function in thatG.Functions (sv2)) {
|
||
|
SymValue v1 = thisG [function, sv1];
|
||
|
SymValue v2 = thatG [function, sv2];
|
||
|
if (DebugOptions.Debug)
|
||
|
Console.WriteLine (" {0}-{1}->{2} <=? {3}-{4}->{5}", sv1, function, v1, sv2, function, v2);
|
||
|
workList.Add (new EqualityPair<TFunc, TADomain> (v1, v2));
|
||
|
}
|
||
|
|
||
|
foreach (var e in thatG.MultiEdges (sv2)) {
|
||
|
foreach (SymValue sv in thatG.MultiEdgeMap [sv2, e].AsEnumerable ()) {
|
||
|
if (!UpdateTrigger (sv, e, ref triggers))
|
||
|
continue;
|
||
|
|
||
|
SymGraphTerm<TFunc> term = thatG.EqualMultiTermsMap [sv];
|
||
|
var args = new SymValue[term.Args.Length];
|
||
|
for (int i = 0; i < args.Length; i++)
|
||
|
args [i] = backwardMap [term.Args [i]];
|
||
|
|
||
|
SymValue v1 = thisG.LookupWithoutManifesting (args, e.Function);
|
||
|
if (v1 == null) {
|
||
|
if (DebugOptions.Debug)
|
||
|
Console.WriteLine ("---LessEqual fails due to missing multi term {0}({1})",
|
||
|
e.Function,
|
||
|
string.Join (", ", term.Args.Select (it => it.ToString ())));
|
||
|
forward = null;
|
||
|
backward = null;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
workList.Add (new EqualityPair<TFunc, TADomain> (v1, sv));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
forward = forwardMap;
|
||
|
backward = CompleteWithCommon (backwardMap, thisG, commonTail.IdGenerator);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private static IImmutableMap<SymValue, SymValue> CompleteWithCommon (IImmutableMap<SymValue, SymValue> map,
|
||
|
SymGraph<TFunc, TADomain> thisGraph, int lastCommonId)
|
||
|
{
|
||
|
IEnumerable<SymValue> symValues = thisGraph.EqualTermsMap.Keys.Concat (thisGraph.EqualMultiTermsMap.Keys);
|
||
|
foreach (SymValue sv in symValues) {
|
||
|
if (IsCommon (sv, lastCommonId) && !map.ContainsKey (sv))
|
||
|
map = map.Add (sv, sv);
|
||
|
}
|
||
|
return map;
|
||
|
}
|
||
|
|
||
|
private static bool IsCommon (SymValue sv, int lastCommonId)
|
||
|
{
|
||
|
return sv.UniqueId <= lastCommonId;
|
||
|
}
|
||
|
|
||
|
private static bool UpdateTrigger (SymValue sv, MultiEdge<TFunc, TADomain> edge, ref IImmutableMap<SymValue, int> triggers)
|
||
|
{
|
||
|
int val = triggers [sv] + 1;
|
||
|
triggers = triggers.Add (sv, val);
|
||
|
return (val == edge.Arity);
|
||
|
}
|
||
|
|
||
|
private static bool VisitedBefore (SymValue sv2,
|
||
|
IImmutableSet<SymValue> backwardManifested,
|
||
|
IImmutableMap<SymValue, SymValue> backward,
|
||
|
out SymValue sv1)
|
||
|
{
|
||
|
sv1 = backward [sv2];
|
||
|
return sv1 != null || backwardManifested.Contains (sv2);
|
||
|
}
|
||
|
|
||
|
private bool IsSameEGraph (SymGraph<TFunc, TADomain> that)
|
||
|
{
|
||
|
if (this == that)
|
||
|
return true;
|
||
|
if (that.Parent == this)
|
||
|
return that.Updates == Updates;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private void MarkAsImmutable ()
|
||
|
{
|
||
|
this.is_immutable = true;
|
||
|
}
|
||
|
|
||
|
#region Merge updates
|
||
|
private void AddUpdate (Update<TFunc, TADomain> update)
|
||
|
{
|
||
|
Updates = Updates.Cons (update);
|
||
|
}
|
||
|
|
||
|
private void AddAbstractValueUpdate (SymValue sv)
|
||
|
{
|
||
|
if (!IsOldSymbol (sv))
|
||
|
return;
|
||
|
AddUpdate (new AbstractDomainUpdate<TFunc, TADomain> (sv));
|
||
|
}
|
||
|
|
||
|
private void AddEqualityUpdate (SymValue sv1, SymValue sv2)
|
||
|
{
|
||
|
if (!IsOldSymbol (sv1) || !IsOldSymbol (sv2))
|
||
|
return;
|
||
|
AddUpdate (new EqualityUpdate<TFunc, TADomain> (sv1, sv2));
|
||
|
}
|
||
|
|
||
|
private void AddEdgeUpdate (SymValue from, TFunc function)
|
||
|
{
|
||
|
if (!IsOldSymbol (from))
|
||
|
return;
|
||
|
AddUpdate (new EdgeUpdate<TFunc, TADomain> (from, function));
|
||
|
}
|
||
|
|
||
|
private void AddEliminateAllUpdate (SymValue from)
|
||
|
{
|
||
|
if (!IsOldSymbol (from))
|
||
|
return;
|
||
|
foreach (TFunc function in TermMap.Keys2 (from))
|
||
|
AddUpdate (new EliminateEdgeUpdate<TFunc, TADomain> (from, function));
|
||
|
}
|
||
|
|
||
|
private void AddEliminateEdgeUpdate (SymValue from, TFunc function)
|
||
|
{
|
||
|
if (!IsOldSymbol (from))
|
||
|
return;
|
||
|
AddUpdate (new EliminateEdgeUpdate<TFunc, TADomain> (from, function));
|
||
|
}
|
||
|
|
||
|
private void AddMultiEdgeUpdate (SymValue[] from, TFunc function)
|
||
|
{
|
||
|
for (int i = 0; i < from.Length; i++) {
|
||
|
if (!IsOldSymbol (from [i]))
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
AddUpdate (new MultiEdgeUpdate<TFunc, TADomain> (from, function));
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
public IImmutableMap<SymValue, Sequence<SymValue>> GetForwardIdentityMap ()
|
||
|
{
|
||
|
var res = ImmutableIntKeyMap<SymValue, Sequence<SymValue>>.Empty (SymValue.GetUniqueKey);
|
||
|
foreach (var sv in this.EqualTermsMap.Keys.Union (this.EqualMultiTermsMap.Keys)) {
|
||
|
res = res.Add (sv, Sequence<SymValue>.Cons (sv, null));
|
||
|
}
|
||
|
return res;
|
||
|
}
|
||
|
}
|
||
|
}
|