5 // Alexander Chebaturkin (chebaturkin@gmail.com)
7 // Copyright (C) 2011 Alexander Chebaturkin
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.Collections.Generic;
32 using Mono.CodeContracts.Static.DataStructures;
33 using Mono.CodeContracts.Static.Extensions;
35 namespace Mono.CodeContracts.Static.Analysis.HeapAnalysis.SymbolicGraph {
36 class MergeInfo<TFunc, TADomain> : IMergeInfo
37 where TFunc : IEquatable<TFunc>, IConstantInfo
38 where TADomain : IAbstractDomainForEGraph<TADomain>, IEquatable<TADomain> {
40 public readonly SymGraph<TFunc, TADomain> Result;
41 public readonly SymGraph<TFunc, TADomain> Graph1;
42 public readonly SymGraph<TFunc, TADomain> Graph2;
44 public readonly int LastCommonVariable;
45 public readonly bool Widen;
47 private readonly HashSet<SymValue> manifested;
48 private readonly DoubleDictionary<SymValue, SymValue, int> pending_counts;
49 private readonly HashSet<Tuple<SymValue, SymValue, MultiEdge<TFunc, TADomain>>> visited_multi_edges;
51 private DoubleImmutableMap<SymValue, SymValue, SymValue> mappings;
52 private IImmutableSet<SymValue> visited_key1;
53 private LispList<Tuple<SymValue, SymValue, SymValue>> merge_triples;
55 public MergeInfo (SymGraph<TFunc, TADomain> result,
56 SymGraph<TFunc, TADomain> g1,
57 SymGraph<TFunc, TADomain> g2, bool widen)
59 this.mappings = DoubleImmutableMap<SymValue, SymValue, SymValue>.Empty (SymValue.GetUniqueKey);
60 this.visited_key1 = ImmutableSet<SymValue>.Empty (SymValue.GetUniqueKey);
61 this.visited_multi_edges = new HashSet<Tuple<SymValue, SymValue, MultiEdge<TFunc, TADomain>>> ();
62 this.pending_counts = new DoubleDictionary<SymValue, SymValue, int> ();
63 this.manifested = new HashSet<SymValue> ();
65 this.LastCommonVariable = result.IdGenerator;
74 #region IMergeInfo Members
75 public bool Changed { get; set; }
77 public IEnumerable<Tuple<SymValue, SymValue, SymValue>> MergeTriples
79 get { return this.merge_triples.AsEnumerable (); }
82 public IImmutableMap<SymValue, LispList<SymValue>> ForwardG1Map
84 get { return GetForwardGraphMap ((t) => t.Item1); }
87 public IImmutableMap<SymValue, LispList<SymValue>> ForwardG2Map
89 get { return GetForwardGraphMap ((t) => t.Item2); }
92 public bool IsResultGraph<TFunc1, TAbstractDomain> (SymGraph<TFunc1, TAbstractDomain> graph)
93 where TFunc1 : IEquatable<TFunc1>, IConstantInfo
94 where TAbstractDomain : IAbstractDomainForEGraph<TAbstractDomain>, IEquatable<TAbstractDomain>
96 return Equals (graph, this.Result);
99 public bool IsGraph1<TFunc1, TAbstractDomain> (SymGraph<TFunc1, TAbstractDomain> graph)
100 where TFunc1 : IEquatable<TFunc1>, IConstantInfo
101 where TAbstractDomain : IAbstractDomainForEGraph<TAbstractDomain>, IEquatable<TAbstractDomain>
103 return (Equals (this.Graph1, graph) || Equals (this.Graph1.Parent, graph) && Equals (this.Graph1.Updates, graph.Updates));
106 public bool IsGraph2<TFunc1, TAbstractDomain> (SymGraph<TFunc1, TAbstractDomain> graph)
107 where TFunc1 : IEquatable<TFunc1>, IConstantInfo
108 where TAbstractDomain : IAbstractDomainForEGraph<TAbstractDomain>, IEquatable<TAbstractDomain>
110 return (Equals (this.Graph2, graph) || Equals (this.Graph2.Parent, graph) && Equals (this.Graph2.Updates, graph.Updates));
114 public void AddMapping (SymValue v1, SymValue v2, SymValue result)
116 if (v1 != null && v2 != null)
117 this.mappings = this.mappings.Add (v1, v2, result);
119 this.visited_key1 = this.visited_key1.Add (v1);
121 this.visited_key1 = this.visited_key1.Add (v2);
123 AddMergeTriple (v1, v2, result);
126 public SymValue AddJointEdge (SymValue v1Target, SymValue v2Target, TFunc function, SymValue resultArg)
128 SymValue result = LookupMapping (v1Target, v2Target);
129 bool newEdge = false;
132 if (IsMappingAlreadyAdded (v1Target, v2Target))
134 if (DebugOptions.Debug)
135 Console.WriteLine ("---SymGraph changed due to pre-existing mapping in G1 of {0}", v1Target);
137 if (v1Target == null)
139 if (this.manifested.Contains (v2Target))
141 this.manifested.Add (v2Target);
143 if (v2Target == null)
145 if (this.manifested.Contains (v1Target))
147 this.manifested.Add (v1Target);
151 result = v1Target == null || v1Target.UniqueId > this.LastCommonVariable || v1Target != v2Target ? this.Result.FreshSymbol () : v1Target;
152 AddMapping (v1Target, v2Target, result);
154 else if (this.Result.LookupWithoutManifesting (resultArg, function) == result)
156 this.Result[function, resultArg] = result;
157 TADomain val1 = Graph1ADomain (v1Target);
158 TADomain val2 = Graph2ADomain (v2Target);
161 TADomain join = val1.Join (val2, this.Widen, out weaker);
162 this.Result[result] = join;
166 if (DebugOptions.Debug)
168 Console.WriteLine ("----SymGraph changed due to join of abstract values of [{0}, {1}] " +
169 "(prev {2}, new {3}, join {4}", v1Target, v2Target, val1, val2, join);
174 return newEdge ? result : null;
177 public SymValue AddJointEdge (SymValue v1Target, SymValue v2Target, TFunc function, SymValue[] resultArgs)
179 SymValue result = LookupMapping (v1Target, v2Target);
180 bool newEdge = false;
183 if (IsMappingAlreadyAdded (v1Target, v2Target))
185 if (DebugOptions.Debug)
186 Console.WriteLine ("---SymGraph changed due to pre-existing mapping in G1 of {0}", v1Target);
188 if (v1Target == null || v2Target == null)
192 result = v1Target == null || v1Target.UniqueId > this.LastCommonVariable || v1Target != v2Target ? this.Result.FreshSymbol () : v1Target;
193 AddMapping (v1Target, v2Target, result);
195 else if (this.Result.LookupWithoutManifesting (resultArgs, function) == result)
197 this.Result[resultArgs, function] = result;
198 TADomain val1 = Graph1ADomain (v1Target);
199 TADomain val2 = Graph2ADomain (v2Target);
202 TADomain joinValue = val1.Join (val2, this.Widen, out weaker);
204 this.Result[result] = joinValue;
207 if (DebugOptions.Debug)
208 Console.WriteLine ("----SymGraph changed due to join of abstract values of [{0}, {1}] (prev {2}, new {3}, join {4}",
210 val1, val2, joinValue);
214 if (DebugOptions.Debug)
216 Console.WriteLine ("AddJointEdge: ({0}) -{1} -> [{2},{3},{4}]",
217 resultArgs.ToString (", "), function,
218 v1Target, v2Target, result);
220 return newEdge ? result : null;
223 public bool IsCommon (SymValue sv)
225 return sv.UniqueId <= this.LastCommonVariable;
228 public bool AreCommon (SymValue[] svs)
230 return svs.All (sv => IsCommon (sv));
233 public void JoinSymbolicValue (SymValue sv1, SymValue sv2, SymValue r)
235 if (this.Graph2.HasAllBottomFields (sv2)) {
237 foreach (TFunc function in this.Graph1.TermMap.Keys2 (sv1)) {
238 SymValue v1 = this.Graph1.LookupWithoutManifesting (sv1, function);
240 SymValue v2 = this.Graph2.LookupOrBottomPlaceHolder (sv2, function, out isPlaceHolder);
241 if (!isPlaceHolder || function.KeepAsBottomField) {
242 SymValue r1 = AddJointEdge (v1, v2, function, r);
244 JoinSymbolicValue (v1, v2, r1);
248 } else if (!this.Widen && this.Graph1.HasAllBottomFields (sv1)) {
249 if (DebugOptions.Debug)
250 Console.WriteLine ("---SymGraph changed due to an all bottom field value in G1 changing to non-bottom");
253 foreach (TFunc function in this.Graph2.TermMap.Keys2 (sv2)) {
255 SymValue v1 = this.Graph1.LookupOrBottomPlaceHolder (sv1, function, out isPlaceHolder);
256 SymValue v2 = this.Graph2.LookupWithoutManifesting (sv2, function);
257 if (!isPlaceHolder || function.KeepAsBottomField) {
258 SymValue r1 = AddJointEdge (v1, v2, function, r);
260 JoinSymbolicValue (v1, v2, r1);
265 IEnumerable<TFunc> functions;
267 if (this.Graph1.TermMap.Keys2Count (sv1) <= this.Graph2.TermMap.Keys2Count (sv2))
268 functions = this.Graph1.TermMap.Keys2 (sv1);
270 functions = this.Graph2.TermMap.Keys2 (sv2);
271 if (DebugOptions.Debug)
272 Console.WriteLine ("---SymGraph changed because G2 has fewer keys for {0} than {1} in G1", sv2, sv1);
276 if (this.Graph1.TermMap.Keys2Count (sv1) < this.Graph2.TermMap.Keys2Count (sv2)) {
277 functions = this.Graph2.TermMap.Keys2 (sv2);
278 if (DebugOptions.Debug)
279 Console.WriteLine ("---SymGraph changed because G1 has fewer keys for {0} than {1} in G2", sv1, sv2);
282 functions = this.Graph1.TermMap.Keys2 (sv1);
285 foreach (TFunc function in functions) {
286 SymValue v1 = this.Graph1.LookupWithoutManifesting (sv1, function);
287 SymValue v2 = this.Graph2.LookupWithoutManifesting (sv2, function);
290 if (!this.Widen && function.ManifestField) {
291 if (DebugOptions.Debug)
292 Console.WriteLine ("---SymGraph changed due to manifestation of a top edge in G1");
298 if (v2 == null && (this.Widen || !function.ManifestField)) {
299 if (DebugOptions.Debug)
300 Console.WriteLine ("---SymGraph changed due to absence of map {0}-{1} -> in G2", sv2, function);
304 if (v1 != null && v2 != null)
306 //we have to joint ends of edges
307 SymValue r1 = AddJointEdge (v1, v2, function, r);
309 JoinSymbolicValue (v1, v2, r1);
314 JoinMultiEdges (sv1, sv2);
317 public void JoinMultiEdge (SymValue sv1, SymValue sv2, MultiEdge<TFunc, TADomain> edge)
319 var key = new Tuple<SymValue, SymValue, MultiEdge<TFunc, TADomain>> (sv1, sv2, edge);
320 if (!this.visited_multi_edges.Add (key))
323 LispList<SymValue> list1 = this.Graph1.MultiEdgeMap [sv1, edge];
324 LispList<SymValue> list2 = this.Graph2.MultiEdgeMap [sv2, edge];
325 if (list2.IsEmpty ())
327 foreach (SymValue v1 in list1.AsEnumerable ()) {
328 foreach (SymValue v2 in list2.AsEnumerable ()) {
329 if (UpdatePendingCount (v1, v2, edge.Arity)) {
330 SymGraphTerm<TFunc> term1 = this.Graph1.EqualMultiTermsMap [v1];
331 SymGraphTerm<TFunc> term2 = this.Graph2.EqualMultiTermsMap [v2];
332 if (term1.Args != null && term2.Args != null) {
333 var resultRoots = new SymValue[term1.Args.Length];
334 for (int i = 0; i < resultRoots.Length; i++)
335 resultRoots [i] = this.mappings [term1.Args [i], term2.Args [i]];
336 SymValue r = AddJointEdge (v1, v2, edge.Function, resultRoots);
338 JoinSymbolicValue (sv1, sv2, r);
346 public void Replay (SymGraph<TFunc, TADomain> common)
348 PrimeMapWithCommon ();
349 Replay (this.Graph1.Updates, common.Updates);
350 Replay (this.Graph2.Updates, common.Updates);
353 public void ReplayEliminations (SymGraph<TFunc, TADomain> common)
355 ReplayEliminations (this.Graph1.Updates, common.Updates);
356 ReplayEliminations (this.Graph2.Updates, common.Updates);
359 public void Commit ()
364 bool needContinue = false;
365 foreach (var edge in this.Graph1.ValidMultiTerms)
367 SymGraphTerm<TFunc> term = edge.Value;
368 var args = new SymValue[term.Args.Length];
370 for (int i = 0; i < args.Length; ++i)
372 SymValue sv = term.Args[i];
373 if (IsMappingAlreadyAdded (sv, null))
375 if (this.mappings.Keys2 (sv) != null && this.mappings.Keys2 (sv).Count () == 1)
376 args[i] = this.mappings[sv, this.mappings.Keys2 (sv).First ()];
394 SymValue symbol = this.Result.LookupWithoutManifesting (args, term.Function);
397 SymValue key = edge.Key;
398 if (this.mappings.Keys2 (key) != null && this.mappings.Keys2 (key).Count () == 1 && this.mappings[key, this.mappings.Keys2 (key).First ()] == symbol)
407 private IImmutableMap<SymValue, LispList<SymValue>> GetForwardGraphMap (Func<Tuple<SymValue, SymValue, SymValue>, SymValue> sourceSelector)
409 IImmutableMap<SymValue, LispList<SymValue>> res = ImmutableIntKeyMap<SymValue, LispList<SymValue>>.Empty (SymValue.GetUniqueKey);
410 foreach (var tuple in this.merge_triples.AsEnumerable ()) {
411 SymValue sv = sourceSelector (tuple);
413 res = res.Add (sv, res [sv].Cons (tuple.Item3));
418 private bool UpdatePendingCount (SymValue xi, SymValue yi, int arity)
421 this.pending_counts.TryGetValue (xi, yi, out result);
424 this.pending_counts [xi, yi] = result;
431 private void JoinMultiEdges (SymValue sv1, SymValue sv2)
433 if (sv1 == null || sv2 == null)
436 IEnumerable<MultiEdge<TFunc, TADomain>> edges =
437 this.Graph1.MultiEdgeMap.Keys2Count (sv1) > this.Graph2.MultiEdgeMap.Keys2Count (sv2)
438 ? this.Graph2.MultiEdgeMap.Keys2 (sv2)
439 : this.Graph1.MultiEdgeMap.Keys2 (sv1);
440 foreach (var edge in edges)
441 JoinMultiEdge (sv1, sv2, edge);
444 private TADomain Graph1ADomain (SymValue sv)
447 return this.Graph1 [sv];
448 return this.Graph1.UnderlyingTopValue.ForManifestedField ();
451 private TADomain Graph2ADomain (SymValue sv)
454 return this.Graph2 [sv];
455 return this.Graph2.UnderlyingTopValue.ForManifestedField ();
458 private void AddMergeTriple (SymValue v1, SymValue v2, SymValue result)
460 this.merge_triples = this.merge_triples.Cons (new Tuple<SymValue, SymValue, SymValue> (v1, v2, result));
463 private bool IsMappingAlreadyAdded (SymValue v1, SymValue v2)
466 return this.visited_key1.Contains (v1) || this.mappings.ContainsKey1 (v1);
468 return this.visited_key1.Contains (v2);
471 private SymValue LookupMapping (SymValue v1, SymValue v2)
473 if (v1 == null || v2 == null)
476 return this.mappings [v1, v2];
479 private void PrimeMapWithCommon ()
481 LispList<SymValue> rest = null;
482 foreach (SymValue sv in this.Graph1.EqualTermsMap.Keys) {
483 if (IsCommon (sv) && (this.Graph2.EqualTermsMap.ContainsKey (sv) || this.Graph2.EqualMultiTermsMap.ContainsKey (sv))) {
484 if (this.Graph1.MultiEdgeMap.ContainsKey1 (sv))
485 rest = rest.Cons (sv);
486 AddMapping (sv, sv, sv);
489 foreach (SymValue sv in this.Graph1.EqualMultiTermsMap.Keys) {
490 if (IsCommon (sv) && (this.Graph2.EqualTermsMap.ContainsKey (sv) || this.Graph2.EqualMultiTermsMap.ContainsKey (sv)) && this.mappings [sv, sv] == null) {
491 if (this.Graph1.MultiEdgeMap.ContainsKey1 (sv))
492 rest = rest.Cons (sv);
493 AddMapping (sv, sv, sv);
496 while (rest != null) {
497 SymValue sv = rest.Head;
499 foreach (var edge in this.Graph1.MultiEdgeMap.Keys2 (sv))
500 JoinMultiEdge (sv, sv, edge);
504 private void Replay (LispList<Update<TFunc, TADomain>> updates, LispList<Update<TFunc, TADomain>> common)
506 for (Update<TFunc, TADomain> update = Update<TFunc, TADomain>.Reverse (updates, common); update != null; update = update.Next)
507 update.Replay (this);
510 private void ReplayEliminations (LispList<Update<TFunc, TADomain>> updates, LispList<Update<TFunc, TADomain>> common)
512 for (Update<TFunc, TADomain> update = Update<TFunc, TADomain>.Reverse (updates, common); update != null; update = update.Next)
513 update.ReplayElimination (this);