From be35cf14af3be591a9fabdbed5f9439b00e4ad59 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 15:26:52 +0200 Subject: [PATCH 01/42] Extracted classes into separate files --- Source/Core/AST/Absy.cs | 1075 ---------------------------- Source/Core/AST/AbsyCmd.cs | 219 ------ Source/Core/AST/Block.cs | 226 ++++++ Source/Core/AST/Implementation.cs | 1082 +++++++++++++++++++++++++++++ 4 files changed, 1308 insertions(+), 1294 deletions(-) create mode 100644 Source/Core/AST/Block.cs create mode 100644 Source/Core/AST/Implementation.cs diff --git a/Source/Core/AST/Absy.cs b/Source/Core/AST/Absy.cs index fd844a775..7e0afdc3d 100644 --- a/Source/Core/AST/Absy.cs +++ b/Source/Core/AST/Absy.cs @@ -6,7 +6,6 @@ using System.Diagnostics.Contracts; using System.Security.Cryptography; using Microsoft.BaseTypes; -using Microsoft.Boogie.GraphUtil; namespace Microsoft.Boogie { @@ -3507,1080 +3506,6 @@ public Block getBlock(string label) } } - public class Implementation : DeclWithFormals { - public List LocVars; - - [Rep] public StmtList StructuredStmts; - - [field: Rep] - public List Blocks { - get; - set; - } - public Procedure Proc; - - // Blocks before applying passification etc. - // Both are used only when /inline is set. - public List OriginalBlocks; - public List OriginalLocVars; - - // Map filled in during passification to allow augmented error trace reporting - public Dictionary> debugInfos = new(); - - public readonly ISet AssertionChecksums = new HashSet(ChecksumComparer.Default); - - public sealed class ChecksumComparer : IEqualityComparer - { - static IEqualityComparer defaultComparer; - - public static IEqualityComparer Default - { - get - { - if (defaultComparer == null) - { - defaultComparer = new ChecksumComparer(); - } - - return defaultComparer; - } - } - - public bool Equals(byte[] x, byte[] y) - { - if (x == null || y == null) - { - return x == y; - } - else - { - return x.SequenceEqual(y); - } - } - - public int GetHashCode(byte[] checksum) - { - if (checksum == null) - { - throw new ArgumentNullException("checksum"); - } - else - { - var result = 17; - for (int i = 0; i < checksum.Length; i++) - { - result = result * 23 + checksum[i]; - } - - return result; - } - } - } - - public void AddAssertionChecksum(byte[] checksum) - { - Contract.Requires(checksum != null); - - if (AssertionChecksums != null) - { - AssertionChecksums.Add(checksum); - } - } - - public ISet AssertionChecksumsInCachedSnapshot { get; set; } - - public bool IsAssertionChecksumInCachedSnapshot(byte[] checksum) - { - Contract.Requires(AssertionChecksumsInCachedSnapshot != null); - - return AssertionChecksumsInCachedSnapshot.Contains(checksum); - } - - public IList RecycledFailingAssertions { get; protected set; } - - public void AddRecycledFailingAssertion(AssertCmd assertion) - { - if (RecycledFailingAssertions == null) - { - RecycledFailingAssertions = new List(); - } - - RecycledFailingAssertions.Add(assertion); - } - - public Cmd ExplicitAssumptionAboutCachedPrecondition { get; set; } - - // Strongly connected components - private StronglyConnectedComponents scc; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(LocVars != null); - Contract.Invariant(cce.NonNullElements(Blocks)); - Contract.Invariant(cce.NonNullElements(OriginalBlocks, true)); - Contract.Invariant(cce.NonNullElements(scc, true)); - } - - private bool BlockPredecessorsComputed; - - public bool StronglyConnectedComponentsComputed - { - get { return this.scc != null; } - } - - public bool IsSkipVerification(CoreOptions options) - { - bool verify = true; - cce.NonNull(this.Proc).CheckBooleanAttribute("verify", ref verify); - this.CheckBooleanAttribute("verify", ref verify); - if (!verify) { - return true; - } - - if (options.ProcedureInlining == CoreOptions.Inlining.Assert || - options.ProcedureInlining == CoreOptions.Inlining.Assume) { - Expr inl = this.FindExprAttribute("inline"); - if (inl == null) { - inl = this.Proc.FindExprAttribute("inline"); - } - - if (inl != null) { - return true; - } - } - - if (options.StratifiedInlining > 0) { - return !QKeyValue.FindBoolAttribute(Attributes, "entrypoint"); - } - - return false; - } - - public string Id - { - get - { - var id = (this as ICarriesAttributes).FindStringAttribute("id"); - if (id == null) - { - id = Name + GetHashCode().ToString() + ":0"; - } - - return id; - } - } - - public int Priority - { - get - { - int priority = 0; - CheckIntAttribute("priority", ref priority); - if (priority <= 0) - { - priority = 1; - } - - return priority; - } - } - - public Dictionary GetExtraSMTOptions() - { - Dictionary extraSMTOpts = new(); - for (var a = Attributes; a != null; a = a.Next) { - var n = a.Params.Count; - var k = a.Key; - if (k.Equals("smt_option")) { - if (n == 2 && a.Params[0] is string s) { - extraSMTOpts.Add(s, a.Params[1].ToString()); - } - } - } - - return extraSMTOpts; - } - - public IDictionary ErrorChecksumToCachedError { get; private set; } - - public bool IsErrorChecksumInCachedSnapshot(byte[] checksum) - { - Contract.Requires(ErrorChecksumToCachedError != null); - - return ErrorChecksumToCachedError.ContainsKey(checksum); - } - - public void SetErrorChecksumToCachedError(IEnumerable> errors) - { - Contract.Requires(errors != null); - - ErrorChecksumToCachedError = new Dictionary(ChecksumComparer.Default); - foreach (var kv in errors) - { - ErrorChecksumToCachedError[kv.Item1] = kv.Item3; - if (kv.Item2 != null) - { - ErrorChecksumToCachedError[kv.Item2] = null; - } - } - } - - public bool HasCachedSnapshot - { - get { return ErrorChecksumToCachedError != null && AssertionChecksumsInCachedSnapshot != null; } - } - - public bool AnyErrorsInCachedSnapshot - { - get - { - Contract.Requires(ErrorChecksumToCachedError != null); - - return ErrorChecksumToCachedError.Any(); - } - } - - IList injectedAssumptionVariables; - - public IList InjectedAssumptionVariables - { - get { return injectedAssumptionVariables != null ? injectedAssumptionVariables : new List(); } - } - - IList doomedInjectedAssumptionVariables; - - public IList DoomedInjectedAssumptionVariables - { - get - { - return doomedInjectedAssumptionVariables != null - ? doomedInjectedAssumptionVariables - : new List(); - } - } - - public List RelevantInjectedAssumptionVariables(Dictionary incarnationMap) - { - return InjectedAssumptionVariables.Where(v => - { - if (incarnationMap.TryGetValue(v, out var e)) - { - var le = e as LiteralExpr; - return le == null || !le.IsTrue; - } - else - { - return false; - } - }).ToList(); - } - - public List RelevantDoomedInjectedAssumptionVariables(Dictionary incarnationMap) - { - return DoomedInjectedAssumptionVariables.Where(v => - { - if (incarnationMap.TryGetValue(v, out var e)) - { - var le = e as LiteralExpr; - return le == null || !le.IsTrue; - } - else - { - return false; - } - }).ToList(); - } - - public Expr ConjunctionOfInjectedAssumptionVariables(Dictionary incarnationMap, out bool isTrue) - { - Contract.Requires(incarnationMap != null); - - var vars = RelevantInjectedAssumptionVariables(incarnationMap).Select(v => incarnationMap[v]).ToList(); - isTrue = vars.Count == 0; - return LiteralExpr.BinaryTreeAnd(vars); - } - - public void InjectAssumptionVariable(LocalVariable variable, bool isDoomed = false) - { - LocVars.Add(variable); - if (isDoomed) - { - if (doomedInjectedAssumptionVariables == null) - { - doomedInjectedAssumptionVariables = new List(); - } - - doomedInjectedAssumptionVariables.Add(variable); - } - else - { - if (injectedAssumptionVariables == null) - { - injectedAssumptionVariables = new List(); - } - - injectedAssumptionVariables.Add(variable); - } - } - - public Implementation(IToken tok, string name, List typeParams, List inParams, - List outParams, List localVariables, [Captured] StmtList structuredStmts, QKeyValue kv) - : this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, kv, new Errors()) - { - Contract.Requires(structuredStmts != null); - Contract.Requires(localVariables != null); - Contract.Requires(outParams != null); - Contract.Requires(inParams != null); - Contract.Requires(typeParams != null); - Contract.Requires(name != null); - Contract.Requires(tok != null); - //:this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, new Errors()); - } - - public Implementation(IToken tok, string name, List typeParams, List inParams, - List outParams, List localVariables, [Captured] StmtList structuredStmts) - : this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, new Errors()) - { - Contract.Requires(structuredStmts != null); - Contract.Requires(localVariables != null); - Contract.Requires(outParams != null); - Contract.Requires(inParams != null); - Contract.Requires(typeParams != null); - Contract.Requires(name != null); - Contract.Requires(tok != null); - //:this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, new Errors()); - } - - public Implementation(IToken tok, string name, List typeParams, List inParams, - List outParams, List localVariables, [Captured] StmtList structuredStmts, Errors errorHandler) - : this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, errorHandler) - { - Contract.Requires(errorHandler != null); - Contract.Requires(structuredStmts != null); - Contract.Requires(localVariables != null); - Contract.Requires(outParams != null); - Contract.Requires(inParams != null); - Contract.Requires(typeParams != null); - Contract.Requires(name != null); - Contract.Requires(tok != null); - //:this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, errorHandler); - } - - public Implementation(IToken /*!*/ tok, - string /*!*/ name, - List /*!*/ typeParams, - List /*!*/ inParams, - List /*!*/ outParams, - List /*!*/ localVariables, - [Captured] StmtList /*!*/ structuredStmts, - QKeyValue kv, - Errors /*!*/ errorHandler) - : base(tok, name, typeParams, inParams, outParams) - { - Contract.Requires(tok != null); - Contract.Requires(name != null); - Contract.Requires(typeParams != null); - Contract.Requires(inParams != null); - Contract.Requires(outParams != null); - Contract.Requires(localVariables != null); - Contract.Requires(structuredStmts != null); - Contract.Requires(errorHandler != null); - LocVars = localVariables; - StructuredStmts = structuredStmts; - BigBlocksResolutionContext ctx = new BigBlocksResolutionContext(structuredStmts, errorHandler); - Blocks = ctx.Blocks; - BlockPredecessorsComputed = false; - scc = null; - Attributes = kv; - } - - public Implementation(IToken tok, string name, List typeParams, List inParams, - List outParams, List localVariables, [Captured] List block) - : this(tok, name, typeParams, inParams, outParams, localVariables, block, null) - { - Contract.Requires(cce.NonNullElements(block)); - Contract.Requires(localVariables != null); - Contract.Requires(outParams != null); - Contract.Requires(inParams != null); - Contract.Requires(typeParams != null); - Contract.Requires(name != null); - Contract.Requires(tok != null); - //:this(tok, name, typeParams, inParams, outParams, localVariables, block, null); - } - - public Implementation(IToken /*!*/ tok, - string /*!*/ name, - List /*!*/ typeParams, - List /*!*/ inParams, - List /*!*/ outParams, - List /*!*/ localVariables, - [Captured] List /*!*/ blocks, - QKeyValue kv) - : base(tok, name, typeParams, inParams, outParams) - { - Contract.Requires(name != null); - Contract.Requires(inParams != null); - Contract.Requires(outParams != null); - Contract.Requires(localVariables != null); - Contract.Requires(cce.NonNullElements(blocks)); - LocVars = localVariables; - Blocks = blocks; - BlockPredecessorsComputed = false; - scc = null; - Attributes = kv; - } - - public override void Emit(TokenTextWriter stream, int level) - { - stream.Write(this, level, "implementation "); - EmitAttributes(stream); - stream.Write(this, level, "{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); - EmitSignature(stream, false); - stream.WriteLine(); - - stream.WriteLine(level, "{0}", '{'); - - foreach (Variable /*!*/ v in this.LocVars) - { - Contract.Assert(v != null); - v.Emit(stream, level + 1); - } - - if (this.StructuredStmts != null && !stream.Options.PrintInstrumented && - !stream.Options.PrintInlined) - { - if (this.LocVars.Count > 0) - { - stream.WriteLine(); - } - - if (stream.Options.PrintUnstructured < 2) - { - if (stream.Options.PrintUnstructured == 1) - { - stream.WriteLine(this, level + 1, "/*** structured program:"); - } - - this.StructuredStmts.Emit(stream, level + 1); - if (stream.Options.PrintUnstructured == 1) - { - stream.WriteLine(level + 1, "**** end structured program */"); - } - } - } - - if (this.StructuredStmts == null || 1 <= stream.Options.PrintUnstructured || - stream.Options.PrintInstrumented || stream.Options.PrintInlined) - { - foreach (Block b in this.Blocks) - { - b.Emit(stream, level + 1); - } - } - - stream.WriteLine(level, "{0}", '}'); - - stream.WriteLine(); - stream.WriteLine(); - } - - public override void Register(ResolutionContext rc) - { - // nothing to register - } - - public override void Resolve(ResolutionContext rc) - { - if (Proc != null) - { - // already resolved - return; - } - - Proc = rc.LookUpProcedure(cce.NonNull(this.Name)); - if (Proc == null) - { - rc.Error(this, "implementation given for undeclared procedure: {0}", this.Name); - } - else if (Proc is ActionDecl actionDecl) - { - actionDecl.Impl = this; - } - - int previousTypeBinderState = rc.TypeBinderState; - try - { - RegisterTypeParameters(rc); - - rc.PushVarContext(); - rc.Proc = Proc; - RegisterFormals(InParams, rc); - RegisterFormals(OutParams, rc); - foreach (Variable v in LocVars) - { - Contract.Assert(v != null); - v.Register(rc); - v.Resolve(rc); - } - rc.Proc = null; - - foreach (Variable v in LocVars) - { - Contract.Assert(v != null); - v.ResolveWhere(rc); - } - - rc.PushProcedureContext(); - foreach (Block b in Blocks) - { - b.Register(rc); - } - - (this as ICarriesAttributes).ResolveAttributes(rc); - - rc.Proc = Proc; - rc.StateMode = Proc.IsPure ? ResolutionContext.State.StateLess : ResolutionContext.State.Two; - foreach (Block b in Blocks) - { - b.Resolve(rc); - } - rc.Proc = null; - rc.StateMode = ResolutionContext.State.Single; - - rc.PopProcedureContext(); - rc.PopVarContext(); - - Type.CheckBoundVariableOccurrences(TypeParameters, - InParams.Select(Item => Item.TypedIdent.Type).ToList(), - OutParams.Select(Item => Item.TypedIdent.Type).ToList(), - this.tok, "implementation arguments", - rc); - } - finally - { - rc.TypeBinderState = previousTypeBinderState; - } - - SortTypeParams(); - } - - public override void Typecheck(TypecheckingContext tc) - { - //Contract.Requires(tc != null); - base.Typecheck(tc); - - Contract.Assume(this.Proc != null); - - if (this.TypeParameters.Count != Proc.TypeParameters.Count) - { - tc.Error(this, "mismatched number of type parameters in procedure implementation: {0}", - this.Name); - } - else - { - // if the numbers of type parameters are different, it is - // difficult to compare the argument types - MatchFormals(this.InParams, Proc.InParams, "in", tc); - MatchFormals(this.OutParams, Proc.OutParams, "out", tc); - } - - var oldProc = tc.Proc; - tc.Proc = Proc; - tc.Impl = this; - foreach (Variable /*!*/ v in LocVars) - { - Contract.Assert(v != null); - v.Typecheck(tc); - } - foreach (Block b in Blocks) - { - b.Typecheck(tc); - } - Contract.Assert(tc.Proc == Proc); - tc.Impl = null; - tc.Proc = oldProc; - - if (Proc is ActionDecl || Proc is YieldProcedureDecl) - { - var graph = Program.GraphFromImpl(this); - if (!Graph.Acyclic(graph)) - { - if (Proc is ActionDecl) - { - tc.Error(this, "action implementation may not have loops"); - } - else // Proc is YieldProcedureDecl - { - graph.ComputeLoops(); - if (!graph.Reducible) - { - tc.Error(this, "irreducible control flow graph not allowed"); - } - else - { - TypecheckLoopAnnotations(tc, graph); - } - } - } - } - } - - private void TypecheckLoopAnnotations(TypecheckingContext tc, Graph graph) - { - var yieldingProc = (YieldProcedureDecl)Proc; - foreach (var header in graph.Headers) - { - var yieldingLayer = yieldingProc.Layer; - var yieldCmd = (PredicateCmd)header.Cmds.FirstOrDefault(cmd => - cmd is PredicateCmd predCmd && predCmd.HasAttribute(CivlAttributes.YIELDS)); - if (yieldCmd == null) - { - yieldingLayer = int.MinValue; - } - else - { - var layers = yieldCmd.Layers; - header.Cmds.Remove(yieldCmd); - if (layers.Any()) - { - if (layers.Count > 1) - { - tc.Error(header, "expected layer attribute to indicate the highest yielding layer of this loop"); - continue; - } - if (layers[0] > yieldingLayer) - { - tc.Error(header, - "yielding layer of loop must not be more than the layer of enclosing procedure"); - continue; - } - yieldingLayer = layers[0]; - } - } - - var yieldInvariants = header.Cmds - .TakeWhile(cmd => cmd is CallCmd { Proc: YieldInvariantDecl }).OfType() - .ToList(); - header.Cmds.RemoveRange(0, yieldInvariants.Count); - if (yieldInvariants.Any() && yieldCmd == null) - { - tc.Error(header, "expected :yields attribute on this loop"); - } - foreach (var callCmd in yieldInvariants) - { - var yieldInvariant = (YieldInvariantDecl)callCmd.Proc; - var calleeLayerNum = yieldInvariant.Layer; - if (calleeLayerNum > yieldingLayer) - { - tc.Error(callCmd, $"loop must yield at layer {calleeLayerNum} of the called yield invariant"); - } - } - foreach (var predCmd in header.Cmds.TakeWhile(cmd => cmd is PredicateCmd).OfType()) - { - if (predCmd.Layers.Min() <= yieldingLayer && - VariableCollector.Collect(predCmd, true).OfType().Any()) - { - tc.Error(predCmd, - "invariant may not access a global variable since one of its layers is a yielding layer of its loop"); - } - } - - yieldingProc.YieldingLoops[header] = new YieldingLoop(yieldingLayer, yieldInvariants); - } - } - - void MatchFormals(List /*!*/ implFormals, List /*!*/ procFormals, string /*!*/ inout, - TypecheckingContext /*!*/ tc) - { - Contract.Requires(implFormals != null); - Contract.Requires(procFormals != null); - Contract.Requires(inout != null); - Contract.Requires(tc != null); - if (implFormals.Count != procFormals.Count) - { - tc.Error(this, "mismatched number of {0}-parameters in procedure implementation: {1}", - inout, this.Name); - } - else - { - // unify the type parameters so that types can be compared - Contract.Assert(Proc != null); - Contract.Assert(this.TypeParameters.Count == Proc.TypeParameters.Count); - - IDictionary /*!*/ - subst1 = - new Dictionary(); - IDictionary /*!*/ - subst2 = - new Dictionary(); - - for (int i = 0; i < this.TypeParameters.Count; ++i) - { - TypeVariable /*!*/ - newVar = - new TypeVariable(Token.NoToken, Proc.TypeParameters[i].Name); - Contract.Assert(newVar != null); - subst1.Add(Proc.TypeParameters[i], newVar); - subst2.Add(this.TypeParameters[i], newVar); - } - - for (int i = 0; i < implFormals.Count; i++) - { - // the names of the formals are allowed to change from the proc to the impl - - // but types must be identical - Type t = cce.NonNull((Variable) implFormals[i]).TypedIdent.Type.Substitute(subst2); - Type u = cce.NonNull((Variable) procFormals[i]).TypedIdent.Type.Substitute(subst1); - if (!t.Equals(u)) - { - string /*!*/ - a = cce.NonNull((Variable) implFormals[i]).Name; - Contract.Assert(a != null); - string /*!*/ - b = cce.NonNull((Variable) procFormals[i]).Name; - Contract.Assert(b != null); - string /*!*/ - c; - if (a == b) - { - c = a; - } - else - { - c = String.Format("{0} (named {1} in implementation)", b, a); - } - - tc.Error(this, "mismatched type of {0}-parameter in implementation {1}: {2}", inout, this.Name, c); - } - } - } - } - - private Dictionary /*?*/ - formalMap = null; - - public void ResetImplFormalMap() - { - this.formalMap = null; - } - - public Dictionary /*!*/ GetImplFormalMap(CoreOptions options) - { - Contract.Ensures(Contract.Result>() != null); - - if (this.formalMap != null) - { - return this.formalMap; - } - else - { - Dictionary /*!*/ - map = new Dictionary(InParams.Count + OutParams.Count); - - Contract.Assume(this.Proc != null); - Contract.Assume(InParams.Count == Proc.InParams.Count); - for (int i = 0; i < InParams.Count; i++) - { - Variable /*!*/ - v = InParams[i]; - Contract.Assert(v != null); - IdentifierExpr ie = new IdentifierExpr(v.tok, v); - Variable /*!*/ - pv = Proc.InParams[i]; - Contract.Assert(pv != null); - map.Add(pv, ie); - } - - System.Diagnostics.Debug.Assert(OutParams.Count == Proc.OutParams.Count); - for (int i = 0; i < OutParams.Count; i++) - { - Variable /*!*/ - v = cce.NonNull(OutParams[i]); - IdentifierExpr ie = new IdentifierExpr(v.tok, v); - Variable pv = cce.NonNull(Proc.OutParams[i]); - map.Add(pv, ie); - } - - this.formalMap = map; - - if (options.PrintWithUniqueASTIds) - { - options.OutputWriter.WriteLine("Implementation.GetImplFormalMap on {0}:", this.Name); - using TokenTextWriter stream = - new TokenTextWriter("", options.OutputWriter, /*setTokens=*/false, /*pretty=*/ false, options); - foreach (var e in map) - { - options.OutputWriter.Write(" "); - cce.NonNull((Variable /*!*/) e.Key).Emit(stream, 0); - options.OutputWriter.Write(" --> "); - cce.NonNull((Expr) e.Value).Emit(stream); - options.OutputWriter.WriteLine(); - } - } - - return map; - } - } - - /// - /// Return a collection of blocks that are reachable from the block passed as a parameter. - /// The block must be defined in the current implementation - /// - public ICollection GetConnectedComponents(Block startingBlock) - { - Contract.Requires(startingBlock != null); - Contract.Ensures(cce.NonNullElements(Contract.Result>(), true)); - Contract.Assert(this.Blocks.Contains(startingBlock)); - - if (!this.BlockPredecessorsComputed) - { - ComputeStronglyConnectedComponents(); - } - -#if DEBUG_PRINT - System.Console.WriteLine("* Strongly connected components * \n{0} \n ** ", scc); -#endif - - foreach (ICollection component in cce.NonNull(this.scc)) - { - foreach (Block /*!*/ b in component) - { - Contract.Assert(b != null); - if (b == startingBlock) // We found the compontent that owns the startingblock - { - return component; - } - } - } - - { - Contract.Assert(false); - throw new cce.UnreachableException(); - } // if we are here, it means that the block is not in one of the components. This is an error. - } - - /// - /// Compute the strongly connected compontents of the blocks in the implementation. - /// As a side effect, it also computes the "predecessor" relation for the block in the implementation - /// - override public void ComputeStronglyConnectedComponents() - { - if (!this.BlockPredecessorsComputed) - { - ComputePredecessorsForBlocks(); - } - - Adjacency next = new Adjacency(Successors); - Adjacency prev = new Adjacency(Predecessors); - - this.scc = new StronglyConnectedComponents(this.Blocks, next, prev); - scc.Compute(); - - - foreach (Block /*!*/ block in this.Blocks) - { - Contract.Assert(block != null); - block.Predecessors = new List(); - } - } - - /// - /// Reset the abstract stated computed before - /// - override public void ResetAbstractInterpretationState() - { - foreach (Block /*!*/ b in this.Blocks) - { - Contract.Assert(b != null); - b.ResetAbstractInterpretationState(); - } - } - - /// - /// A private method used as delegate for the strongly connected components. - /// It return, given a node, the set of its successors - /// - private IEnumerable /**/ /*!*/ Successors(Block node) - { - Contract.Requires(node != null); - Contract.Ensures(Contract.Result() != null); - - GotoCmd gotoCmd = node.TransferCmd as GotoCmd; - - if (gotoCmd != null) - { - // If it is a gotoCmd - Contract.Assert(gotoCmd.labelTargets != null); - - return gotoCmd.labelTargets; - } - else - { - // otherwise must be a ReturnCmd - Contract.Assert(node.TransferCmd is ReturnCmd); - - return new List(); - } - } - - /// - /// A private method used as delegate for the strongly connected components. - /// It return, given a node, the set of its predecessors - /// - private IEnumerable /**/ /*!*/ Predecessors(Block node) - { - Contract.Requires(node != null); - Contract.Ensures(Contract.Result() != null); - - Contract.Assert(this.BlockPredecessorsComputed); - - return node.Predecessors; - } - - /// - /// Compute the predecessor informations for the blocks - /// - public void ComputePredecessorsForBlocks() - { - var blocks = this.Blocks; - foreach (Block b in blocks) - { - b.Predecessors = new List(); - } - - ComputePredecessorsForBlocks(blocks); - - this.BlockPredecessorsComputed = true; - } - - public static void ComputePredecessorsForBlocks(List blocks) - { - foreach (var block in blocks) { - if (block.TransferCmd is not GotoCmd gtc) { - continue; - } - - Contract.Assert(gtc.labelTargets != null); - foreach (var /*!*/ dest in gtc.labelTargets) - { - Contract.Assert(dest != null); - dest.Predecessors.Add(block); - } - } - } - - public void PruneUnreachableBlocks(CoreOptions options) - { - var toVisit = new Stack(); - var reachableBlocks = new List(); - var reachable = new HashSet(); // the set of elements in "reachableBlocks" - - toVisit.Push(Blocks[0]); - while (toVisit.Count != 0) - { - var block = toVisit.Pop(); - if (!reachable.Add(block)) { - continue; - } - - reachableBlocks.Add(block); - if (block.TransferCmd is GotoCmd gotoCmd) { - if (options.PruneInfeasibleEdges) { - foreach (var command in block.Cmds) { - Contract.Assert(command != null); - if (command is PredicateCmd { Expr: LiteralExpr { IsFalse: true } }) { - // This statement sequence will never reach the end, because of this "assume false" or "assert false". - // Hence, it does not reach its successors. - block.TransferCmd = new ReturnCmd(block.TransferCmd.tok); - goto NEXT_BLOCK; - } - } - } - - // it seems that the goto statement at the end may be reached - foreach (var next in gotoCmd.labelTargets) { - Contract.Assume(next != null); - toVisit.Push(next); - } - } - - NEXT_BLOCK: - { - } - } - - this.Blocks = reachableBlocks; - } - - public override Absy StdDispatch(StandardVisitor visitor) - { - //Contract.Requires(visitor != null); - Contract.Ensures(Contract.Result() != null); - return visitor.VisitImplementation(this); - } - - public void FreshenCaptureStates() - { - // Assume commands with the "captureState" attribute allow model states to be - // captured for error reporting. - // Some program transformations, such as loop unrolling, duplicate parts of the - // program, leading to "capture-state-assumes" being duplicated. This leads - // to ambiguity when getting a state from the model. - // This method replaces the key of every "captureState" attribute with something - // unique - - int FreshCounter = 0; - foreach (var b in Blocks) - { - List newCmds = new List(); - for (int i = 0; i < b.Cmds.Count(); i++) - { - var a = b.Cmds[i] as AssumeCmd; - if (a != null && (QKeyValue.FindStringAttribute(a.Attributes, "captureState") != null)) - { - string StateName = QKeyValue.FindStringAttribute(a.Attributes, "captureState"); - newCmds.Add(new AssumeCmd(Token.NoToken, a.Expr, FreshenCaptureState(a.Attributes, FreshCounter))); - FreshCounter++; - } - else - { - newCmds.Add(b.Cmds[i]); - } - } - - b.Cmds = newCmds; - } - } - - private QKeyValue FreshenCaptureState(QKeyValue Attributes, int FreshCounter) - { - // Returns attributes identical to Attributes, but: - // - reversed (for ease of implementation; should not matter) - // - with the value for "captureState" replaced by a fresh value - Contract.Requires(QKeyValue.FindStringAttribute(Attributes, "captureState") != null); - string FreshValue = QKeyValue.FindStringAttribute(Attributes, "captureState") + "$renamed$" + Name + "$" + - FreshCounter; - - QKeyValue result = null; - while (Attributes != null) - { - if (Attributes.Key.Equals("captureState")) - { - result = new QKeyValue(Token.NoToken, Attributes.Key, new List() {FreshValue}, result); - } - else - { - result = new QKeyValue(Token.NoToken, Attributes.Key, Attributes.Params, result); - } - - Attributes = Attributes.Next; - } - - return result; - } - } - public class TypedIdent : Absy { diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 76123cb38..3583e68b5 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -1189,225 +1189,6 @@ public override void Emit(TokenTextWriter stream, int level) //--------------------------------------------------------------------- // Block - public sealed class Block : Absy - { - private string /*!*/ label; // Note, Label is mostly readonly, but it can change to the name of a nearby block during block coalescing and empty-block removal - - public string /*!*/ Label - { - get - { - Contract.Ensures(Contract.Result() != null); - return this.label; - } - set - { - Contract.Requires(value != null); - this.label = value; - } - } - - [Rep] [ElementsPeer] public List /*!*/ cmds; - - public List /*!*/ Cmds - { - get - { - Contract.Ensures(Contract.Result>() != null); - return this.cmds; - } - set - { - Contract.Requires(value != null); - this.cmds = value; - } - } - - public IEnumerable Exits() - { - if (TransferCmd is GotoCmd g) - { - return cce.NonNull(g.labelTargets); - } - return new List(); - } - - [Rep] //PM: needed to verify Traverse.Visit - public TransferCmd - TransferCmd; // maybe null only because we allow deferred initialization (necessary for cyclic structures) - - public byte[] Checksum; - - // Abstract interpretation - - // public bool currentlyTraversed; - - public enum VisitState - { - ToVisit, - BeingVisited, - AlreadyVisited - } // used by WidenPoints.Compute - - public VisitState TraversingStatus; - - public int aiId; // block ID used by the abstract interpreter, which may change these numbers with each AI run - public bool widenBlock; - - public int - iterations; // Count the number of time we visited the block during fixpoint computation. Used to decide if we widen or not - - // VC generation and SCC computation - public List /*!*/ Predecessors; - - // This field is used during passification to null-out entries in block2Incarnation dictionary early - public int succCount; - - private HashSet _liveVarsBefore; - - public IEnumerable liveVarsBefore - { - get - { - Contract.Ensures(cce.NonNullElements(Contract.Result>(), true)); - if (this._liveVarsBefore == null) - { - return null; - } - else - { - return this._liveVarsBefore.AsEnumerable(); - } - } - set - { - Contract.Requires(cce.NonNullElements(value, true)); - if (value == null) - { - this._liveVarsBefore = null; - } - else - { - this._liveVarsBefore = new HashSet(value); - } - } - } - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(this.label != null); - Contract.Invariant(this.cmds != null); - Contract.Invariant(cce.NonNullElements(this._liveVarsBefore, true)); - } - - public bool IsLive(Variable v) - { - Contract.Requires(v != null); - if (liveVarsBefore == null) - { - return true; - } - - return liveVarsBefore.Contains(v); - } - - public Block() - : this(Token.NoToken, "", new List(), new ReturnCmd(Token.NoToken)) - { - } - - public Block(IToken tok, string /*!*/ label, List /*!*/ cmds, TransferCmd transferCmd) - : base(tok) - { - Contract.Requires(label != null); - Contract.Requires(cmds != null); - Contract.Requires(tok != null); - this.label = label; - this.cmds = cmds; - this.TransferCmd = transferCmd; - this.Predecessors = new List(); - this._liveVarsBefore = null; - this.TraversingStatus = VisitState.ToVisit; - this.iterations = 0; - } - - public void Emit(TokenTextWriter stream, int level) - { - Contract.Requires(stream != null); - stream.WriteLine(); - stream.WriteLine( - this, - level, - "{0}:{1}", - stream.Options.PrintWithUniqueASTIds - ? String.Format("h{0}^^{1}", this.GetHashCode(), this.Label) - : this.Label, - this.widenBlock ? " // cut point" : ""); - - foreach (Cmd /*!*/ c in this.Cmds) - { - Contract.Assert(c != null); - c.Emit(stream, level + 1); - } - - Contract.Assume(this.TransferCmd != null); - this.TransferCmd.Emit(stream, level + 1); - } - - public void Register(ResolutionContext rc) - { - Contract.Requires(rc != null); - rc.AddBlock(this); - } - - public override void Resolve(ResolutionContext rc) - { - foreach (Cmd /*!*/ c in Cmds) - { - Contract.Assert(c != null); - c.Resolve(rc); - } - - Contract.Assume(this.TransferCmd != null); - TransferCmd.Resolve(rc); - } - - public override void Typecheck(TypecheckingContext tc) - { - foreach (Cmd /*!*/ c in Cmds) - { - Contract.Assert(c != null); - c.Typecheck(tc); - } - - Contract.Assume(this.TransferCmd != null); - TransferCmd.Typecheck(tc); - } - - /// - /// Reset the abstract intepretation state of this block. It does this by putting the iterations to 0 and the pre and post states to null - /// - public void ResetAbstractInterpretationState() - { - // this.currentlyTraversed = false; - this.TraversingStatus = VisitState.ToVisit; - this.iterations = 0; - } - - [Pure] - public override string ToString() - { - Contract.Ensures(Contract.Result() != null); - return this.Label + (this.widenBlock ? "[w]" : ""); - } - - public override Absy StdDispatch(StandardVisitor visitor) - { - Contract.Ensures(Contract.Result() != null); - return visitor.VisitBlock(this); - } - } //--------------------------------------------------------------------- // Commands diff --git a/Source/Core/AST/Block.cs b/Source/Core/AST/Block.cs new file mode 100644 index 000000000..3e4aa3277 --- /dev/null +++ b/Source/Core/AST/Block.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; + +namespace Microsoft.Boogie; + +public sealed class Block : Absy +{ + private string /*!*/ label; // Note, Label is mostly readonly, but it can change to the name of a nearby block during block coalescing and empty-block removal + + public string /*!*/ Label + { + get + { + Contract.Ensures(Contract.Result() != null); + return this.label; + } + set + { + Contract.Requires(value != null); + this.label = value; + } + } + + [Rep] [ElementsPeer] public List /*!*/ cmds; + + public List /*!*/ Cmds + { + get + { + Contract.Ensures(Contract.Result>() != null); + return this.cmds; + } + set + { + Contract.Requires(value != null); + this.cmds = value; + } + } + + public IEnumerable Exits() + { + if (TransferCmd is GotoCmd g) + { + return cce.NonNull(g.labelTargets); + } + return new List(); + } + + [Rep] //PM: needed to verify Traverse.Visit + public TransferCmd + TransferCmd; // maybe null only because we allow deferred initialization (necessary for cyclic structures) + + public byte[] Checksum; + + // Abstract interpretation + + // public bool currentlyTraversed; + + public enum VisitState + { + ToVisit, + BeingVisited, + AlreadyVisited + } // used by WidenPoints.Compute + + public VisitState TraversingStatus; + + public int aiId; // block ID used by the abstract interpreter, which may change these numbers with each AI run + public bool widenBlock; + + public int + iterations; // Count the number of time we visited the block during fixpoint computation. Used to decide if we widen or not + + // VC generation and SCC computation + public List /*!*/ Predecessors; + + // This field is used during passification to null-out entries in block2Incarnation dictionary early + public int succCount; + + private HashSet _liveVarsBefore; + + public IEnumerable liveVarsBefore + { + get + { + Contract.Ensures(cce.NonNullElements(Contract.Result>(), true)); + if (this._liveVarsBefore == null) + { + return null; + } + else + { + return this._liveVarsBefore.AsEnumerable(); + } + } + set + { + Contract.Requires(cce.NonNullElements(value, true)); + if (value == null) + { + this._liveVarsBefore = null; + } + else + { + this._liveVarsBefore = new HashSet(value); + } + } + } + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(this.label != null); + Contract.Invariant(this.cmds != null); + Contract.Invariant(cce.NonNullElements(this._liveVarsBefore, true)); + } + + public bool IsLive(Variable v) + { + Contract.Requires(v != null); + if (liveVarsBefore == null) + { + return true; + } + + return liveVarsBefore.Contains(v); + } + + public Block() + : this(Token.NoToken, "", new List(), new ReturnCmd(Token.NoToken)) + { + } + + public Block(IToken tok, string /*!*/ label, List /*!*/ cmds, TransferCmd transferCmd) + : base(tok) + { + Contract.Requires(label != null); + Contract.Requires(cmds != null); + Contract.Requires(tok != null); + this.label = label; + this.cmds = cmds; + this.TransferCmd = transferCmd; + this.Predecessors = new List(); + this._liveVarsBefore = null; + this.TraversingStatus = VisitState.ToVisit; + this.iterations = 0; + } + + public void Emit(TokenTextWriter stream, int level) + { + Contract.Requires(stream != null); + stream.WriteLine(); + stream.WriteLine( + this, + level, + "{0}:{1}", + stream.Options.PrintWithUniqueASTIds + ? String.Format("h{0}^^{1}", this.GetHashCode(), this.Label) + : this.Label, + this.widenBlock ? " // cut point" : ""); + + foreach (Cmd /*!*/ c in this.Cmds) + { + Contract.Assert(c != null); + c.Emit(stream, level + 1); + } + + Contract.Assume(this.TransferCmd != null); + this.TransferCmd.Emit(stream, level + 1); + } + + public void Register(ResolutionContext rc) + { + Contract.Requires(rc != null); + rc.AddBlock(this); + } + + public override void Resolve(ResolutionContext rc) + { + foreach (Cmd /*!*/ c in Cmds) + { + Contract.Assert(c != null); + c.Resolve(rc); + } + + Contract.Assume(this.TransferCmd != null); + TransferCmd.Resolve(rc); + } + + public override void Typecheck(TypecheckingContext tc) + { + foreach (Cmd /*!*/ c in Cmds) + { + Contract.Assert(c != null); + c.Typecheck(tc); + } + + Contract.Assume(this.TransferCmd != null); + TransferCmd.Typecheck(tc); + } + + /// + /// Reset the abstract intepretation state of this block. It does this by putting the iterations to 0 and the pre and post states to null + /// + public void ResetAbstractInterpretationState() + { + // this.currentlyTraversed = false; + this.TraversingStatus = VisitState.ToVisit; + this.iterations = 0; + } + + [Pure] + public override string ToString() + { + Contract.Ensures(Contract.Result() != null); + return this.Label + (this.widenBlock ? "[w]" : ""); + } + + public override Absy StdDispatch(StandardVisitor visitor) + { + Contract.Ensures(Contract.Result() != null); + return visitor.VisitBlock(this); + } +} \ No newline at end of file diff --git a/Source/Core/AST/Implementation.cs b/Source/Core/AST/Implementation.cs new file mode 100644 index 000000000..384e5d227 --- /dev/null +++ b/Source/Core/AST/Implementation.cs @@ -0,0 +1,1082 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using Microsoft.Boogie.GraphUtil; + +namespace Microsoft.Boogie; + +public class Implementation : DeclWithFormals { + public List LocVars; + + [Rep] public StmtList StructuredStmts; + + [field: Rep] + public List Blocks { + get; + set; + } + public Procedure Proc; + + // Blocks before applying passification etc. + // Both are used only when /inline is set. + public List OriginalBlocks; + public List OriginalLocVars; + + // Map filled in during passification to allow augmented error trace reporting + public Dictionary> debugInfos = new(); + + public readonly ISet AssertionChecksums = new HashSet(ChecksumComparer.Default); + + public sealed class ChecksumComparer : IEqualityComparer + { + static IEqualityComparer defaultComparer; + + public static IEqualityComparer Default + { + get + { + if (defaultComparer == null) + { + defaultComparer = new ChecksumComparer(); + } + + return defaultComparer; + } + } + + public bool Equals(byte[] x, byte[] y) + { + if (x == null || y == null) + { + return x == y; + } + else + { + return x.SequenceEqual(y); + } + } + + public int GetHashCode(byte[] checksum) + { + if (checksum == null) + { + throw new ArgumentNullException("checksum"); + } + else + { + var result = 17; + for (int i = 0; i < checksum.Length; i++) + { + result = result * 23 + checksum[i]; + } + + return result; + } + } + } + + public void AddAssertionChecksum(byte[] checksum) + { + Contract.Requires(checksum != null); + + if (AssertionChecksums != null) + { + AssertionChecksums.Add(checksum); + } + } + + public ISet AssertionChecksumsInCachedSnapshot { get; set; } + + public bool IsAssertionChecksumInCachedSnapshot(byte[] checksum) + { + Contract.Requires(AssertionChecksumsInCachedSnapshot != null); + + return AssertionChecksumsInCachedSnapshot.Contains(checksum); + } + + public IList RecycledFailingAssertions { get; protected set; } + + public void AddRecycledFailingAssertion(AssertCmd assertion) + { + if (RecycledFailingAssertions == null) + { + RecycledFailingAssertions = new List(); + } + + RecycledFailingAssertions.Add(assertion); + } + + public Cmd ExplicitAssumptionAboutCachedPrecondition { get; set; } + + // Strongly connected components + private StronglyConnectedComponents scc; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(LocVars != null); + Contract.Invariant(cce.NonNullElements(Blocks)); + Contract.Invariant(cce.NonNullElements(OriginalBlocks, true)); + Contract.Invariant(cce.NonNullElements(scc, true)); + } + + private bool BlockPredecessorsComputed; + + public bool StronglyConnectedComponentsComputed + { + get { return this.scc != null; } + } + + public bool IsSkipVerification(CoreOptions options) + { + bool verify = true; + cce.NonNull(this.Proc).CheckBooleanAttribute("verify", ref verify); + this.CheckBooleanAttribute("verify", ref verify); + if (!verify) { + return true; + } + + if (options.ProcedureInlining == CoreOptions.Inlining.Assert || + options.ProcedureInlining == CoreOptions.Inlining.Assume) { + Expr inl = this.FindExprAttribute("inline"); + if (inl == null) { + inl = this.Proc.FindExprAttribute("inline"); + } + + if (inl != null) { + return true; + } + } + + if (options.StratifiedInlining > 0) { + return !QKeyValue.FindBoolAttribute(Attributes, "entrypoint"); + } + + return false; + } + + public string Id + { + get + { + var id = (this as ICarriesAttributes).FindStringAttribute("id"); + if (id == null) + { + id = Name + GetHashCode().ToString() + ":0"; + } + + return id; + } + } + + public int Priority + { + get + { + int priority = 0; + CheckIntAttribute("priority", ref priority); + if (priority <= 0) + { + priority = 1; + } + + return priority; + } + } + + public Dictionary GetExtraSMTOptions() + { + Dictionary extraSMTOpts = new(); + for (var a = Attributes; a != null; a = a.Next) { + var n = a.Params.Count; + var k = a.Key; + if (k.Equals("smt_option")) { + if (n == 2 && a.Params[0] is string s) { + extraSMTOpts.Add(s, a.Params[1].ToString()); + } + } + } + + return extraSMTOpts; + } + + public IDictionary ErrorChecksumToCachedError { get; private set; } + + public bool IsErrorChecksumInCachedSnapshot(byte[] checksum) + { + Contract.Requires(ErrorChecksumToCachedError != null); + + return ErrorChecksumToCachedError.ContainsKey(checksum); + } + + public void SetErrorChecksumToCachedError(IEnumerable> errors) + { + Contract.Requires(errors != null); + + ErrorChecksumToCachedError = new Dictionary(ChecksumComparer.Default); + foreach (var kv in errors) + { + ErrorChecksumToCachedError[kv.Item1] = kv.Item3; + if (kv.Item2 != null) + { + ErrorChecksumToCachedError[kv.Item2] = null; + } + } + } + + public bool HasCachedSnapshot + { + get { return ErrorChecksumToCachedError != null && AssertionChecksumsInCachedSnapshot != null; } + } + + public bool AnyErrorsInCachedSnapshot + { + get + { + Contract.Requires(ErrorChecksumToCachedError != null); + + return ErrorChecksumToCachedError.Any(); + } + } + + IList injectedAssumptionVariables; + + public IList InjectedAssumptionVariables + { + get { return injectedAssumptionVariables != null ? injectedAssumptionVariables : new List(); } + } + + IList doomedInjectedAssumptionVariables; + + public IList DoomedInjectedAssumptionVariables + { + get + { + return doomedInjectedAssumptionVariables != null + ? doomedInjectedAssumptionVariables + : new List(); + } + } + + public List RelevantInjectedAssumptionVariables(Dictionary incarnationMap) + { + return InjectedAssumptionVariables.Where(v => + { + if (incarnationMap.TryGetValue(v, out var e)) + { + var le = e as LiteralExpr; + return le == null || !le.IsTrue; + } + else + { + return false; + } + }).ToList(); + } + + public List RelevantDoomedInjectedAssumptionVariables(Dictionary incarnationMap) + { + return DoomedInjectedAssumptionVariables.Where(v => + { + if (incarnationMap.TryGetValue(v, out var e)) + { + var le = e as LiteralExpr; + return le == null || !le.IsTrue; + } + else + { + return false; + } + }).ToList(); + } + + public Expr ConjunctionOfInjectedAssumptionVariables(Dictionary incarnationMap, out bool isTrue) + { + Contract.Requires(incarnationMap != null); + + var vars = RelevantInjectedAssumptionVariables(incarnationMap).Select(v => incarnationMap[v]).ToList(); + isTrue = vars.Count == 0; + return LiteralExpr.BinaryTreeAnd(vars); + } + + public void InjectAssumptionVariable(LocalVariable variable, bool isDoomed = false) + { + LocVars.Add(variable); + if (isDoomed) + { + if (doomedInjectedAssumptionVariables == null) + { + doomedInjectedAssumptionVariables = new List(); + } + + doomedInjectedAssumptionVariables.Add(variable); + } + else + { + if (injectedAssumptionVariables == null) + { + injectedAssumptionVariables = new List(); + } + + injectedAssumptionVariables.Add(variable); + } + } + + public Implementation(IToken tok, string name, List typeParams, List inParams, + List outParams, List localVariables, [Captured] StmtList structuredStmts, QKeyValue kv) + : this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, kv, new Errors()) + { + Contract.Requires(structuredStmts != null); + Contract.Requires(localVariables != null); + Contract.Requires(outParams != null); + Contract.Requires(inParams != null); + Contract.Requires(typeParams != null); + Contract.Requires(name != null); + Contract.Requires(tok != null); + //:this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, new Errors()); + } + + public Implementation(IToken tok, string name, List typeParams, List inParams, + List outParams, List localVariables, [Captured] StmtList structuredStmts) + : this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, new Errors()) + { + Contract.Requires(structuredStmts != null); + Contract.Requires(localVariables != null); + Contract.Requires(outParams != null); + Contract.Requires(inParams != null); + Contract.Requires(typeParams != null); + Contract.Requires(name != null); + Contract.Requires(tok != null); + //:this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, new Errors()); + } + + public Implementation(IToken tok, string name, List typeParams, List inParams, + List outParams, List localVariables, [Captured] StmtList structuredStmts, Errors errorHandler) + : this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, errorHandler) + { + Contract.Requires(errorHandler != null); + Contract.Requires(structuredStmts != null); + Contract.Requires(localVariables != null); + Contract.Requires(outParams != null); + Contract.Requires(inParams != null); + Contract.Requires(typeParams != null); + Contract.Requires(name != null); + Contract.Requires(tok != null); + //:this(tok, name, typeParams, inParams, outParams, localVariables, structuredStmts, null, errorHandler); + } + + public Implementation(IToken /*!*/ tok, + string /*!*/ name, + List /*!*/ typeParams, + List /*!*/ inParams, + List /*!*/ outParams, + List /*!*/ localVariables, + [Captured] StmtList /*!*/ structuredStmts, + QKeyValue kv, + Errors /*!*/ errorHandler) + : base(tok, name, typeParams, inParams, outParams) + { + Contract.Requires(tok != null); + Contract.Requires(name != null); + Contract.Requires(typeParams != null); + Contract.Requires(inParams != null); + Contract.Requires(outParams != null); + Contract.Requires(localVariables != null); + Contract.Requires(structuredStmts != null); + Contract.Requires(errorHandler != null); + LocVars = localVariables; + StructuredStmts = structuredStmts; + BigBlocksResolutionContext ctx = new BigBlocksResolutionContext(structuredStmts, errorHandler); + Blocks = ctx.Blocks; + BlockPredecessorsComputed = false; + scc = null; + Attributes = kv; + } + + public Implementation(IToken tok, string name, List typeParams, List inParams, + List outParams, List localVariables, [Captured] List block) + : this(tok, name, typeParams, inParams, outParams, localVariables, block, null) + { + Contract.Requires(cce.NonNullElements(block)); + Contract.Requires(localVariables != null); + Contract.Requires(outParams != null); + Contract.Requires(inParams != null); + Contract.Requires(typeParams != null); + Contract.Requires(name != null); + Contract.Requires(tok != null); + //:this(tok, name, typeParams, inParams, outParams, localVariables, block, null); + } + + public Implementation(IToken /*!*/ tok, + string /*!*/ name, + List /*!*/ typeParams, + List /*!*/ inParams, + List /*!*/ outParams, + List /*!*/ localVariables, + [Captured] List /*!*/ blocks, + QKeyValue kv) + : base(tok, name, typeParams, inParams, outParams) + { + Contract.Requires(name != null); + Contract.Requires(inParams != null); + Contract.Requires(outParams != null); + Contract.Requires(localVariables != null); + Contract.Requires(cce.NonNullElements(blocks)); + LocVars = localVariables; + Blocks = blocks; + BlockPredecessorsComputed = false; + scc = null; + Attributes = kv; + } + + public override void Emit(TokenTextWriter stream, int level) + { + stream.Write(this, level, "implementation "); + EmitAttributes(stream); + stream.Write(this, level, "{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + EmitSignature(stream, false); + stream.WriteLine(); + + stream.WriteLine(level, "{0}", '{'); + + foreach (Variable /*!*/ v in this.LocVars) + { + Contract.Assert(v != null); + v.Emit(stream, level + 1); + } + + if (this.StructuredStmts != null && !stream.Options.PrintInstrumented && + !stream.Options.PrintInlined) + { + if (this.LocVars.Count > 0) + { + stream.WriteLine(); + } + + if (stream.Options.PrintUnstructured < 2) + { + if (stream.Options.PrintUnstructured == 1) + { + stream.WriteLine(this, level + 1, "/*** structured program:"); + } + + this.StructuredStmts.Emit(stream, level + 1); + if (stream.Options.PrintUnstructured == 1) + { + stream.WriteLine(level + 1, "**** end structured program */"); + } + } + } + + if (this.StructuredStmts == null || 1 <= stream.Options.PrintUnstructured || + stream.Options.PrintInstrumented || stream.Options.PrintInlined) + { + foreach (Block b in this.Blocks) + { + b.Emit(stream, level + 1); + } + } + + stream.WriteLine(level, "{0}", '}'); + + stream.WriteLine(); + stream.WriteLine(); + } + + public override void Register(ResolutionContext rc) + { + // nothing to register + } + + public override void Resolve(ResolutionContext rc) + { + if (Proc != null) + { + // already resolved + return; + } + + Proc = rc.LookUpProcedure(cce.NonNull(this.Name)); + if (Proc == null) + { + rc.Error(this, "implementation given for undeclared procedure: {0}", this.Name); + } + else if (Proc is ActionDecl actionDecl) + { + actionDecl.Impl = this; + } + + int previousTypeBinderState = rc.TypeBinderState; + try + { + RegisterTypeParameters(rc); + + rc.PushVarContext(); + rc.Proc = Proc; + RegisterFormals(InParams, rc); + RegisterFormals(OutParams, rc); + foreach (Variable v in LocVars) + { + Contract.Assert(v != null); + v.Register(rc); + v.Resolve(rc); + } + rc.Proc = null; + + foreach (Variable v in LocVars) + { + Contract.Assert(v != null); + v.ResolveWhere(rc); + } + + rc.PushProcedureContext(); + foreach (Block b in Blocks) + { + b.Register(rc); + } + + (this as ICarriesAttributes).ResolveAttributes(rc); + + rc.Proc = Proc; + rc.StateMode = Proc.IsPure ? ResolutionContext.State.StateLess : ResolutionContext.State.Two; + foreach (Block b in Blocks) + { + b.Resolve(rc); + } + rc.Proc = null; + rc.StateMode = ResolutionContext.State.Single; + + rc.PopProcedureContext(); + rc.PopVarContext(); + + Type.CheckBoundVariableOccurrences(TypeParameters, + InParams.Select(Item => Item.TypedIdent.Type).ToList(), + OutParams.Select(Item => Item.TypedIdent.Type).ToList(), + this.tok, "implementation arguments", + rc); + } + finally + { + rc.TypeBinderState = previousTypeBinderState; + } + + SortTypeParams(); + } + + public override void Typecheck(TypecheckingContext tc) + { + //Contract.Requires(tc != null); + base.Typecheck(tc); + + Contract.Assume(this.Proc != null); + + if (this.TypeParameters.Count != Proc.TypeParameters.Count) + { + tc.Error(this, "mismatched number of type parameters in procedure implementation: {0}", + this.Name); + } + else + { + // if the numbers of type parameters are different, it is + // difficult to compare the argument types + MatchFormals(this.InParams, Proc.InParams, "in", tc); + MatchFormals(this.OutParams, Proc.OutParams, "out", tc); + } + + var oldProc = tc.Proc; + tc.Proc = Proc; + tc.Impl = this; + foreach (Variable /*!*/ v in LocVars) + { + Contract.Assert(v != null); + v.Typecheck(tc); + } + foreach (Block b in Blocks) + { + b.Typecheck(tc); + } + Contract.Assert(tc.Proc == Proc); + tc.Impl = null; + tc.Proc = oldProc; + + if (Proc is ActionDecl || Proc is YieldProcedureDecl) + { + var graph = Program.GraphFromImpl(this); + if (!Graph.Acyclic(graph)) + { + if (Proc is ActionDecl) + { + tc.Error(this, "action implementation may not have loops"); + } + else // Proc is YieldProcedureDecl + { + graph.ComputeLoops(); + if (!graph.Reducible) + { + tc.Error(this, "irreducible control flow graph not allowed"); + } + else + { + TypecheckLoopAnnotations(tc, graph); + } + } + } + } + } + + private void TypecheckLoopAnnotations(TypecheckingContext tc, Graph graph) + { + var yieldingProc = (YieldProcedureDecl)Proc; + foreach (var header in graph.Headers) + { + var yieldingLayer = yieldingProc.Layer; + var yieldCmd = (PredicateCmd)header.Cmds.FirstOrDefault(cmd => + cmd is PredicateCmd predCmd && predCmd.HasAttribute(CivlAttributes.YIELDS)); + if (yieldCmd == null) + { + yieldingLayer = int.MinValue; + } + else + { + var layers = yieldCmd.Layers; + header.Cmds.Remove(yieldCmd); + if (layers.Any()) + { + if (layers.Count > 1) + { + tc.Error(header, "expected layer attribute to indicate the highest yielding layer of this loop"); + continue; + } + if (layers[0] > yieldingLayer) + { + tc.Error(header, + "yielding layer of loop must not be more than the layer of enclosing procedure"); + continue; + } + yieldingLayer = layers[0]; + } + } + + var yieldInvariants = header.Cmds + .TakeWhile(cmd => cmd is CallCmd { Proc: YieldInvariantDecl }).OfType() + .ToList(); + header.Cmds.RemoveRange(0, yieldInvariants.Count); + if (yieldInvariants.Any() && yieldCmd == null) + { + tc.Error(header, "expected :yields attribute on this loop"); + } + foreach (var callCmd in yieldInvariants) + { + var yieldInvariant = (YieldInvariantDecl)callCmd.Proc; + var calleeLayerNum = yieldInvariant.Layer; + if (calleeLayerNum > yieldingLayer) + { + tc.Error(callCmd, $"loop must yield at layer {calleeLayerNum} of the called yield invariant"); + } + } + foreach (var predCmd in header.Cmds.TakeWhile(cmd => cmd is PredicateCmd).OfType()) + { + if (predCmd.Layers.Min() <= yieldingLayer && + VariableCollector.Collect(predCmd, true).OfType().Any()) + { + tc.Error(predCmd, + "invariant may not access a global variable since one of its layers is a yielding layer of its loop"); + } + } + + yieldingProc.YieldingLoops[header] = new YieldingLoop(yieldingLayer, yieldInvariants); + } + } + + void MatchFormals(List /*!*/ implFormals, List /*!*/ procFormals, string /*!*/ inout, + TypecheckingContext /*!*/ tc) + { + Contract.Requires(implFormals != null); + Contract.Requires(procFormals != null); + Contract.Requires(inout != null); + Contract.Requires(tc != null); + if (implFormals.Count != procFormals.Count) + { + tc.Error(this, "mismatched number of {0}-parameters in procedure implementation: {1}", + inout, this.Name); + } + else + { + // unify the type parameters so that types can be compared + Contract.Assert(Proc != null); + Contract.Assert(this.TypeParameters.Count == Proc.TypeParameters.Count); + + IDictionary /*!*/ + subst1 = + new Dictionary(); + IDictionary /*!*/ + subst2 = + new Dictionary(); + + for (int i = 0; i < this.TypeParameters.Count; ++i) + { + TypeVariable /*!*/ + newVar = + new TypeVariable(Token.NoToken, Proc.TypeParameters[i].Name); + Contract.Assert(newVar != null); + subst1.Add(Proc.TypeParameters[i], newVar); + subst2.Add(this.TypeParameters[i], newVar); + } + + for (int i = 0; i < implFormals.Count; i++) + { + // the names of the formals are allowed to change from the proc to the impl + + // but types must be identical + Type t = cce.NonNull((Variable) implFormals[i]).TypedIdent.Type.Substitute(subst2); + Type u = cce.NonNull((Variable) procFormals[i]).TypedIdent.Type.Substitute(subst1); + if (!t.Equals(u)) + { + string /*!*/ + a = cce.NonNull((Variable) implFormals[i]).Name; + Contract.Assert(a != null); + string /*!*/ + b = cce.NonNull((Variable) procFormals[i]).Name; + Contract.Assert(b != null); + string /*!*/ + c; + if (a == b) + { + c = a; + } + else + { + c = String.Format("{0} (named {1} in implementation)", b, a); + } + + tc.Error(this, "mismatched type of {0}-parameter in implementation {1}: {2}", inout, this.Name, c); + } + } + } + } + + private Dictionary /*?*/ + formalMap = null; + + public void ResetImplFormalMap() + { + this.formalMap = null; + } + + public Dictionary /*!*/ GetImplFormalMap(CoreOptions options) + { + Contract.Ensures(Contract.Result>() != null); + + if (this.formalMap != null) + { + return this.formalMap; + } + else + { + Dictionary /*!*/ + map = new Dictionary(InParams.Count + OutParams.Count); + + Contract.Assume(this.Proc != null); + Contract.Assume(InParams.Count == Proc.InParams.Count); + for (int i = 0; i < InParams.Count; i++) + { + Variable /*!*/ + v = InParams[i]; + Contract.Assert(v != null); + IdentifierExpr ie = new IdentifierExpr(v.tok, v); + Variable /*!*/ + pv = Proc.InParams[i]; + Contract.Assert(pv != null); + map.Add(pv, ie); + } + + System.Diagnostics.Debug.Assert(OutParams.Count == Proc.OutParams.Count); + for (int i = 0; i < OutParams.Count; i++) + { + Variable /*!*/ + v = cce.NonNull(OutParams[i]); + IdentifierExpr ie = new IdentifierExpr(v.tok, v); + Variable pv = cce.NonNull(Proc.OutParams[i]); + map.Add(pv, ie); + } + + this.formalMap = map; + + if (options.PrintWithUniqueASTIds) + { + options.OutputWriter.WriteLine("Implementation.GetImplFormalMap on {0}:", this.Name); + using TokenTextWriter stream = + new TokenTextWriter("", options.OutputWriter, /*setTokens=*/false, /*pretty=*/ false, options); + foreach (var e in map) + { + options.OutputWriter.Write(" "); + cce.NonNull((Variable /*!*/) e.Key).Emit(stream, 0); + options.OutputWriter.Write(" --> "); + cce.NonNull((Expr) e.Value).Emit(stream); + options.OutputWriter.WriteLine(); + } + } + + return map; + } + } + + /// + /// Return a collection of blocks that are reachable from the block passed as a parameter. + /// The block must be defined in the current implementation + /// + public ICollection GetConnectedComponents(Block startingBlock) + { + Contract.Requires(startingBlock != null); + Contract.Ensures(cce.NonNullElements(Contract.Result>(), true)); + Contract.Assert(this.Blocks.Contains(startingBlock)); + + if (!this.BlockPredecessorsComputed) + { + ComputeStronglyConnectedComponents(); + } + +#if DEBUG_PRINT + System.Console.WriteLine("* Strongly connected components * \n{0} \n ** ", scc); +#endif + + foreach (ICollection component in cce.NonNull(this.scc)) + { + foreach (Block /*!*/ b in component) + { + Contract.Assert(b != null); + if (b == startingBlock) // We found the compontent that owns the startingblock + { + return component; + } + } + } + + { + Contract.Assert(false); + throw new cce.UnreachableException(); + } // if we are here, it means that the block is not in one of the components. This is an error. + } + + /// + /// Compute the strongly connected compontents of the blocks in the implementation. + /// As a side effect, it also computes the "predecessor" relation for the block in the implementation + /// + override public void ComputeStronglyConnectedComponents() + { + if (!this.BlockPredecessorsComputed) + { + ComputePredecessorsForBlocks(); + } + + Adjacency next = new Adjacency(Successors); + Adjacency prev = new Adjacency(Predecessors); + + this.scc = new StronglyConnectedComponents(this.Blocks, next, prev); + scc.Compute(); + + + foreach (Block /*!*/ block in this.Blocks) + { + Contract.Assert(block != null); + block.Predecessors = new List(); + } + } + + /// + /// Reset the abstract stated computed before + /// + override public void ResetAbstractInterpretationState() + { + foreach (Block /*!*/ b in this.Blocks) + { + Contract.Assert(b != null); + b.ResetAbstractInterpretationState(); + } + } + + /// + /// A private method used as delegate for the strongly connected components. + /// It return, given a node, the set of its successors + /// + private IEnumerable /**/ /*!*/ Successors(Block node) + { + Contract.Requires(node != null); + Contract.Ensures(Contract.Result() != null); + + GotoCmd gotoCmd = node.TransferCmd as GotoCmd; + + if (gotoCmd != null) + { + // If it is a gotoCmd + Contract.Assert(gotoCmd.labelTargets != null); + + return gotoCmd.labelTargets; + } + else + { + // otherwise must be a ReturnCmd + Contract.Assert(node.TransferCmd is ReturnCmd); + + return new List(); + } + } + + /// + /// A private method used as delegate for the strongly connected components. + /// It return, given a node, the set of its predecessors + /// + private IEnumerable /**/ /*!*/ Predecessors(Block node) + { + Contract.Requires(node != null); + Contract.Ensures(Contract.Result() != null); + + Contract.Assert(this.BlockPredecessorsComputed); + + return node.Predecessors; + } + + /// + /// Compute the predecessor informations for the blocks + /// + public void ComputePredecessorsForBlocks() + { + var blocks = this.Blocks; + foreach (Block b in blocks) + { + b.Predecessors = new List(); + } + + ComputePredecessorsForBlocks(blocks); + + this.BlockPredecessorsComputed = true; + } + + public static void ComputePredecessorsForBlocks(List blocks) + { + foreach (var block in blocks) { + if (block.TransferCmd is not GotoCmd gtc) { + continue; + } + + Contract.Assert(gtc.labelTargets != null); + foreach (var /*!*/ dest in gtc.labelTargets) + { + Contract.Assert(dest != null); + dest.Predecessors.Add(block); + } + } + } + + public void PruneUnreachableBlocks(CoreOptions options) + { + var toVisit = new Stack(); + var reachableBlocks = new List(); + var reachable = new HashSet(); // the set of elements in "reachableBlocks" + + toVisit.Push(Blocks[0]); + while (toVisit.Count != 0) + { + var block = toVisit.Pop(); + if (!reachable.Add(block)) { + continue; + } + + reachableBlocks.Add(block); + if (block.TransferCmd is GotoCmd gotoCmd) { + if (options.PruneInfeasibleEdges) { + foreach (var command in block.Cmds) { + Contract.Assert(command != null); + if (command is PredicateCmd { Expr: LiteralExpr { IsFalse: true } }) { + // This statement sequence will never reach the end, because of this "assume false" or "assert false". + // Hence, it does not reach its successors. + block.TransferCmd = new ReturnCmd(block.TransferCmd.tok); + goto NEXT_BLOCK; + } + } + } + + // it seems that the goto statement at the end may be reached + foreach (var next in gotoCmd.labelTargets) { + Contract.Assume(next != null); + toVisit.Push(next); + } + } + + NEXT_BLOCK: + { + } + } + + this.Blocks = reachableBlocks; + } + + public override Absy StdDispatch(StandardVisitor visitor) + { + //Contract.Requires(visitor != null); + Contract.Ensures(Contract.Result() != null); + return visitor.VisitImplementation(this); + } + + public void FreshenCaptureStates() + { + // Assume commands with the "captureState" attribute allow model states to be + // captured for error reporting. + // Some program transformations, such as loop unrolling, duplicate parts of the + // program, leading to "capture-state-assumes" being duplicated. This leads + // to ambiguity when getting a state from the model. + // This method replaces the key of every "captureState" attribute with something + // unique + + int FreshCounter = 0; + foreach (var b in Blocks) + { + List newCmds = new List(); + for (int i = 0; i < b.Cmds.Count(); i++) + { + var a = b.Cmds[i] as AssumeCmd; + if (a != null && (QKeyValue.FindStringAttribute(a.Attributes, "captureState") != null)) + { + string StateName = QKeyValue.FindStringAttribute(a.Attributes, "captureState"); + newCmds.Add(new AssumeCmd(Token.NoToken, a.Expr, FreshenCaptureState(a.Attributes, FreshCounter))); + FreshCounter++; + } + else + { + newCmds.Add(b.Cmds[i]); + } + } + + b.Cmds = newCmds; + } + } + + private QKeyValue FreshenCaptureState(QKeyValue Attributes, int FreshCounter) + { + // Returns attributes identical to Attributes, but: + // - reversed (for ease of implementation; should not matter) + // - with the value for "captureState" replaced by a fresh value + Contract.Requires(QKeyValue.FindStringAttribute(Attributes, "captureState") != null); + string FreshValue = QKeyValue.FindStringAttribute(Attributes, "captureState") + "$renamed$" + Name + "$" + + FreshCounter; + + QKeyValue result = null; + while (Attributes != null) + { + if (Attributes.Key.Equals("captureState")) + { + result = new QKeyValue(Token.NoToken, Attributes.Key, new List() {FreshValue}, result); + } + else + { + result = new QKeyValue(Token.NoToken, Attributes.Key, Attributes.Params, result); + } + + Attributes = Attributes.Next; + } + + return result; + } +} \ No newline at end of file From 52d696bc511c0548b6456090b2f8c735370dc579 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 15:35:29 +0200 Subject: [PATCH 02/42] Print splits and various small fixes --- Source/Core/AST/Implementation.cs | 81 +++++++++++-------- Source/Core/AST/Program.cs | 1 + Source/Core/CoreOptions.cs | 3 +- Source/Core/PrintOptions.cs | 4 +- Source/ExecutionEngine/CommandLineOptions.cs | 29 +++++-- Source/ExecutionEngine/ExecutionEngine.cs | 19 ++--- Source/Graph/Graph.cs | 13 +-- Source/Provers/LeanAuto/LeanAutoGenerator.cs | 2 +- Source/VCGeneration/Checker.cs | 2 +- Source/VCGeneration/ConditionGeneration.cs | 24 +++--- Source/VCGeneration/ManualSplit.cs | 1 + Source/VCGeneration/Prune/DataflowAnalysis.cs | 3 +- .../Prune/{Prune.cs => Pruner.cs} | 23 +++--- Source/VCGeneration/Prune/RevealedAnalysis.cs | 10 +-- Source/VCGeneration/Split.cs | 57 ++++++++----- .../VerificationConditionGenerator.cs | 11 +++ 16 files changed, 165 insertions(+), 118 deletions(-) rename Source/VCGeneration/Prune/{Prune.cs => Pruner.cs} (88%) diff --git a/Source/Core/AST/Implementation.cs b/Source/Core/AST/Implementation.cs index 384e5d227..9169f014b 100644 --- a/Source/Core/AST/Implementation.cs +++ b/Source/Core/AST/Implementation.cs @@ -431,7 +431,46 @@ public Implementation(IToken /*!*/ tok, Attributes = kv; } - public override void Emit(TokenTextWriter stream, int level) + public override void Emit(TokenTextWriter stream, int level) { + void BlocksWriters(TokenTextWriter stream) { + if (this.StructuredStmts != null && !stream.Options.PrintInstrumented && !stream.Options.PrintInlined) { + if (this.LocVars.Count > 0) { + stream.WriteLine(); + } + + if (stream.Options.PrintUnstructured < 2) { + if (stream.Options.PrintUnstructured == 1) { + stream.WriteLine(this, level + 1, "/*** structured program:"); + } + + this.StructuredStmts.Emit(stream, level + 1); + if (stream.Options.PrintUnstructured == 1) { + stream.WriteLine(level + 1, "**** end structured program */"); + } + } + } + + if (StructuredStmts == null || 1 <= stream.Options.PrintUnstructured || + stream.Options.PrintInstrumented || stream.Options.PrintInlined) { + foreach (Block b in Blocks) { + b.Emit(stream, level + 1); + } + } + } + + EmitImplementation(stream, level, BlocksWriters, showLocals: true); + } + + public void EmitImplementation(TokenTextWriter stream, int level, IEnumerable blocks, + bool showLocals) { + EmitImplementation(stream, level, writer => { + foreach (var block in blocks) { + block.Emit(writer, level + 1); + } + }, showLocals); + } + + public void EmitImplementation(TokenTextWriter stream, int level, Action printBlocks, bool showLocals) { stream.Write(this, level, "implementation "); EmitAttributes(stream); @@ -441,43 +480,15 @@ public override void Emit(TokenTextWriter stream, int level) stream.WriteLine(level, "{0}", '{'); - foreach (Variable /*!*/ v in this.LocVars) - { - Contract.Assert(v != null); - v.Emit(stream, level + 1); - } - - if (this.StructuredStmts != null && !stream.Options.PrintInstrumented && - !stream.Options.PrintInlined) - { - if (this.LocVars.Count > 0) + if (showLocals) { + foreach (Variable /*!*/ v in this.LocVars) { - stream.WriteLine(); - } - - if (stream.Options.PrintUnstructured < 2) - { - if (stream.Options.PrintUnstructured == 1) - { - stream.WriteLine(this, level + 1, "/*** structured program:"); - } - - this.StructuredStmts.Emit(stream, level + 1); - if (stream.Options.PrintUnstructured == 1) - { - stream.WriteLine(level + 1, "**** end structured program */"); - } + Contract.Assert(v != null); + v.Emit(stream, level + 1); } } - if (this.StructuredStmts == null || 1 <= stream.Options.PrintUnstructured || - stream.Options.PrintInstrumented || stream.Options.PrintInlined) - { - foreach (Block b in this.Blocks) - { - b.Emit(stream, level + 1); - } - } + printBlocks(stream); stream.WriteLine(level, "{0}", '}'); @@ -864,7 +875,7 @@ public void ResetImplFormalMap() /// Compute the strongly connected compontents of the blocks in the implementation. /// As a side effect, it also computes the "predecessor" relation for the block in the implementation /// - override public void ComputeStronglyConnectedComponents() + public override void ComputeStronglyConnectedComponents() { if (!this.BlockPredecessorsComputed) { diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index a171b549d..5684c315f 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; +using System.Text; using Microsoft.Boogie.GraphUtil; namespace Microsoft.Boogie; diff --git a/Source/Core/CoreOptions.cs b/Source/Core/CoreOptions.cs index 81d3ec591..5e2a19351 100644 --- a/Source/Core/CoreOptions.cs +++ b/Source/Core/CoreOptions.cs @@ -82,7 +82,8 @@ public enum VerbosityLevel int VcsMaxKeepGoingSplits { get; } double VcsMaxCost { get; } bool VcsSplitOnEveryAssert { get; } - string PrintPrunedFile { get; } + string PrintSplitFile { get; } + bool PrintSplitDeclarations { get; } bool PrettyPrint { get; } bool NormalizeDeclarationOrder { get; } XmlSink XmlSink { get; } diff --git a/Source/Core/PrintOptions.cs b/Source/Core/PrintOptions.cs index fc7663b44..80b4b8fc8 100644 --- a/Source/Core/PrintOptions.cs +++ b/Source/Core/PrintOptions.cs @@ -13,13 +13,13 @@ public interface PrintOptions { bool PrintInlined { get; } int StratifiedInlining { get; } bool PrintDesugarings { get; set; } - bool PrintPassive { get; set; } + string PrintPassiveFile { get; set; } int PrintUnstructured { get; set; } bool ReflectAdd { get; } } record PrintOptionsRec(bool PrintWithUniqueASTIds, bool PrintInstrumented, bool PrintInlined, int StratifiedInlining, bool ReflectAdd) : PrintOptions { public bool PrintDesugarings { get; set; } - public bool PrintPassive { get; set; } + public string PrintPassiveFile { get; set; } public int PrintUnstructured { get; set; } } \ No newline at end of file diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 5c1966e04..426c46e0b 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -65,7 +65,8 @@ void ObjectInvariant2() public int VerifySnapshots { get; set; } = -1; public bool VerifySeparately { get; set; } public string PrintFile { get; set; } - public string PrintPrunedFile { get; set; } + public string PrintSplitFile { get; set; } + public bool PrintSplitDeclarations { get; set; } /** * Whether to emit {:qid}, {:skolemid} and set-info :boogie-vc-id @@ -88,9 +89,9 @@ public bool PrintDesugarings { set => printDesugarings = value; } - public bool PrintPassive { - get => printPassive; - set => printPassive = value; + public string PrintPassiveFile { + get => printPassiveFile; + set => printPassiveFile = value; } public List> UseResolvedProgram { get; } = new(); @@ -600,7 +601,7 @@ void ObjectInvariant5() private bool printWithUniqueAstIds = false; private int printUnstructured = 0; private bool printDesugarings = false; - private bool printPassive = false; + private string printPassiveFile; private bool emitDebugInformation = true; private bool normalizeNames; private bool normalizeDeclarationOrder = true; @@ -698,9 +699,25 @@ protected override bool ParseOption(string name, CommandLineParseState ps) return true; case "printPruned": + case "printSplit": if (ps.ConfirmArgumentCount(1)) { - PrintPrunedFile = args[ps.i]; + PrintSplitFile = args[ps.i]; + } + + return true; + case "printSplitDeclarations": + if (ps.ConfirmArgumentCount(0)) + { + PrintSplitDeclarations = true; + } + + return true; + + case "printPassive": + if (ps.ConfirmArgumentCount(1)) + { + PrintPassiveFile = args[ps.i]; } return true; diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 6fe111559..df60a98ca 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -135,11 +135,6 @@ public async Task ProcessProgram(TextWriter output, Program program, strin { programId = "main_program_id"; } - - if (Options.PrintFile != null && !Options.PrintPassive) { - // Printing passive programs happens later - PrintBplFile(Options.PrintFile, program, false, true, Options.PrettyPrint); - } PipelineOutcome outcome = ResolveAndTypecheck(program, bplFileName, out var civlTypeChecker); if (outcome != PipelineOutcome.ResolvedAndTypeChecked) { @@ -240,15 +235,15 @@ public void EliminateDeadVariables(Program program) { Microsoft.Boogie.UnusedVarEliminator.Eliminate(program); } - - - public void PrintBplFile(string filename, Program program, bool allowPrintDesugaring, bool setTokens = true, - bool pretty = false) - { + + public void PrintBplFile(string filename, Program program, + bool allowPrintDesugaring, bool setTokens = true, + bool pretty = false) { PrintBplFile(Options, filename, program, allowPrintDesugaring, setTokens, pretty); } - public static void PrintBplFile(ExecutionEngineOptions options, string filename, Program program, bool allowPrintDesugaring, bool setTokens = true, + public static void PrintBplFile(ExecutionEngineOptions options, string filename, Program program, + bool allowPrintDesugaring, bool setTokens = true, bool pretty = false) { @@ -612,7 +607,7 @@ private ProcessedProgram PreProcessProgramVerification(Program program) program.Emit(new TokenTextWriter(Options.OutputWriter, Options.PrettyPrint, Options)); } - program.DeclarationDependencies = Prune.ComputeDeclarationDependencies(Options, program); + program.DeclarationDependencies = Pruner.ComputeDeclarationDependencies(Options, program); return processedProgram; }).Result; } diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index ebb80e3e4..d3be8bfe6 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -1145,15 +1145,18 @@ public ICollection ComputeReachability(Node start, bool forward = true) todo.Push(start); while (todo.Any()) { - var b = todo.Pop(); - if (visited.Contains(b)) + var current = todo.Pop(); + if (!visited.Add(current)) { continue; } - visited.Add(b); - var related = forward ? this.Successors(b) : this.Predecessors(b); - related.Where(blk => !visited.Contains(blk)).ToList().ForEach(blk => todo.Push(blk)); + var targets = forward ? this.Successors(current) : this.Predecessors(current); + foreach (var target in targets) { + if (!visited.Contains(target)) { + todo.Push(target); + } + } } return visited; } diff --git a/Source/Provers/LeanAuto/LeanAutoGenerator.cs b/Source/Provers/LeanAuto/LeanAutoGenerator.cs index 511a099fa..e3a5310b4 100644 --- a/Source/Provers/LeanAuto/LeanAutoGenerator.cs +++ b/Source/Provers/LeanAuto/LeanAutoGenerator.cs @@ -50,7 +50,7 @@ public static void EmitPassiveProgramAsLean(VCGenOptions options, Program p, Tex var liveDeclarations = !options.Prune ? p.TopLevelDeclarations - : Prune.GetLiveDeclarations(options, p, allBlocks.ToList()).ToList(); + : Pruner.GetLiveDeclarations(options, p, allBlocks.ToList()).ToList(); generator.WriteLine("-- Type constructors"); // Always include all type constructors diff --git a/Source/VCGeneration/Checker.cs b/Source/VCGeneration/Checker.cs index 8c993f068..49f23b1df 100644 --- a/Source/VCGeneration/Checker.cs +++ b/Source/VCGeneration/Checker.cs @@ -163,7 +163,7 @@ private void Setup(Program program, ProverContext ctx, Split split) // TODO(wuestholz): Is this lock necessary? lock (Program.TopLevelDeclarations) { - var declarations = split.TopLevelDeclarations; + var declarations = split.PrunedDeclarations; var reorderedDeclarations = GetReorderedDeclarations(declarations, SolverOptions.RandomSeed); foreach (var declaration in reorderedDeclarations) { Contract.Assert(declaration != null); diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index d1528d907..f0f481b8e 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -223,10 +223,9 @@ protected static void InjectPreconditions(VCGenOptions options, ImplementationRu Block origStartBlock = impl.Blocks[0]; Block insertionPoint = new Block( new Token(-17, -4), blockLabel, startCmds, - new GotoCmd(Token.NoToken, new List {origStartBlock.Label}, new List {origStartBlock})); + new GotoCmd(impl.tok, new List {origStartBlock.Label}, new List {origStartBlock})); - impl.Blocks[0] = insertionPoint; // make insertionPoint the start block - impl.Blocks.Add(origStartBlock); // and put the previous start block at the end of the list + impl.Blocks.Insert(0, insertionPoint); // make insertionPoint the start block // (free and checked) requires clauses foreach (Requires req in impl.Proc.Requires) @@ -506,17 +505,17 @@ public virtual void Close() } - public static void EmitImpl(VCGenOptions options, ImplementationRun run, bool printDesugarings) + public static void EmitImpl(VCGenOptions options, ImplementationRun run, bool printDesugarings, IEnumerable overrideBlocks = null) { var impl = run.Implementation; + overrideBlocks ??= impl.Blocks; + Contract.Requires(impl != null); - int oldPrintUnstructured = options.PrintUnstructured; - options.PrintUnstructured = 2; // print only the unstructured program bool oldPrintDesugaringSetting = options.PrintDesugarings; options.PrintDesugarings = printDesugarings; - impl.Emit(new TokenTextWriter("", run.OutputWriter, /*setTokens=*/ false, /*pretty=*/ false, options), 0); + var writer = new TokenTextWriter("", run.OutputWriter, /*setTokens=*/ false, /*pretty=*/ false, options); + impl.EmitImplementation(writer, 0, overrideBlocks, true); options.PrintDesugarings = oldPrintDesugaringSetting; - options.PrintUnstructured = oldPrintUnstructured; } @@ -546,20 +545,19 @@ protected Block GenerateUnifiedExit(Implementation impl, Dictionary 1) { string unifiedExitLabel = "GeneratedUnifiedExit"; - Block unifiedExit; - unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), + var unifiedExit = new Block(new Token(-17, -4), unifiedExitLabel, new List(), new ReturnCmd(impl.StructuredStmts != null ? impl.StructuredStmts.EndCurly : Token.NoToken)); Contract.Assert(unifiedExit != null); foreach (Block b in impl.Blocks) { - if (b.TransferCmd is ReturnCmd) + if (b.TransferCmd is ReturnCmd returnCmd) { List labels = new List(); labels.Add(unifiedExitLabel); List bs = new List(); bs.Add(unifiedExit); - GotoCmd go = new GotoCmd(Token.NoToken, labels, bs); - gotoCmdOrigins[go] = (ReturnCmd) b.TransferCmd; + GotoCmd go = new GotoCmd(returnCmd.tok, labels, bs); + gotoCmdOrigins[go] = returnCmd; b.TransferCmd = go; unifiedExit.Predecessors.Add(b); } diff --git a/Source/VCGeneration/ManualSplit.cs b/Source/VCGeneration/ManualSplit.cs index e46d722f8..daa7c6bdc 100644 --- a/Source/VCGeneration/ManualSplit.cs +++ b/Source/VCGeneration/ManualSplit.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Microsoft.Boogie; diff --git a/Source/VCGeneration/Prune/DataflowAnalysis.cs b/Source/VCGeneration/Prune/DataflowAnalysis.cs index 071d0ffec..3a1896f1a 100644 --- a/Source/VCGeneration/Prune/DataflowAnalysis.cs +++ b/Source/VCGeneration/Prune/DataflowAnalysis.cs @@ -42,8 +42,7 @@ public void Run() { var previous = getPrevious(node); var previousStates = previous.Select(p => outStates.GetValueOrDefault(p)).Where(x => x != null).ToList(); var inState = previousStates.Any() ? previousStates.Aggregate(Merge) : Empty; - var previousInState = inStates.GetValueOrDefault(node); - if (previousInState != null && StateEquals(inState, previousInState)) { + if (inStates.ContainsKey(node) && StateEquals(inState, inStates[node])) { continue; } diff --git a/Source/VCGeneration/Prune/Prune.cs b/Source/VCGeneration/Prune/Pruner.cs similarity index 88% rename from Source/VCGeneration/Prune/Prune.cs rename to Source/VCGeneration/Prune/Pruner.cs index 455d2878a..969054063 100644 --- a/Source/VCGeneration/Prune/Prune.cs +++ b/Source/VCGeneration/Prune/Pruner.cs @@ -8,7 +8,7 @@ namespace Microsoft.Boogie { - public class Prune { + public class Pruner { public static Dictionary>? ComputeDeclarationDependencies(VCGenOptions options, Program program) { @@ -98,7 +98,7 @@ private static RevealedState GetRevealedState(List blocks) Aggregate(RevealedState.AllHidden, RevealedAnalysis.MergeStates); } - private static Graph GetControlFlowGraph(List blocks) + public static Graph GetControlFlowGraph(List blocks) { /* * Generally the blocks created by splitting have unset block.Predecessors fields @@ -110,22 +110,17 @@ private static Graph GetControlFlowGraph(List blocks) block.Predecessors.Clear(); } Implementation.ComputePredecessorsForBlocks(blocks); - var graph = new Graph(); + var graph = new Graph(); foreach (var block in blocks) { + var commands = block.Cmds.Append(block.TransferCmd).ToList(); foreach (var predecessor in block.Predecessors) { - var last = predecessor.Cmds.LastOrDefault(); - if (last != null) { - graph.AddEdge(last, block.Cmds[0]); - } else { - if (predecessor.Predecessors.Any()) { - throw new Exception(); - } - } + var previous = predecessor.TransferCmd; + graph.AddEdge(previous, commands.First()); } - for (var index = 0; index < block.Cmds.Count - 1; index++) { - var command = block.Cmds[index]; - var nextCommand = block.Cmds[index + 1]; + for (var index = 0; index < commands.Count - 1; index++) { + var command = commands[index]; + var nextCommand = commands[index + 1]; graph.AddEdge(command, nextCommand); } } diff --git a/Source/VCGeneration/Prune/RevealedAnalysis.cs b/Source/VCGeneration/Prune/RevealedAnalysis.cs index b146ce22b..59adfe6a0 100644 --- a/Source/VCGeneration/Prune/RevealedAnalysis.cs +++ b/Source/VCGeneration/Prune/RevealedAnalysis.cs @@ -16,11 +16,11 @@ public bool IsRevealed(Function function) { public static readonly RevealedState AllHidden = new(HideRevealCmd.Modes.Hide, ImmutableHashSet.Empty); } -class RevealedAnalysis : DataflowAnalysis> { +class RevealedAnalysis : DataflowAnalysis> { - public RevealedAnalysis(IReadOnlyList roots, - Func> getNext, - Func> getPrevious) : base(roots, getNext, getPrevious) + public RevealedAnalysis(IReadOnlyList roots, + Func> getNext, + Func> getPrevious) : base(roots, getNext, getPrevious) { } @@ -80,7 +80,7 @@ static RevealedState GetUpdatedState(HideRevealCmd hideRevealCmd, RevealedState return state with { Offset = newOffset }; } - protected override ImmutableStack Update(Cmd node, ImmutableStack state) { + protected override ImmutableStack Update(Absy node, ImmutableStack state) { if (state.IsEmpty) { throw new Exception("Unbalanced use of push and pop commands"); } diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index 4cd19abc5..4012a6bdd 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Threading; @@ -10,6 +11,7 @@ using Microsoft.Boogie.VCExprAST; using Microsoft.Boogie.SMTLib; using System.Threading.Tasks; +using VCGeneration; namespace VC { @@ -19,12 +21,23 @@ public class Split : ProofRun private readonly Random randomGen; public ImplementationRun Run { get; } - public int RandomSeed { get; private set; } + public int RandomSeed { get; } - public List Blocks { get; } + public List Blocks { get; set; } readonly List bigBlocks = new(); public List Asserts => Blocks.SelectMany(block => block.cmds.OfType()).ToList(); public readonly IReadOnlyList TopLevelDeclarations; + public IReadOnlyList prunedDeclarations; + + public IReadOnlyList PrunedDeclarations { + get { + if (prunedDeclarations == null) { + prunedDeclarations = Pruner.GetLiveDeclarations(parent.Options, parent.program, Blocks).ToList(); + } + + return prunedDeclarations; + } + } readonly Dictionary /*!*/ stats = new Dictionary(); @@ -66,7 +79,6 @@ public readonly VerificationConditionGenerator /*!*/ public int SplitIndex { get; set; } public VerificationConditionGenerator.ErrorReporter reporter; - private static int debugCounter; public Split(VCGenOptions options, List /*!*/ blocks, Dictionary /*!*/ gotoCmdOrigins, VerificationConditionGenerator /*!*/ par, ImplementationRun run, int? randomSeed = null) @@ -82,32 +94,38 @@ public Split(VCGenOptions options, List /*!*/ blocks, Interlocked.Increment(ref currentId); TopLevelDeclarations = par.program.TopLevelDeclarations; - var counter = debugCounter++; - PrintTopLevelDeclarationsForPruning(par.program, Implementation, "before#" + counter); - TopLevelDeclarations = Prune.GetLiveDeclarations(options, par.program, blocks).ToList(); - PrintTopLevelDeclarationsForPruning(par.program, Implementation, "after#" + counter); RandomSeed = randomSeed ?? Implementation.RandomSeed ?? Options.RandomSeed ?? 0; randomGen = new Random(RandomSeed); } + + - private void PrintTopLevelDeclarationsForPruning(Program program, Implementation implementation, string suffix) - { - if (!Options.Prune || Options.PrintPrunedFile == null) - { + public void PrintSplit() { + if (Options.PrintSplitFile == null) { return; } using var writer = new TokenTextWriter( - $"{Options.PrintPrunedFile}-{suffix}-{Util.EscapeFilename(implementation.Name)}.bpl", false, + $"{Options.PrintSplitFile}-{Util.EscapeFilename(Implementation.Name)}-{SplitIndex}.spl", false, Options.PrettyPrint, Options); + Implementation.EmitImplementation(writer, 0, Blocks, false); + PrintSplitDeclarations(writer); + } + + private void PrintSplitDeclarations(TokenTextWriter writer) + { + if (!Options.Prune || !Options.PrintSplitDeclarations) + { + return; + } + var functionAxioms = - program.Functions.Where(f => f.DefinitionAxioms.Any()).SelectMany(f => f.DefinitionAxioms); + PrunedDeclarations.OfType().Where(f => f.DefinitionAxioms.Any()).SelectMany(f => f.DefinitionAxioms); var constantAxioms = - program.Constants.Where(f => f.DefinitionAxioms.Any()).SelectMany(c => c.DefinitionAxioms); + PrunedDeclarations.OfType().Where(f => f.DefinitionAxioms.Any()).SelectMany(c => c.DefinitionAxioms); - foreach (var declaration in (TopLevelDeclarations ?? program.TopLevelDeclarations) - .Except(functionAxioms.Concat(constantAxioms)).ToList()) + foreach (var declaration in PrunedDeclarations.Except(functionAxioms.Concat(constantAxioms)).ToList()) { declaration.Emit(writer, 0); } @@ -715,11 +733,7 @@ private Split SliceAsserts(double limit, bool pos) void Print() { - List tmp = Implementation.Blocks; - Contract.Assert(tmp != null); - Implementation.Blocks = Blocks; - ConditionGeneration.EmitImpl(Options, Run, false); - Implementation.Blocks = tmp; + ConditionGeneration.EmitImpl(Options, Run, false, Blocks); } public Counterexample ToCounterexample(ProverContext context) @@ -923,6 +937,7 @@ public async Task BeginCheck(TextWriter traceWriter, Checker checker, VerifierCa ModelViewInfo mvInfo, uint timeout, uint rlimit, CancellationToken cancellationToken) { + PrintSplit(); Contract.Requires(checker != null); Contract.Requires(callback != null); diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index f2e57c3b8..bdff1b0f2 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -450,6 +450,17 @@ public void PrepareImplementation(ImplementationRun run, VerifierCallback callba data.ModelViewInfo = modelViewInfo; ExpandAsserts(run.Implementation); + + if (Options.PrintPassiveFile != null) { + lock (Options) { + var prev = Options.PrintUnstructured; + Options.PrintUnstructured = 2; + using var writer = new TokenTextWriter(Options.PrintPassiveFile, false, false, Options); + writer.WriteLine(); + program.Emit(writer); + Options.PrintUnstructured = prev; + } + } } else { From 5e2d5483eb7bb91b9735911eb38ff55d0787fbbc Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 15:58:50 +0200 Subject: [PATCH 03/42] Update tests --- Test/pruning/Focus.bpl | 9 +-------- Test/pruning/Focus.bpl.expect | 2 +- Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl | 10 ++++------ Test/test0/Split/Split.bpl | 12 +++++------- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Test/pruning/Focus.bpl b/Test/pruning/Focus.bpl index d2e2b6092..735a57d47 100644 --- a/Test/pruning/Focus.bpl +++ b/Test/pruning/Focus.bpl @@ -1,12 +1,5 @@ -// RUN: %parallel-boogie /vcsDumpSplits /errorTrace:0 "%s" > "%t" -// RUN: mv %/S/*.split* %/S/Output/ +// RUN: %parallel-boogie /printSplit:%t /errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" -// RUN: %diff ./Output/Ex.split.0.bpl ./Output/Ex.split.0.bpl -// RUN: %diff ./Output/Ex.split.1.bpl ./Output/Ex.split.1.bpl -// RUN: %diff ./Output/Ex.split.2.bpl ./Output/Ex.split.2.bpl -// RUN: %diff ./Output/Ex.split.3.bpl ./Output/Ex.split.3.bpl -// RUN: %diff ./Output/Ex.split.4.bpl ./Output/Ex.split.4.bpl -// RUN: %diff ./Output/Ex.split.5.bpl ./Output/Ex.split.5.bpl procedure Ex() returns (y: int) ensures y >= 0; diff --git a/Test/pruning/Focus.bpl.expect b/Test/pruning/Focus.bpl.expect index 18f894e42..d44953784 100644 --- a/Test/pruning/Focus.bpl.expect +++ b/Test/pruning/Focus.bpl.expect @@ -1,3 +1,3 @@ -Focus.bpl(22,5): Error: this assertion could not be proved +Focus.bpl(21,5): Error: this assertion could not be proved Boogie program verifier finished with 1 verified, 1 error diff --git a/Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl b/Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl index 00dd16edf..148216280 100644 --- a/Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl +++ b/Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl @@ -1,10 +1,8 @@ -// RUN: %parallel-boogie /vcsDumpSplits "%s" > "%t" -// RUN: mv %/S/Foo.split*.bpl %/S/Output/ -// RUN: mv %/S/Foo.split*.dot %/S/Output/ +// RUN: %parallel-boogie /printSplit:%t "%s" > "%t" // RUN: %diff "%s.expect" "%t" -// RUN: %diff %S/Foo.split.0.bpl.expect ./Output/Foo.split.0.bpl -// RUN: %diff %S/Foo.split.1.bpl.expect ./Output/Foo.split.1.bpl -// RUN: %diff %S/Foo.split.2.bpl.expect ./Output/Foo.split.2.bpl +// RUN: %diff %S/Foo.split.0.bpl.expect %t-Foo-0.spl +// RUN: %diff %S/Foo.split.1.bpl.expect %t-Foo-1.spl +// RUN: %diff %S/Foo.split.2.bpl.expect %t-Foo-2.spl procedure Foo() { diff --git a/Test/test0/Split/Split.bpl b/Test/test0/Split/Split.bpl index 62e6b49fd..e99d76351 100644 --- a/Test/test0/Split/Split.bpl +++ b/Test/test0/Split/Split.bpl @@ -1,11 +1,9 @@ -// RUN: %parallel-boogie /vcsDumpSplits "%s" > "%t" -// RUN: mv %/S/Foo.split*.bpl %/S/Output/ -// RUN: mv %/S/Foo.split*.dot %/S/Output/ +// RUN: %parallel-boogie /printSplit:%t "%s" > "%t" // RUN: %diff "%s.expect" "%t" -// RUN: %diff %S/Foo.split.0.bpl.expect ./Output/Foo.split.0.bpl -// RUN: %diff %S/Foo.split.1.bpl.expect ./Output/Foo.split.1.bpl -// RUN: %diff %S/Foo.split.2.bpl.expect ./Output/Foo.split.2.bpl -// RUN: %diff %S/Foo.split.3.bpl.expect ./Output/Foo.split.3.bpl +// RUN: %diff %S/Foo.split.0.bpl.expect %t-Foo-0.spl +// RUN: %diff %S/Foo.split.1.bpl.expect %t-Foo-1.spl +// RUN: %diff %S/Foo.split.2.bpl.expect %t-Foo-2.spl +// RUN: %diff %S/Foo.split.3.bpl.expect %t-Foo-3.spl procedure Foo() returns (y: int) ensures y >= 0; From c4a034242c1fe2f3e00f88cbe173f33551e03a70 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 16:02:27 +0200 Subject: [PATCH 04/42] Update tests --- Test/pruning/MonomorphicSplit.bpl | 6 +++--- Test/pruning/UsesClauses.bpl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Test/pruning/MonomorphicSplit.bpl b/Test/pruning/MonomorphicSplit.bpl index 6ce0fc954..15351bb11 100644 --- a/Test/pruning/MonomorphicSplit.bpl +++ b/Test/pruning/MonomorphicSplit.bpl @@ -1,5 +1,5 @@ -// RUN: %parallel-boogie /prune:1 /errorTrace:0 /printPruned:"%t" "%s" > "%t" -// RUN: %OutputCheck "%s" --file-to-check="%t-after#0-monomorphicSplit.bpl" +// RUN: %parallel-boogie /prune:1 /errorTrace:0 /printSplit:"%t" /printSplitDeclarations "%s" > "%t" +// RUN: %OutputCheck "%s" --file-to-check="%t-monomorphicSplit--1.spl" // The following checks are a bit simplistic, but this is // on purpose to reduce brittleness. We assume there would now be two uses clauses @@ -7,7 +7,7 @@ // the instantiations of the original axiom. // // Last CHECK-NOT is for ensuring definition axioms are not printed outside -// uses clauses when using /printPruned. +// uses clauses when using /printSplitDeclarations. // CHECK-L: uses // CHECK-NEXT-L: axiom diff --git a/Test/pruning/UsesClauses.bpl b/Test/pruning/UsesClauses.bpl index 0ebe6cfe8..95ae69226 100644 --- a/Test/pruning/UsesClauses.bpl +++ b/Test/pruning/UsesClauses.bpl @@ -1,4 +1,4 @@ -// RUN: %parallel-boogie /prune:1 /printPruned:"%tpruned" /errorTrace:0 "%s" > "%t" +// RUN: %parallel-boogie /prune:1 /printSplit:"%t" /printSplitDeclarations /errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" // UNSUPPORTED: batch_mode From b89dde0fbebe64b3faab2645349f4d35b2642a4f Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 17:15:25 +0200 Subject: [PATCH 05/42] Add back /print --- Source/ExecutionEngine/ExecutionEngine.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index df60a98ca..e69a2639a 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -136,6 +136,10 @@ public async Task ProcessProgram(TextWriter output, Program program, strin programId = "main_program_id"; } + if (Options.PrintFile != null) { + PrintBplFile(Options.PrintFile, program, false, true, Options.PrettyPrint); + } + PipelineOutcome outcome = ResolveAndTypecheck(program, bplFileName, out var civlTypeChecker); if (outcome != PipelineOutcome.ResolvedAndTypeChecked) { return true; From 70ec2a0c6b262d59b7466a731b6d291adfca5016 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 17:40:24 +0200 Subject: [PATCH 06/42] Optimize blocks --- Source/Core/AST/AbsyCmd.cs | 7 +- Source/ExecutionEngine/ExecutionEngine.cs | 4 +- Source/ExecutionEngine/VerificationTask.cs | 2 +- Source/VCGeneration/BlockTransformations.cs | 150 ++++++++++++++++-- Source/VCGeneration/FocusAttribute.cs | 8 +- Source/VCGeneration/ManualSplit.cs | 5 +- Source/VCGeneration/ManualSplitFinder.cs | 79 +++++++-- .../VCGeneration/OldBlockTransformations.cs | 9 ++ Source/VCGeneration/Split.cs | 32 ++-- Source/VCGeneration/SplitAndVerifyWorker.cs | 3 +- .../VerificationConditionGenerator.cs | 2 +- 11 files changed, 239 insertions(+), 62 deletions(-) create mode 100644 Source/VCGeneration/OldBlockTransformations.cs diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 3583e68b5..3a0bc2f09 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -3497,7 +3497,7 @@ void ObjectInvariant() { Contract.Invariant(labelNames == null || labelTargets == null || labelNames.Count == labelTargets.Count); } - + [NotDelayed] public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq) : base(tok) @@ -3539,6 +3539,11 @@ public GotoCmd(IToken /*!*/ tok, List /*!*/ blockSeq) this.labelTargets = blockSeq; } + public void RemoveTarget(Block b) { + labelNames.Remove(b.Label); + labelTargets.Remove(b); + } + public void AddTarget(Block b) { Contract.Requires(b != null); diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index e69a2639a..4dcfdb023 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -732,11 +732,13 @@ public IReadOnlyList GetVerificationTasks(Program program) Inline(program); var processedProgram = PreProcessProgramVerification(program); + return GetPrioritizedImplementations(program).SelectMany(implementation => { var writer = TextWriter.Null; var vcGenerator = new VerificationConditionGenerator(processedProgram.Program, CheckerPool); + var run = new ImplementationRun(implementation, writer); var collector = new VerificationResultCollector(Options); vcGenerator.PrepareImplementation(run, collector, out _, @@ -744,7 +746,7 @@ public IReadOnlyList GetVerificationTasks(Program program) out var modelViewInfo); ConditionGeneration.ResetPredecessors(run.Implementation.Blocks); - var splits = ManualSplitFinder.FocusAndSplit(Options, run, gotoCmdOrigins, vcGenerator).ToList(); + var splits = ManualSplitFinder.FocusAndSplit(program, Options, run, gotoCmdOrigins, vcGenerator).ToList(); for (var index = 0; index < splits.Count; index++) { var split = splits[index]; split.SplitIndex = index; diff --git a/Source/ExecutionEngine/VerificationTask.cs b/Source/ExecutionEngine/VerificationTask.cs index 297d7619d..d61e7cd18 100644 --- a/Source/ExecutionEngine/VerificationTask.cs +++ b/Source/ExecutionEngine/VerificationTask.cs @@ -44,7 +44,7 @@ public VerificationTask(ExecutionEngine engine, ProcessedProgram processedProgra public IVerificationTask FromSeed(int newSeed) { - var split = new ManualSplit(Split.Options, Split.Blocks, Split.GotoCmdOrigins, + var split = new ManualSplit(Split.Options, () => Split.Blocks, Split.GotoCmdOrigins, Split.parent, Split.Run, Split.Token, newSeed); split.SplitIndex = Split.SplitIndex; return new VerificationTask(engine, ProcessedProgram, split, modelViewInfo); diff --git a/Source/VCGeneration/BlockTransformations.cs b/Source/VCGeneration/BlockTransformations.cs index 9e9606ecf..9a423f437 100644 --- a/Source/VCGeneration/BlockTransformations.cs +++ b/Source/VCGeneration/BlockTransformations.cs @@ -1,16 +1,45 @@ +using System; +using System.Collections; using System.Collections.Generic; -using System.Diagnostics.Contracts; +using System.Collections.Immutable; using System.Linq; +using System.Reactive; using Microsoft.Boogie; +using Microsoft.Boogie.GraphUtil; +using VCGeneration.Prune; namespace VCGeneration; -public static class BlockTransformations -{ - public static List DeleteNoAssertionBlocks(List blocks) +public static class BlockTransformations { + + public static void Optimize(List blocks) { + foreach (var block in blocks) + { + // make blocks ending in assume false leaves of the CFG-DAG + StopControlFlowAtAssumeFalse(block); + } + + DeleteBlocksNotLeadingToAssertions(blocks); + DeleteUselessBlocks(blocks); + MergeOneToOneBlocks(blocks); + } + + private static void StopControlFlowAtAssumeFalse(Block b) { + var firstFalseIdx = b.Cmds.FindIndex(IsAssumeFalse); + if (firstFalseIdx == -1) + { + return; + } + + b.Cmds = b.Cmds.Take(firstFalseIdx + 1).ToList(); + b.TransferCmd = b.TransferCmd is GotoCmd ? new ReturnCmd(b.tok) : b.TransferCmd; + } + + private static bool IsAssumeFalse (Cmd c) { return c is AssumeCmd { Expr: LiteralExpr { asBool: false } }; } - blocks.ForEach(StopControlFlowAtAssumeFalse); // make blocks ending in assume false leaves of the CFG-DAG -- this is probably unnecessary, may have been done previously + public static void DeleteBlocksNotLeadingToAssertions(List blocks) + { var todo = new Stack(); var peeked = new HashSet(); var interestingBlocks = new HashSet(); @@ -28,7 +57,6 @@ public static List DeleteNoAssertionBlocks(List blocks) if (currentBlock.TransferCmd is GotoCmd exit) { if (pop) { - Contract.Assert(pop); var gtc = new GotoCmd(exit.tok, exit.labelTargets.Where(l => interestingBlocks.Contains(l)).ToList()); currentBlock.TransferCmd = gtc; interesting = interesting || gtc.labelTargets.Count != 0; @@ -48,7 +76,9 @@ public static List DeleteNoAssertionBlocks(List blocks) } } interestingBlocks.Add(blocks[0]); // must not be empty - return blocks.Where(b => interestingBlocks.Contains(b)).ToList(); // this is not the same as interestingBlocks.ToList() because the resulting lists will have different orders. + var result = blocks.Where(b => interestingBlocks.Contains(b)).ToList(); // this is not the same as interestingBlocks.ToList() because the resulting lists will have different orders. + blocks.Clear(); + blocks.AddRange(result); } private static bool ContainsAssert(Block b) @@ -57,17 +87,103 @@ private static bool ContainsAssert(Block b) return b.Cmds.Exists(IsNonTrivialAssert); } - private static void StopControlFlowAtAssumeFalse(Block b) - { - var firstFalseIdx = b.Cmds.FindIndex(IsAssumeFalse); - if (firstFalseIdx == -1) - { - return; + private static void OptimizeBlocks(List blocks) { + DeleteUselessBlocks(blocks); + MergeOneToOneBlocks(blocks); + } + + private static void DeleteUselessBlocks(List blocks) { + var toVisit = new HashSet(); + var removed = new HashSet(); + foreach (var block in blocks) { + toVisit.Add(block); } + while(toVisit.Count > 0) { + var block = toVisit.First(); + toVisit.Remove(block); + if (removed.Contains(block)) { + continue; + } + if (block.Cmds.Any()) { + continue; + } - b.Cmds = b.Cmds.Take(firstFalseIdx + 1).ToList(); - b.TransferCmd = b.TransferCmd is GotoCmd ? new ReturnCmd(b.tok) : b.TransferCmd; + var isBranchingBlock = block.TransferCmd is GotoCmd gotoCmd1 && gotoCmd1.labelTargets.Count > 1 && + block.Predecessors.Count != 1; + if (isBranchingBlock) { + continue; + } + + removed.Add(block); + blocks.Remove(block); + + var noPredecessors = !block.Predecessors.Any(); + var noSuccessors = block.TransferCmd is not GotoCmd outGoto2 || !outGoto2.labelTargets.Any(); + foreach (var predecessor in block.Predecessors) { + var intoCmd = (GotoCmd)predecessor.TransferCmd; + intoCmd.RemoveTarget(block); + if (noSuccessors) { + toVisit.Add(predecessor); + } + } + + if (block.TransferCmd is not GotoCmd outGoto) { + continue; + } + + foreach (var outBlock in outGoto.labelTargets) { + outBlock.Predecessors.Remove(block); + if (noPredecessors) { + toVisit.Add(outBlock); + } + } + + foreach (var predecessor in block.Predecessors) { + var intoCmd = (GotoCmd)predecessor.TransferCmd; + foreach (var outBlock in outGoto.labelTargets) { + if (!intoCmd.labelTargets.Contains(outBlock)) { + intoCmd.AddTarget(outBlock); + outBlock.Predecessors.Add(predecessor); + } + } + } + } + } + + private static void MergeOneToOneBlocks(List blocks) { + var stack = new Stack(); + foreach (var block in blocks) { + if (!block.Predecessors.Any()) { + stack.Push(block); + } + } + while (stack.Any()) { + var current = stack.Pop(); + if (current.TransferCmd is not GotoCmd gotoCmd) { + continue; + } + + if (gotoCmd.labelTargets.Count != 1) { + foreach (var aNext in gotoCmd.labelTargets) { + stack.Push(aNext); + } + continue; + } + + var next = gotoCmd.labelTargets.Single(); + if (next.Predecessors.Count != 1) { + stack.Push(next); + continue; + } + + blocks.Remove(next); + current.Cmds.AddRange(next.Cmds); + current.TransferCmd = next.TransferCmd; + foreach (var nextTarget in current.Exits()) { + nextTarget.Predecessors.Remove(next); + nextTarget.Predecessors.Add(current); + } + stack.Push(current); + } } - - private static bool IsAssumeFalse (Cmd c) { return c is AssumeCmd { Expr: LiteralExpr { asBool: false } }; } } \ No newline at end of file diff --git a/Source/VCGeneration/FocusAttribute.cs b/Source/VCGeneration/FocusAttribute.cs index ebaeedf60..da5bac811 100644 --- a/Source/VCGeneration/FocusAttribute.cs +++ b/Source/VCGeneration/FocusAttribute.cs @@ -28,7 +28,7 @@ public static List FocusImpl(VCGenOptions options, ImplementationRu focusBlocks.Reverse(); } if (!focusBlocks.Any()) { - return new List { new(options, impl.Blocks, gotoCmdOrigins, par, run, run.Implementation.tok) }; + return new List { new(options, () => impl.Blocks, gotoCmdOrigins, par, run, run.Implementation.tok) }; } var ancestorsPerBlock = new Dictionary>(); @@ -71,7 +71,11 @@ void FocusRec(IToken focusToken, int focusIndex, IReadOnlyList blocksToIn } newBlocks.Reverse(); Contract.Assert(newBlocks[0] == oldToNewBlockMap[impl.Blocks[0]]); - result.Add(new ManualSplit(options, BlockTransformations.DeleteNoAssertionBlocks(newBlocks), gotoCmdOrigins, par, run, focusToken)); + result.Add(new ManualSplit(options, () => + { + BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); + return newBlocks; + }, gotoCmdOrigins, par, run, focusToken)); } else if (!blocksToInclude.Contains(focusBlocks[focusIndex].Block) || freeAssumeBlocks.Contains(focusBlocks[focusIndex].Block)) { diff --git a/Source/VCGeneration/ManualSplit.cs b/Source/VCGeneration/ManualSplit.cs index daa7c6bdc..09927d3c1 100644 --- a/Source/VCGeneration/ManualSplit.cs +++ b/Source/VCGeneration/ManualSplit.cs @@ -8,10 +8,9 @@ public class ManualSplit : Split { public IToken Token { get; } - - + public ManualSplit(VCGenOptions options, - List blocks, + Func> blocks, Dictionary gotoCmdOrigins, VerificationConditionGenerator par, ImplementationRun run, diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index 9d1804742..5b73a1266 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -1,23 +1,28 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; +using System.Threading.Tasks.Dataflow; using Microsoft.Boogie; using VC; namespace VCGeneration; public static class ManualSplitFinder { - public static IEnumerable FocusAndSplit(VCGenOptions options, ImplementationRun run, Dictionary gotoCmdOrigins, VerificationConditionGenerator par) { + public static IEnumerable FocusAndSplit(Program program, VCGenOptions options, ImplementationRun run, + Dictionary gotoCmdOrigins, VerificationConditionGenerator par) { var focussedImpl = FocusAttribute.FocusImpl(options, run, gotoCmdOrigins, par); - return focussedImpl.SelectMany(FindManualSplits); + return focussedImpl.SelectMany(s => FindManualSplits(program, s)); } - private static List FindManualSplits(ManualSplit initialSplit) { + private static List FindManualSplits(Program program, ManualSplit initialSplit) { Contract.Requires(initialSplit.Implementation != null); Contract.Ensures(Contract.Result>() == null || cce.NonNullElements(Contract.Result>())); var splitOnEveryAssert = initialSplit.Options.VcsSplitOnEveryAssert; initialSplit.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); + if (splitOnEveryAssert) { + return SplitEachAssert(program, initialSplit); + } var splitPoints = new Dictionary>(); foreach (var block in initialSplit.Blocks) { @@ -34,10 +39,12 @@ public static IEnumerable FocusAndSplit(VCGenOptions options, Imple Block entryPoint = initialSplit.Blocks[0]; var blockAssignments = PickBlocksToVerify(initialSplit.Blocks, splitPoints); var entryBlockHasSplit = splitPoints.ContainsKey(entryPoint); - var baseSplitBlocks = BlockTransformations.DeleteNoAssertionBlocks( - DoPreAssignedManualSplit(initialSplit.Options, initialSplit.Blocks, blockAssignments, - -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert)); - splits.Add(new ManualSplit(initialSplit.Options, baseSplitBlocks, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, initialSplit.Token)); + var firstSplitBlocks = DoPreAssignedManualSplit(initialSplit.Options, initialSplit.Blocks, blockAssignments, + -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert); + splits.Add(new ManualSplit(initialSplit.Options, () => { + BlockTransformations.Optimize(firstSplitBlocks); + return firstSplitBlocks; + }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, initialSplit.Token)); foreach (var block in initialSplit.Blocks) { var tokens = splitPoints.GetValueOrDefault(block); if (tokens == null) { @@ -49,13 +56,35 @@ public static IEnumerable FocusAndSplit(VCGenOptions options, Imple bool lastSplitInBlock = i == tokens.Count - 1; var newBlocks = DoPreAssignedManualSplit(initialSplit.Options, initialSplit.Blocks, blockAssignments, i, block, lastSplitInBlock, splitOnEveryAssert); splits.Add(new ManualSplit(initialSplit.Options, - BlockTransformations.DeleteNoAssertionBlocks(newBlocks), initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, token)); + () => { + BlockTransformations.Optimize(newBlocks); + return newBlocks; + }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, token)); } } } return splits; } + private static List SplitEachAssert(Program program, ManualSplit initialSplit) + { + var result = new List(); + foreach (var block in initialSplit.Blocks) { + foreach (Cmd command in block.Cmds) { + if (command is AssertCmd assertCmd) { + var newBlocks = SplitOnAssert(initialSplit.Options, initialSplit.Blocks, assertCmd); + result.Add(new ManualSplit(initialSplit.Options, + () => { + BlockTransformations.Optimize(newBlocks); + return newBlocks; + }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, assertCmd.tok)); + } + } + } + + return result; + } + private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { return c is PredicateCmd p && QKeyValue.FindBoolAttribute(p.Attributes, "split_here") || c is AssertCmd && splitOnEveryAssert; @@ -90,7 +119,26 @@ private static Dictionary PickBlocksToVerify(List blocks, D return blockAssignments; } - private static List DoPreAssignedManualSplit(VCGenOptions options, List blocks, Dictionary blockAssignments, int splitNumberWithinBlock, + private static List SplitOnAssert(VCGenOptions options, List oldBlocks, AssertCmd assertToKeep) { + var oldToNewBlockMap = new Dictionary(oldBlocks.Count); + + var newBlocks = new List(oldBlocks.Count); + foreach (var oldBlock in oldBlocks) { + var newBlock = new Block { + Label = oldBlock.Label, + tok = oldBlock.tok, + Cmds = oldBlock.Cmds.Select(cmd => + cmd != assertToKeep ? CommandTransformations.AssertIntoAssume(options, cmd) : cmd).ToList() + }; + oldToNewBlockMap[oldBlock] = newBlock; + newBlocks.Add(newBlock); + } + + AddBlockJumps(oldBlocks, oldToNewBlockMap); + return newBlocks; + } + private static List DoPreAssignedManualSplit(VCGenOptions options, List blocks, + Dictionary blockAssignments, int splitNumberWithinBlock, Block containingBlock, bool lastSplitInBlock, bool splitOnEveryAssert) { var newBlocks = new List(blocks.Count); // Copies of the original blocks //var duplicator = new Duplicator(); @@ -127,8 +175,16 @@ private static List DoPreAssignedManualSplit(VCGenOptions options, List oldBlocks, Dictionary oldToNewBlockMap) + { + foreach (var oldBlock in oldBlocks) { + var newBlock = oldToNewBlockMap[oldBlock]; + if (oldBlock.TransferCmd is ReturnCmd returnCmd) { + ((ReturnCmd)newBlock.TransferCmd).tok = returnCmd.tok; continue; } @@ -142,6 +198,5 @@ private static List DoPreAssignedManualSplit(VCGenOptions options, List Run.Implementation; - Dictionary /*!*/ - copies = new Dictionary(); + Dictionary /*!*/ copies = new(); bool doingSlice; double sliceInitialLimit; double sliceLimit; bool slicePos; - - HashSet /*!*/ protectedFromAssertToAssume = new HashSet(); - - HashSet /*!*/ keepAtAll = new HashSet(); + HashSet /*!*/ protectedFromAssertToAssume = new(); + HashSet /*!*/ keepAtAll = new(); // async interface public int SplitIndex { get; set; } public VerificationConditionGenerator.ErrorReporter reporter; - public Split(VCGenOptions options, List /*!*/ blocks, + public Split(VCGenOptions options, Func> /*!*/ getBlocks, Dictionary /*!*/ gotoCmdOrigins, VerificationConditionGenerator /*!*/ par, ImplementationRun run, int? randomSeed = null) { - Contract.Requires(cce.NonNullElements(blocks)); Contract.Requires(gotoCmdOrigins != null); Contract.Requires(par != null); - this.Blocks = blocks; + this.getBlocks = getBlocks; this.GotoCmdOrigins = gotoCmdOrigins; parent = par; this.Run = run; @@ -98,8 +94,6 @@ public Split(VCGenOptions options, List /*!*/ blocks, randomGen = new Random(RandomSeed); } - - public void PrintSplit() { if (Options.PrintSplitFile == null) { return; @@ -196,21 +190,13 @@ public void DumpDot(int splitNum) string filename = string.Format("{0}.split.{1}.bpl", Implementation.Name, splitNum); using (StreamWriter sw = File.CreateText(filename)) { - int oldPrintUnstructured = Options.PrintUnstructured; - Options.PrintUnstructured = 2; // print only the unstructured program - bool oldPrintDesugaringSetting = Options.PrintDesugarings; - Options.PrintDesugarings = false; - List backup = Implementation.Blocks; - Contract.Assert(backup != null); - Implementation.Blocks = Blocks; - Implementation.Emit(new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false, Options), 0); - Implementation.Blocks = backup; - Options.PrintDesugarings = oldPrintDesugaringSetting; - Options.PrintUnstructured = oldPrintUnstructured; + var writer = new TokenTextWriter(filename, sw, /*setTokens=*/ false, /*pretty=*/ false, Options); + Implementation.Emit(writer, 0); } } int bsid; + private readonly Func> getBlocks; BlockStats GetBlockStats(Block b) { @@ -697,7 +683,7 @@ private Split DoSplit() } } - return new Split(Options, newBlocks, newGotoCmdOrigins, parent, Run); + return new Split(Options, () => newBlocks, newGotoCmdOrigins, parent, Run); } private Split SplitAt(int idx) diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 63b763de0..7926edaa0 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -35,6 +35,7 @@ public class SplitAndVerifyWorker private int totalResourceCount; public SplitAndVerifyWorker( + Program program, VCGenOptions options, VerificationConditionGenerator verificationConditionGenerator, ImplementationRun run, @@ -64,7 +65,7 @@ public SplitAndVerifyWorker( ResetPredecessors(Implementation.Blocks); - ManualSplits = ManualSplitFinder.FocusAndSplit(options, run, gotoCmdOrigins, verificationConditionGenerator).ToList(); + ManualSplits = ManualSplitFinder.FocusAndSplit(program, options, run, gotoCmdOrigins, verificationConditionGenerator).ToList(); if (ManualSplits.Count == 1 && maxSplits > 1) { diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index bdff1b0f2..d9f14c4c7 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -407,7 +407,7 @@ public override async Task VerifyImplementation(ImplementationRun run } } - var worker = new SplitAndVerifyWorker(Options, this, run, dataGotoCmdOrigins, callback, + var worker = new SplitAndVerifyWorker(program, Options, this, run, dataGotoCmdOrigins, callback, dataModelViewInfo, vcOutcome); vcOutcome = await worker.WorkUntilDone(cancellationToken); From 51d1322b3d9c3798dbe1e953c9717f40108d48e6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 17:43:52 +0200 Subject: [PATCH 07/42] Update expect file --- Test/pruning/Focus.bpl.expect | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/pruning/Focus.bpl.expect b/Test/pruning/Focus.bpl.expect index d44953784..71c2b6338 100644 --- a/Test/pruning/Focus.bpl.expect +++ b/Test/pruning/Focus.bpl.expect @@ -1,3 +1,3 @@ -Focus.bpl(21,5): Error: this assertion could not be proved +Focus.bpl(15,5): Error: this assertion could not be proved Boogie program verifier finished with 1 verified, 1 error From 8393fce86cf676f502c33c002b70f5e017272864 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 17:44:22 +0200 Subject: [PATCH 08/42] Update expect file --- Test/smoke/smoke0.bpl.expect | 1 - 1 file changed, 1 deletion(-) diff --git a/Test/smoke/smoke0.bpl.expect b/Test/smoke/smoke0.bpl.expect index fff67ab89..e5eac3744 100644 --- a/Test/smoke/smoke0.bpl.expect +++ b/Test/smoke/smoke0.bpl.expect @@ -3,7 +3,6 @@ implementation b(x: int) { var y: int; - 0: goto anon0; From 1a161c8f832ec6ebe052f7d99cdc64bdc54b1da6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 17:45:41 +0200 Subject: [PATCH 09/42] Update split expect files --- Test/test0/Split/Foo.split.0.bpl.expect | 7 ------- Test/test0/Split/Foo.split.1.bpl.expect | 7 ------- Test/test0/Split/Foo.split.2.bpl.expect | 7 ------- Test/test0/Split/Foo.split.3.bpl.expect | 7 ------- Test/test0/Split/Foo.split.4.bpl.expect | 7 ------- Test/test0/Split/Split.bpl.expect | 8 ++++---- 6 files changed, 4 insertions(+), 39 deletions(-) diff --git a/Test/test0/Split/Foo.split.0.bpl.expect b/Test/test0/Split/Foo.split.0.bpl.expect index 805b56057..d00176cde 100644 --- a/Test/test0/Split/Foo.split.0.bpl.expect +++ b/Test/test0/Split/Foo.split.0.bpl.expect @@ -1,12 +1,5 @@ implementation Foo() returns (y: int) { - var x: int; - var x#AT#0: int; - var y#AT#0: int; - var x#AT#1: int; - var y#AT#1: int; - var y#AT#2: int; - PreconditionGeneratedEntry: goto anon0; diff --git a/Test/test0/Split/Foo.split.1.bpl.expect b/Test/test0/Split/Foo.split.1.bpl.expect index 16db74aec..bc4ea17f5 100644 --- a/Test/test0/Split/Foo.split.1.bpl.expect +++ b/Test/test0/Split/Foo.split.1.bpl.expect @@ -1,12 +1,5 @@ implementation Foo() returns (y: int) { - var x: int; - var x#AT#0: int; - var y#AT#0: int; - var x#AT#1: int; - var y#AT#1: int; - var y#AT#2: int; - PreconditionGeneratedEntry: goto anon0; diff --git a/Test/test0/Split/Foo.split.2.bpl.expect b/Test/test0/Split/Foo.split.2.bpl.expect index 3791060d5..b07405c80 100644 --- a/Test/test0/Split/Foo.split.2.bpl.expect +++ b/Test/test0/Split/Foo.split.2.bpl.expect @@ -1,12 +1,5 @@ implementation Foo() returns (y: int) { - var x: int; - var x#AT#0: int; - var y#AT#0: int; - var x#AT#1: int; - var y#AT#1: int; - var y#AT#2: int; - PreconditionGeneratedEntry: goto anon0; diff --git a/Test/test0/Split/Foo.split.3.bpl.expect b/Test/test0/Split/Foo.split.3.bpl.expect index 405af4d45..9ef24510f 100644 --- a/Test/test0/Split/Foo.split.3.bpl.expect +++ b/Test/test0/Split/Foo.split.3.bpl.expect @@ -1,12 +1,5 @@ implementation Foo() returns (y: int) { - var x: int; - var x#AT#0: int; - var y#AT#0: int; - var x#AT#1: int; - var y#AT#1: int; - var y#AT#2: int; - PreconditionGeneratedEntry: goto anon0; diff --git a/Test/test0/Split/Foo.split.4.bpl.expect b/Test/test0/Split/Foo.split.4.bpl.expect index 03719366f..83b9850a7 100644 --- a/Test/test0/Split/Foo.split.4.bpl.expect +++ b/Test/test0/Split/Foo.split.4.bpl.expect @@ -1,12 +1,5 @@ implementation Foo() returns (y: int) { - var x: int; - var x#AT#0: int; - var y#AT#0: int; - var x#AT#1: int; - var y#AT#1: int; - var y#AT#2: int; - PreconditionGeneratedEntry: goto anon0; diff --git a/Test/test0/Split/Split.bpl.expect b/Test/test0/Split/Split.bpl.expect index d80dfe031..ad3ef1049 100644 --- a/Test/test0/Split/Split.bpl.expect +++ b/Test/test0/Split/Split.bpl.expect @@ -1,7 +1,7 @@ -Split.bpl(21,5): Error: this assertion could not be proved +Split.bpl(19,5): Error: this assertion could not be proved Execution trace: - Split.bpl(14,5): anon0 - Split.bpl(16,3): anon5_LoopHead - Split.bpl(20,7): anon5_LoopBody + Split.bpl(12,5): anon0 + Split.bpl(14,3): anon5_LoopHead + Split.bpl(18,7): anon5_LoopBody Boogie program verifier finished with 0 verified, 1 error From 8bed7e575166c359ba3b700b582b16aba8fb9bbf Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 17:50:23 +0200 Subject: [PATCH 10/42] Fix bug and update test --- Source/VCGeneration/Split.cs | 8 +++----- Test/test0/AssumeFalseSplit/Foo.split.0.bpl.expect | 3 --- Test/test0/AssumeFalseSplit/Foo.split.1.bpl.expect | 3 --- Test/test0/AssumeFalseSplit/Foo.split.2.bpl.expect | 3 --- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index 149153ad4..7cf747118 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Threading; @@ -11,7 +10,6 @@ using Microsoft.Boogie.VCExprAST; using Microsoft.Boogie.SMTLib; using System.Threading.Tasks; -using VCGeneration; namespace VC { @@ -23,10 +21,11 @@ public class Split : ProofRun public int RandomSeed { get; } - public List Blocks { get; set; } + private List blocks; + public List Blocks => blocks ??= getBlocks(); + readonly List bigBlocks = new(); public List Asserts => Blocks.SelectMany(block => block.cmds.OfType()).ToList(); - public readonly IReadOnlyList TopLevelDeclarations; public IReadOnlyList prunedDeclarations; public IReadOnlyList PrunedDeclarations { @@ -89,7 +88,6 @@ public Split(VCGenOptions options, Func> /*!*/ getBlocks, this.Options = options; Interlocked.Increment(ref currentId); - TopLevelDeclarations = par.program.TopLevelDeclarations; RandomSeed = randomSeed ?? Implementation.RandomSeed ?? Options.RandomSeed ?? 0; randomGen = new Random(RandomSeed); } diff --git a/Test/test0/AssumeFalseSplit/Foo.split.0.bpl.expect b/Test/test0/AssumeFalseSplit/Foo.split.0.bpl.expect index c03efb956..c8727ad27 100644 --- a/Test/test0/AssumeFalseSplit/Foo.split.0.bpl.expect +++ b/Test/test0/AssumeFalseSplit/Foo.split.0.bpl.expect @@ -1,9 +1,6 @@ implementation Foo() { - anon0: - goto anon3_Then; - anon3_Then: assert 2 == 1 + 1; assume false; diff --git a/Test/test0/AssumeFalseSplit/Foo.split.1.bpl.expect b/Test/test0/AssumeFalseSplit/Foo.split.1.bpl.expect index 84c8ec7ce..29ba05d16 100644 --- a/Test/test0/AssumeFalseSplit/Foo.split.1.bpl.expect +++ b/Test/test0/AssumeFalseSplit/Foo.split.1.bpl.expect @@ -1,9 +1,6 @@ implementation Foo() { - anon0: - goto anon3_Else; - anon3_Else: assume 2 == 1 + 1; assert {:split_here} 2 == 2; diff --git a/Test/test0/AssumeFalseSplit/Foo.split.2.bpl.expect b/Test/test0/AssumeFalseSplit/Foo.split.2.bpl.expect index 46590b6d5..838d5aebd 100644 --- a/Test/test0/AssumeFalseSplit/Foo.split.2.bpl.expect +++ b/Test/test0/AssumeFalseSplit/Foo.split.2.bpl.expect @@ -1,9 +1,6 @@ implementation Foo() { - anon0: - goto anon3_Else; - anon3_Else: assume 2 == 1 + 1; assume 2 == 2; From e98b27e44f68f10abf7c061511c09578a9dfd5ab Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 18:16:03 +0200 Subject: [PATCH 11/42] Move deadVarElim.cs classes into separate files --- Source/Core/Analysis/BlockCoalescer.cs | 165 ++ .../LiveVariableAnalysis/GenKillWeight.cs | 191 ++ .../ImplementationControlFlowGraph.cs | 219 ++ .../LiveVariableAnalysis/InterProcGenKill.cs | 1058 ++++++++ .../LiveVariableAnalysis.cs | 213 ++ Source/Core/Analysis/ModSetCollector.cs | 223 ++ .../Core/Analysis/MutableVariableCollector.cs | 31 + Source/Core/Analysis/UnusedVarEliminator.cs | 46 + Source/Core/Analysis/VariableCollector.cs | 82 + Source/Core/DeadVarElim.cs | 2194 ----------------- 10 files changed, 2228 insertions(+), 2194 deletions(-) create mode 100644 Source/Core/Analysis/BlockCoalescer.cs create mode 100644 Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs create mode 100644 Source/Core/Analysis/LiveVariableAnalysis/ImplementationControlFlowGraph.cs create mode 100644 Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs create mode 100644 Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs create mode 100644 Source/Core/Analysis/ModSetCollector.cs create mode 100644 Source/Core/Analysis/MutableVariableCollector.cs create mode 100644 Source/Core/Analysis/UnusedVarEliminator.cs create mode 100644 Source/Core/Analysis/VariableCollector.cs delete mode 100644 Source/Core/DeadVarElim.cs diff --git a/Source/Core/Analysis/BlockCoalescer.cs b/Source/Core/Analysis/BlockCoalescer.cs new file mode 100644 index 000000000..959ff9275 --- /dev/null +++ b/Source/Core/Analysis/BlockCoalescer.cs @@ -0,0 +1,165 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class BlockCoalescer : ReadOnlyVisitor +{ + public static void CoalesceBlocks(Program program) + { + Contract.Requires(program != null); + BlockCoalescer blockCoalescer = new BlockCoalescer(); + blockCoalescer.Visit(program); + } + + private static HashSet /*!*/ ComputeMultiPredecessorBlocks(Implementation /*!*/ impl) + { + Contract.Requires(impl != null); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + HashSet visitedBlocks = new HashSet(); + HashSet multiPredBlocks = new HashSet(); + Stack dfsStack = new Stack(); + dfsStack.Push(impl.Blocks[0]); + while (dfsStack.Count > 0) + { + Block /*!*/ + b = dfsStack.Pop(); + Contract.Assert(b != null); + if (visitedBlocks.Contains(b)) + { + multiPredBlocks.Add(b); + continue; + } + + visitedBlocks.Add(b); + if (b.TransferCmd == null) + { + continue; + } + + if (b.TransferCmd is ReturnCmd) + { + continue; + } + + Contract.Assert(b.TransferCmd is GotoCmd); + GotoCmd gotoCmd = (GotoCmd) b.TransferCmd; + if (gotoCmd.labelTargets == null) + { + continue; + } + + foreach (Block /*!*/ succ in gotoCmd.labelTargets) + { + Contract.Assert(succ != null); + dfsStack.Push(succ); + } + } + + return multiPredBlocks; + } + + public override Implementation VisitImplementation(Implementation impl) + { + //Contract.Requires(impl != null); + Contract.Ensures(Contract.Result() != null); + //Console.WriteLine("Procedure {0}", impl.Name); + //Console.WriteLine("Initial number of blocks = {0}", impl.Blocks.Count); + + HashSet multiPredBlocks = ComputeMultiPredecessorBlocks(impl); + Contract.Assert(cce.NonNullElements(multiPredBlocks)); + HashSet visitedBlocks = new HashSet(); + HashSet removedBlocks = new HashSet(); + Stack dfsStack = new Stack(); + dfsStack.Push(impl.Blocks[0]); + while (dfsStack.Count > 0) + { + Block /*!*/ + b = dfsStack.Pop(); + Contract.Assert(b != null); + if (visitedBlocks.Contains(b)) + { + continue; + } + + visitedBlocks.Add(b); + if (b.TransferCmd == null) + { + continue; + } + + if (b.TransferCmd is ReturnCmd) + { + continue; + } + + Contract.Assert(b.TransferCmd is GotoCmd); + GotoCmd gotoCmd = (GotoCmd) b.TransferCmd; + if (gotoCmd.labelTargets == null) + { + continue; + } + + if (gotoCmd.labelTargets.Count == 1) + { + Block /*!*/ + succ = cce.NonNull(gotoCmd.labelTargets[0]); + if (!multiPredBlocks.Contains(succ)) + { + foreach (Cmd /*!*/ cmd in succ.Cmds) + { + Contract.Assert(cmd != null); + b.Cmds.Add(cmd); + } + + b.TransferCmd = succ.TransferCmd; + if (!b.tok.IsValid && succ.tok.IsValid) + { + b.tok = succ.tok; + b.Label = succ.Label; + } + + removedBlocks.Add(succ); + dfsStack.Push(b); + visitedBlocks.Remove(b); + continue; + } + } + + foreach (Block /*!*/ succ in gotoCmd.labelTargets) + { + Contract.Assert(succ != null); + dfsStack.Push(succ); + } + } + + List newBlocks = new List(); + foreach (Block /*!*/ b in impl.Blocks) + { + Contract.Assert(b != null); + if (visitedBlocks.Contains(b) && !removedBlocks.Contains(b)) + { + newBlocks.Add(b); + } + } + + impl.Blocks = newBlocks; + foreach (Block b in impl.Blocks) + { + if (b.TransferCmd is ReturnCmd) + { + continue; + } + + GotoCmd gotoCmd = b.TransferCmd as GotoCmd; + gotoCmd.labelNames = new List(); + foreach (Block succ in gotoCmd.labelTargets) + { + gotoCmd.labelNames.Add(succ.Label); + } + } + + // Console.WriteLine("Final number of blocks = {0}", impl.Blocks.Count); + return impl; + } +} \ No newline at end of file diff --git a/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs b/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs new file mode 100644 index 000000000..3a4dc7288 --- /dev/null +++ b/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs @@ -0,0 +1,191 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie +{ + /* + // An idempotent semiring interface + abstract public class Weight { + abstract public Weight! one(); + abstract public Weight! zero(); + abstract public Weight! extend(Weight! w1, Weight! w2); + abstract public Weight! combine(Weight! w1, Weight! w2); + abstract public Weight! isEqual(Weight! w); + abstract public Weight! projectLocals() + } + */ + + // Weight domain for LiveVariableAnalysis (Gen/Kill) + + public class GenKillWeight + { + // lambda S. (S - kill) union gen + HashSet /*!*/ + gen; + + HashSet /*!*/ + kill; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cce.NonNullElements(gen)); + Contract.Invariant(cce.NonNullElements(kill)); + Contract.Invariant(oneWeight != null); + Contract.Invariant(zeroWeight != null); + } + + bool isZero; + + public static GenKillWeight /*!*/ + oneWeight = new GenKillWeight(new HashSet(), new HashSet()); + + public static GenKillWeight /*!*/ + zeroWeight = new GenKillWeight(); + + // initializes to zero + public GenKillWeight() + { + this.isZero = true; + this.gen = new HashSet(); + this.kill = new HashSet(); + } + + public GenKillWeight(HashSet gen, HashSet kill) + { + Contract.Requires(cce.NonNullElements(gen)); + Contract.Requires(cce.NonNullElements(kill)); + Contract.Assert(gen != null); + Contract.Assert(kill != null); + this.gen = gen; + this.kill = kill; + this.isZero = false; + } + + public static GenKillWeight one() + { + Contract.Ensures(Contract.Result() != null); + return oneWeight; + } + + public static GenKillWeight zero() + { + Contract.Ensures(Contract.Result() != null); + return zeroWeight; + } + + public static GenKillWeight extend(GenKillWeight w1, GenKillWeight w2) + { + Contract.Requires(w2 != null); + Contract.Requires(w1 != null); + Contract.Ensures(Contract.Result() != null); + if (w1.isZero || w2.isZero) + { + return zero(); + } + + HashSet t = new HashSet(w2.gen); + t.ExceptWith(w1.kill); + HashSet g = new HashSet(w1.gen); + g.UnionWith(t); + HashSet k = new HashSet(w1.kill); + k.UnionWith(w2.kill); + return new GenKillWeight(g, k); + //return new GenKillWeight(w1.gen.Union(w2.gen.Difference(w1.kill)), w1.kill.Union(w2.kill)); + } + + public static GenKillWeight combine(GenKillWeight w1, GenKillWeight w2) + { + Contract.Requires(w2 != null); + Contract.Requires(w1 != null); + Contract.Ensures(Contract.Result() != null); + if (w1.isZero) + { + return w2; + } + + if (w2.isZero) + { + return w1; + } + + HashSet g = new HashSet(w1.gen); + g.UnionWith(w2.gen); + HashSet k = new HashSet(w1.kill); + k.IntersectWith(w2.kill); + return new GenKillWeight(g, k); + //return new GenKillWeight(w1.gen.Union(w2.gen), w1.kill.Intersection(w2.kill)); + } + + public static GenKillWeight projectLocals(GenKillWeight w) + { + Contract.Requires(w != null); + Contract.Ensures(Contract.Result() != null); + HashSet gen = new HashSet(); + foreach (Variable v in w.gen) + { + if (isGlobal(v)) + { + gen.Add(v); + } + } + + HashSet kill = new HashSet(); + foreach (Variable v in w.kill) + { + if (isGlobal(v)) + { + kill.Add(v); + } + } + + return new GenKillWeight(gen, kill); + } + + public static bool isEqual(GenKillWeight w1, GenKillWeight w2) + { + Contract.Requires(w2 != null); + Contract.Requires(w1 != null); + if (w1.isZero) + { + return w2.isZero; + } + + if (w2.isZero) + { + return w1.isZero; + } + + return (w1.gen.Equals(w2.gen) && w1.kill.Equals(w2.kill)); + } + + private static bool isGlobal(Variable v) + { + Contract.Requires(v != null); + return (v is GlobalVariable); + } + + [Pure] + public override string ToString() + { + Contract.Ensures(Contract.Result() != null); + return string.Format("({0},{1})", gen.ToString(), kill.ToString()); + } + + public HashSet /*!*/ getLiveVars() + { + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + return gen; + } + + public HashSet /*!*/ getLiveVars(HashSet /*!*/ lv) + { + Contract.Requires(cce.NonNullElements(lv)); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + HashSet temp = new HashSet(lv); + temp.ExceptWith(kill); + temp.UnionWith(gen); + return temp; + } + } +} \ No newline at end of file diff --git a/Source/Core/Analysis/LiveVariableAnalysis/ImplementationControlFlowGraph.cs b/Source/Core/Analysis/LiveVariableAnalysis/ImplementationControlFlowGraph.cs new file mode 100644 index 000000000..9845609b0 --- /dev/null +++ b/Source/Core/Analysis/LiveVariableAnalysis/ImplementationControlFlowGraph.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using Microsoft.Boogie.GraphUtil; + +namespace Microsoft.Boogie; + +public class ImplementationControlFlowGraph +{ + public Graph /*!*/ + graph; + + // Map from procedure to the list of blocks that call that procedure + public Dictionary /*!*/> /*!*/ + procsCalled; + + public HashSet /*!*/ + nodes; + + public Dictionary /*!*/> /*!*/ + succEdges; + + public Dictionary /*!*/> /*!*/ + predEdges; + + private Dictionary /*!*/ + priority; + + public HashSet /*!*/ + srcNodes; + + public HashSet /*!*/ + exitNodes; + + public Dictionary /*!*/ + weightBefore; + + public Dictionary /*!*/ + weightAfter; + + public Dictionary /*!*/> /*!*/ + liveVarsAfter; + + public Dictionary /*!*/> /*!*/ + liveVarsBefore; + + public GenKillWeight /*!*/ + summary; + + private readonly CoreOptions options; + + public Implementation /*!*/ + impl; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cce.NonNullElements(graph.Nodes)); + Contract.Invariant(cce.NonNullDictionaryAndValues(procsCalled)); + Contract.Invariant(cce.NonNullElements(nodes)); + Contract.Invariant(cce.NonNullDictionaryAndValues(succEdges)); + Contract.Invariant(cce.NonNullDictionaryAndValues(predEdges)); + Contract.Invariant(priority != null); + Contract.Invariant(cce.NonNullElements(srcNodes)); + Contract.Invariant(cce.NonNullElements(exitNodes)); + Contract.Invariant(cce.NonNullDictionaryAndValues(weightBefore)); + Contract.Invariant(cce.NonNullDictionaryAndValues(weightAfter)); + Contract.Invariant(cce.NonNullDictionaryAndValues(liveVarsAfter)); + Contract.Invariant(cce.NonNullDictionaryAndValues(liveVarsBefore)); + Contract.Invariant(summary != null); + Contract.Invariant(impl != null); + } + + + [NotDelayed] + public ImplementationControlFlowGraph(CoreOptions options, Implementation impl) + { + Contract.Requires(impl != null); + this.graph = new Graph(); + this.procsCalled = new Dictionary /*!*/>(); + this.nodes = new HashSet(); + this.succEdges = new Dictionary /*!*/>(); + this.predEdges = new Dictionary /*!*/>(); + + this.priority = new Dictionary(); + + this.srcNodes = new HashSet(); + this.exitNodes = new HashSet(); + + this.weightBefore = new Dictionary(); + this.weightAfter = new Dictionary(); + this.liveVarsAfter = new Dictionary /*!*/>(); + this.liveVarsBefore = new Dictionary /*!*/>(); + + summary = GenKillWeight.zero(); + this.options = options; + this.impl = impl; + + Initialize(impl); + } + + private void Initialize(Implementation impl) + { + Contract.Requires(impl != null); + addSource(impl.Blocks[0]); + graph.AddSource(impl.Blocks[0]); + + foreach (Block /*!*/ b in impl.Blocks) + { + Contract.Assert(b != null); + if (b.TransferCmd is ReturnCmd) + { + exitNodes.Add(b); + } + else + { + GotoCmd gc = b.TransferCmd as GotoCmd; + Contract.Assert(gc != null); + Contract.Assert(gc.labelTargets != null); + foreach (Block /*!*/ t in gc.labelTargets) + { + Contract.Assert(t != null); + addEdge(b, t); + graph.AddEdge(b, t); + } + } + + weightBefore[b] = GenKillWeight.zero(); + weightAfter[b] = GenKillWeight.zero(); + + foreach (Cmd /*!*/ c in b.Cmds) + { + Contract.Assert(c != null); + if (c is CallCmd) + { + CallCmd /*!*/ + cc = cce.NonNull((CallCmd /*!*/) c); + Contract.Assert(cc.Proc != null); + string /*!*/ + procName = cc.Proc.Name; + Contract.Assert(procName != null); + if (!procsCalled.ContainsKey(procName)) + { + procsCalled.Add(procName, new List()); + } + + procsCalled[procName].Add(b); + } + } + } + + graph.TarjanTopSort(out var acyclic, out var sortedNodes); + + if (!acyclic) + { + options.OutputWriter.WriteLine("Warning: graph is not a dag"); + } + + int num = sortedNodes.Count; + foreach (Block /*!*/ b in sortedNodes) + { + Contract.Assert(b != null); + priority.Add(b, num); + num--; + } + } + + public int getPriority(Block b) + { + Contract.Requires(b != null); + if (priority.ContainsKey(b)) + { + return priority[b]; + } + + return Int32.MaxValue; + } + + private void addSource(Block b) + { + Contract.Requires(b != null); + registerNode(b); + this.srcNodes.Add(b); + } + + private void addExit(Block b) + { + Contract.Requires(b != null); + registerNode(b); + this.exitNodes.Add(b); + } + + private void registerNode(Block b) + { + Contract.Requires(b != null); + if (!succEdges.ContainsKey(b)) + { + succEdges.Add(b, new HashSet()); + } + + if (!predEdges.ContainsKey(b)) + { + predEdges.Add(b, new HashSet()); + } + + nodes.Add(b); + } + + private void addEdge(Block src, Block tgt) + { + Contract.Requires(tgt != null); + Contract.Requires(src != null); + registerNode(src); + registerNode(tgt); + + succEdges[src].Add(tgt); + predEdges[tgt].Add(src); + } +} \ No newline at end of file diff --git a/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs b/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs new file mode 100644 index 000000000..0d92163d7 --- /dev/null +++ b/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs @@ -0,0 +1,1058 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using Microsoft.Boogie.GraphUtil; + +namespace Microsoft.Boogie; + +public class InterProcGenKill +{ + private CoreOptions options; + Program /*!*/ program; + + Dictionary /*!*/ + procICFG; + + Dictionary /*!*/ + name2Proc; + + Dictionary /*!*/> /*!*/ + callers; + + Graph /*!*/ + callGraph; + + Dictionary /*!*/ + procPriority; + + int maxBlocksInProc; + + WorkList /*!*/ + workList; + + Implementation /*!*/ + mainImpl; + + static Dictionary /*!*/> /*!*/ + varsLiveAtExit = new Dictionary /*!*/>(); + + static Dictionary /*!*/> /*!*/ + varsLiveAtEntry = new Dictionary /*!*/>(); + + static Dictionary /*!*/ + varsLiveSummary = new Dictionary(); + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(workList != null); + Contract.Invariant(mainImpl != null); + Contract.Invariant(program != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(procICFG)); + Contract.Invariant(cce.NonNullDictionaryAndValues(name2Proc)); + Contract.Invariant(cce.NonNullDictionaryAndValues(callers) && + Contract.ForAll(callers.Values, v => cce.NonNullElements(v))); + Contract.Invariant(cce.NonNullElements(callGraph.Nodes)); + Contract.Invariant(procPriority != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveAtEntry)); + Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveAtExit) && + Contract.ForAll(varsLiveAtExit.Values, v => cce.NonNullElements(v))); + Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveSummary)); + Contract.Invariant(cce.NonNullDictionaryAndValues(weightCacheAfterCall)); + Contract.Invariant(cce.NonNullDictionaryAndValues(weightCacheBeforeCall)); + } + + + [NotDelayed] + public InterProcGenKill(Implementation impl, Program program, CoreOptions options) + { + Contract.Requires(program != null); + Contract.Requires(impl != null); + this.program = program; + this.options = options; + procICFG = new Dictionary(); + name2Proc = new Dictionary(); + workList = new WorkList(); + this.callers = new Dictionary /*!*/>(); + this.callGraph = new Graph(); + this.procPriority = new Dictionary(); + this.maxBlocksInProc = 0; + this.mainImpl = impl; + + Dictionary /*!*/ + name2Impl = new Dictionary(); + varsLiveAtExit.Clear(); + varsLiveAtEntry.Clear(); + varsLiveSummary.Clear(); + + foreach (var decl in program.TopLevelDeclarations) + { + Contract.Assert(decl != null); + if (decl is Implementation) + { + Implementation /*!*/ + imp = (Implementation /*!*/) cce.NonNull(decl); + name2Impl[imp.Name] = imp; + } + else if (decl is Procedure) + { + Procedure /*!*/ + proc = cce.NonNull(decl as Procedure); + name2Proc[proc.Name] = proc; + } + } + + ImplementationControlFlowGraph /*!*/ + mainImplementationControlFlowGraph = new ImplementationControlFlowGraph(this.options, mainImpl); + Contract.Assert(mainImplementationControlFlowGraph != null); + procICFG.Add(mainImplementationControlFlowGraph.impl.Name, mainImplementationControlFlowGraph); + callGraph.AddSource(mainImplementationControlFlowGraph.impl.Name); + + List /*!*/ + procsToConsider = new List(); + procsToConsider.Add(mainImplementationControlFlowGraph); + + while (procsToConsider.Count != 0) + { + ImplementationControlFlowGraph /*!*/ + p = procsToConsider[0]; + Contract.Assert(p != null); + procsToConsider.RemoveAt(0); + + foreach (string /*!*/ callee in p.procsCalled.Keys) + { + Contract.Assert(callee != null); + if (!name2Impl.ContainsKey(callee)) + { + continue; + } + + callGraph.AddEdge(p.impl.Name, callee); + + if (maxBlocksInProc < p.nodes.Count) + { + maxBlocksInProc = p.nodes.Count; + } + + if (!callers.ContainsKey(callee)) + { + callers.Add(callee, new List()); + } + + foreach (Block /*!*/ b in p.procsCalled[callee]) + { + Contract.Assert(b != null); + callers[callee].Add(new WorkItem(p, b)); + } + + if (procICFG.ContainsKey(callee)) + { + continue; + } + + ImplementationControlFlowGraph /*!*/ + ncfg = new ImplementationControlFlowGraph(this.options, name2Impl[callee]); + Contract.Assert(ncfg != null); + procICFG.Add(callee, ncfg); + procsToConsider.Add(ncfg); + } + } + + callGraph.TarjanTopSort(out var acyclic, out var sortedNodes); + + Contract.Assert(acyclic); + + int cnt = 0; + for (int i = sortedNodes.Count - 1; i >= 0; i--) + { + string s = sortedNodes[i]; + if (s == null) + { + continue; + } + + procPriority.Add(s, cnt); + cnt++; + } + } + + public static HashSet /*!*/ GetVarsLiveAtExit(Implementation impl, Program prog) + { + Contract.Requires(prog != null); + Contract.Requires(impl != null); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + if (varsLiveAtExit.ContainsKey(impl.Name)) + { + return varsLiveAtExit[impl.Name]; + } + + // Return default: all globals and out params + HashSet /*!*/ + lv = new HashSet(); + foreach (Variable /*!*/ v in prog.GlobalVariables) + { + Contract.Assert(v != null); + lv.Add(v); + } + + foreach (Variable /*!*/ v in impl.OutParams) + { + Contract.Assert(v != null); + lv.Add(v); + } + + return lv; + } + + public static HashSet /*!*/ GetVarsLiveAtEntry(Implementation impl, Program prog) + { + Contract.Requires(prog != null); + Contract.Requires(impl != null); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + if (varsLiveAtEntry.ContainsKey(impl.Name)) + { + return varsLiveAtEntry[impl.Name]; + } + + // Return default: all globals and in params + HashSet /*!*/ + lv = new HashSet(); + foreach (Variable /*!*/ v in prog.GlobalVariables) + { + Contract.Assert(v != null); + lv.Add(v); + } + + foreach (Variable /*!*/ v in impl.InParams) + { + Contract.Assert(v != null); + lv.Add(v); + } + + return lv; + } + + public static bool HasSummary(string name) + { + Contract.Requires(name != null); + return varsLiveSummary.ContainsKey(name); + } + + public static HashSet /*!*/ + PropagateLiveVarsAcrossCall(CoreOptions options, CallCmd cmd, HashSet /*!*/ lvAfter) + { + Contract.Requires(cmd != null); + Contract.Requires(cce.NonNullElements(lvAfter)); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + Procedure /*!*/ + proc = cce.NonNull(cmd.Proc); + if (varsLiveSummary.ContainsKey(proc.Name)) + { + GenKillWeight /*!*/ + w1 = getWeightBeforeCall(cmd); + Contract.Assert(w1 != null); + GenKillWeight /*!*/ + w2 = varsLiveSummary[proc.Name]; + Contract.Assert(w2 != null); + GenKillWeight /*!*/ + w3 = getWeightAfterCall(cmd); + Contract.Assert(w3 != null); + GenKillWeight /*!*/ + w = GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); + Contract.Assert(w != null); + return w.getLiveVars(lvAfter); + } + + HashSet /*!*/ + ret = new HashSet(); + ret.UnionWith(lvAfter); + new LiveVariableAnalysis(options).Propagate(cmd, ret); + return ret; + } + + class WorkItem + { + public ImplementationControlFlowGraph /*!*/ + cfg; + + public Block /*!*/ + block; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cfg != null); + Contract.Invariant(block != null); + } + + + public WorkItem(ImplementationControlFlowGraph cfg, Block block) + { + Contract.Requires(block != null); + Contract.Requires(cfg != null); + this.cfg = cfg; + this.block = block; + } + + public GenKillWeight getWeightAfter() + { + Contract.Ensures(Contract.Result() != null); + return cfg.weightAfter[block]; + } + + public bool setWeightBefore(GenKillWeight w) + { + Contract.Requires(w != null); + GenKillWeight /*!*/ + prev = cfg.weightBefore[block]; + Contract.Assert(prev != null); + GenKillWeight /*!*/ + curr = GenKillWeight.combine(w, prev); + Contract.Assert(curr != null); + if (GenKillWeight.isEqual(prev, curr)) + { + return false; + } + + cfg.weightBefore[block] = curr; + return true; + } + + [Pure] + [Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object other) + { + WorkItem /*!*/ + wi = (WorkItem /*!*/) cce.NonNull(other); + return (wi.cfg == cfg && wi.block == block); + } + + [Pure] + public override int GetHashCode() + { + return 0; + } + + public string getLabel() + { + Contract.Ensures(Contract.Result() != null); + return cfg.impl.Name + "::" + block.Label; + } + } + + private void AddToWorkList(WorkItem wi) + { + Contract.Requires(wi != null); + int i = procPriority[wi.cfg.impl.Name]; + int j = wi.cfg.getPriority(wi.block); + int priority = (i * maxBlocksInProc) + j; + + workList.Add(wi, priority); + } + + private void AddToWorkListReverse(WorkItem wi) + { + Contract.Requires(wi != null); + int i = procPriority[wi.cfg.impl.Name]; + int j = wi.cfg.getPriority(wi.block); + int priority = (procPriority.Count - i) * maxBlocksInProc + j; + workList.Add(wi, priority); + } + + class WorkList + { + SortedList /*!*/ + priorities; + + HashSet /*!*/ + labels; + + Dictionary /*!*/> /*!*/ + workList; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(priorities != null); + Contract.Invariant(cce.NonNullElements(labels)); + Contract.Invariant(cce.NonNullDictionaryAndValues(workList) && + Contract.ForAll(workList.Values, v => cce.NonNullElements(v))); + } + + + public WorkList() + { + labels = new HashSet(); + priorities = new SortedList(); + workList = new Dictionary /*!*/>(); + } + + public void Add(WorkItem wi, int priority) + { + Contract.Requires(wi != null); + string /*!*/ + lab = wi.getLabel(); + Contract.Assert(lab != null); + if (labels.Contains(lab)) + { + // Already on worklist + return; + } + + labels.Add(lab); + if (!workList.ContainsKey(priority)) + { + workList.Add(priority, new List()); + } + + workList[priority].Add(wi); + if (!priorities.ContainsKey(priority)) + { + priorities.Add(priority, 0); + } + + priorities[priority] = priorities[priority] + 1; + } + + public WorkItem Get() + { + Contract.Ensures(Contract.Result() != null); + // Get minimum priority + int p = cce.NonNull(priorities.Keys)[0]; + priorities[p] = priorities[p] - 1; + if (priorities[p] == 0) + { + priorities.Remove(p); + } + + // Get a WI with this priority + WorkItem /*!*/ + wi = workList[p][0]; + Contract.Assert(wi != null); + workList[p].RemoveAt(0); + + // update labels + labels.Remove(wi.getLabel()); + return wi; + } + + public int Count + { + get { return labels.Count; } + } + } + + private GenKillWeight getSummary(CallCmd cmd) + { + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + Contract.Assert(cmd.Proc != null); + string /*!*/ + procName = cmd.Proc.Name; + Contract.Assert(procName != null); + if (procICFG.ContainsKey(procName)) + { + ImplementationControlFlowGraph /*!*/ + cfg = procICFG[procName]; + Contract.Assert(cfg != null); + return GenKillWeight.projectLocals(cfg.summary); + } + + { + Contract.Assert(false); + throw new cce.UnreachableException(); + } + } + + public void ComputeLiveVars(Implementation impl, Program /*!*/ prog) + { + Contract.Requires(prog != null); + Contract.Requires(impl != null); + InterProcGenKill /*!*/ + ipgk = new InterProcGenKill(impl, prog, options); + Contract.Assert(ipgk != null); + ipgk.Compute(); + } + + public void Compute() + { + // Put all exit nodes in the worklist + foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) + { + Contract.Assert(cfg != null); + foreach (Block /*!*/ eb in cfg.exitNodes) + { + Contract.Assert(eb != null); + WorkItem /*!*/ + wi = new WorkItem(cfg, eb); + Contract.Assert(wi != null); + cfg.weightAfter[eb] = GenKillWeight.one(); + AddToWorkList(wi); + } + } + + while (workList.Count != 0) + { + WorkItem /*!*/ + wi = workList.Get(); + Contract.Assert(wi != null); + process(wi); + } + + // Propagate LV to all procedures + foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) + { + Contract.Assert(cfg != null); + foreach (Block /*!*/ b in cfg.nodes) + { + Contract.Assert(b != null); + cfg.liveVarsAfter.Add(b, new HashSet()); + cfg.liveVarsBefore.Add(b, new HashSet()); + } + } + + ImplementationControlFlowGraph /*!*/ + mainCfg = procICFG[mainImpl.Name]; + Contract.Assert(mainCfg != null); + foreach (Block /*!*/ eb in mainCfg.exitNodes) + { + Contract.Assert(eb != null); + WorkItem /*!*/ + wi = new WorkItem(mainCfg, eb); + Contract.Assert(wi != null); + AddToWorkListReverse(wi); + } + + while (workList.Count != 0) + { + WorkItem /*!*/ + wi = workList.Get(); + Contract.Assert(wi != null); + ProcessLv(wi); + } + + // Set live variable info + foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) + { + Contract.Assert(cfg != null); + HashSet /*!*/ + lv = new HashSet(); + foreach (Block /*!*/ eb in cfg.exitNodes) + { + Contract.Assert(eb != null); + lv.UnionWith(cfg.liveVarsAfter[eb]); + } + + varsLiveAtExit.Add(cfg.impl.Name, lv); + lv = new HashSet(); + foreach (Block /*!*/ eb in cfg.srcNodes) + { + Contract.Assert(eb != null); + lv.UnionWith(cfg.liveVarsBefore[eb]); + } + + varsLiveAtEntry.Add(cfg.impl.Name, lv); + varsLiveSummary.Add(cfg.impl.Name, cfg.summary); + } + + /* + foreach(Block/*!*/ + /* b in mainImpl.Blocks){ +Contract.Assert(b != null); +//Set lv = cfg.weightBefore[b].getLiveVars(); +b.liveVarsBefore = procICFG[mainImpl.Name].liveVarsAfter[b]; +//foreach(GlobalVariable/*!*/ + /* v in program.GlobalVariables){Contract.Assert(v != null); +// b.liveVarsBefore.Add(v); +//} +} +*/ + } + + // Called when summaries have already been computed + private void ProcessLv(WorkItem wi) + { + Contract.Requires(wi != null); + ImplementationControlFlowGraph /*!*/ + cfg = wi.cfg; + Contract.Assert(cfg != null); + Block /*!*/ + block = wi.block; + Contract.Assert(block != null); + HashSet /*!*/ + lv = cfg.liveVarsAfter[block]; + Contract.Assert(cce.NonNullElements(lv)); + // Propagate backwards in the block + HashSet /*!*/ + prop = new HashSet(); + prop.UnionWith(lv); + for (int i = block.Cmds.Count - 1; i >= 0; i--) + { + Cmd /*!*/ + cmd = block.Cmds[i]; + Contract.Assert(cmd != null); + if (cmd is CallCmd) + { + string /*!*/ + procName = cce.NonNull(cce.NonNull((CallCmd) cmd).Proc).Name; + Contract.Assert(procName != null); + if (procICFG.ContainsKey(procName)) + { + ImplementationControlFlowGraph /*!*/ + callee = procICFG[procName]; + Contract.Assert(callee != null); + // Inter propagation + // Remove local variables; add return variables + HashSet /*!*/ + elv = new HashSet(); + foreach (Variable /*!*/ v in prop) + { + Contract.Assert(v != null); + if (v is GlobalVariable) + { + elv.Add(v); + } + } + + foreach (Variable /*!*/ v in callee.impl.OutParams) + { + Contract.Assert(v != null); + elv.Add(v); + } + + foreach (Block /*!*/ eb in callee.exitNodes) + { + Contract.Assert(eb != null); + callee.liveVarsAfter[eb].UnionWith(elv); + // TODO: check if modified before inserting + AddToWorkListReverse(new WorkItem(callee, eb)); + } + + // Continue with intra propagation + GenKillWeight /*!*/ + summary = GetWeightCall(cce.NonNull((CallCmd /*!*/) cmd)); + prop = summary.getLiveVars(prop); + } + else + { + new LiveVariableAnalysis(options).Propagate(cmd, prop); + } + } + else + { + new LiveVariableAnalysis(options).Propagate(cmd, prop); + } + } + + cfg.liveVarsBefore[block].UnionWith(prop); + + foreach (Block /*!*/ b in cfg.predEdges[block]) + { + Contract.Assert(b != null); + HashSet /*!*/ + prev = cfg.liveVarsAfter[b]; + Contract.Assert(cce.NonNullElements(prev)); + HashSet /*!*/ + curr = new HashSet(prev); + curr.UnionWith(cfg.liveVarsBefore[block]); + Contract.Assert(cce.NonNullElements(curr)); + if (curr.Count != prev.Count) + { + cfg.liveVarsAfter[b] = curr; + AddToWorkListReverse(new WorkItem(cfg, b)); + } + } + } + + private void process(WorkItem wi) + { + Contract.Requires(wi != null); + GenKillWeight /*!*/ + w = wi.getWeightAfter(); + Contract.Assert(w != null); + + for (int i = wi.block.Cmds.Count - 1; i >= 0; i--) + { + Cmd /*!*/ + c = wi.block.Cmds[i]; + Contract.Assert(c != null); + if (c is CallCmd && procICFG.ContainsKey(cce.NonNull(cce.NonNull((CallCmd) c).Proc).Name)) + { + w = GenKillWeight.extend(GetWeightCall(cce.NonNull((CallCmd) c)), w); + } + else + { + GenKillWeight /*!*/ + cweight = GetWeight(c, wi.cfg.impl, program); + Contract.Assert(cweight != null); + w = GenKillWeight.extend(cweight, w); + } + } + + bool change = wi.setWeightBefore(w); + + if (change && wi.cfg.srcNodes.Contains(wi.block)) + { + GenKillWeight /*!*/ + prev = wi.cfg.summary; + Contract.Assert(prev != null); + GenKillWeight /*!*/ + curr = GenKillWeight.combine(prev, wi.cfg.weightBefore[wi.block]); + Contract.Assert(curr != null); + if (!GenKillWeight.isEqual(prev, curr)) + { + wi.cfg.summary = curr; + // push callers onto the worklist + if (callers.ContainsKey(wi.cfg.impl.Name)) + { + foreach (WorkItem /*!*/ caller in callers[wi.cfg.impl.Name]) + { + Contract.Assert(caller != null); + AddToWorkList(caller); + } + } + } + } + + foreach (Block /*!*/ b in wi.cfg.predEdges[wi.block]) + { + Contract.Assert(b != null); + GenKillWeight /*!*/ + prev = wi.cfg.weightAfter[b]; + Contract.Assert(prev != null); + GenKillWeight /*!*/ + curr = GenKillWeight.combine(prev, w); + Contract.Assert(curr != null); + if (!GenKillWeight.isEqual(prev, curr)) + { + wi.cfg.weightAfter[b] = curr; + AddToWorkList(new WorkItem(wi.cfg, b)); + } + } + } + + static Dictionary /*!*/ + weightCache = new Dictionary(); + + private GenKillWeight GetWeight(Cmd cmd) + { + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + return GetWeight(cmd, null, null); + } + + private GenKillWeight GetWeightCall(CallCmd cmd) + { + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + GenKillWeight /*!*/ + w1 = getWeightBeforeCall(cmd); + GenKillWeight /*!*/ + w2 = getSummary(cmd); + GenKillWeight /*!*/ + w3 = getWeightAfterCall(cmd); + Contract.Assert(w1 != null); + Contract.Assert(w2 != null); + Contract.Assert(w3 != null); + return GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); + } + + private GenKillWeight GetWeight(Cmd cmd, Implementation impl, Program prog) + { + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + + if (weightCache.ContainsKey(cmd)) + { + return weightCache[cmd]; + } + + HashSet /*!*/ + gen = new HashSet(); + HashSet /*!*/ + kill = new HashSet(); + GenKillWeight /*!*/ + ret; + + if (cmd is AssignCmd) + { + AssignCmd /*!*/ + assignCmd = (AssignCmd) cmd; + Contract.Assert(cmd != null); + // I must first iterate over all the targets and remove the live ones. + // After the removals are done, I must add the variables referred on + // the right side of the removed targets + foreach (AssignLhs /*!*/ lhs in assignCmd.Lhss) + { + Contract.Assert(lhs != null); + Variable var = lhs.DeepAssignedVariable; + if (var != null) + { + if (lhs is SimpleAssignLhs) + { + // we should only remove non-map target variables because there is an implicit + // read of a map variable in an assignment to it + kill.Add(var); + } + } + } + + int index = 0; + foreach (Expr /*!*/ expr in assignCmd.Rhss) + { + Contract.Assert(expr != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(expr); + gen.UnionWith(collector.usedVars); + AssignLhs lhs = assignCmd.Lhss[index]; + if (lhs is MapAssignLhs) + { + // If the target is a map, then all indices are also read + MapAssignLhs malhs = (MapAssignLhs) lhs; + foreach (Expr e in malhs.Indexes) + { + VariableCollector /*!*/ + c = new VariableCollector(); + c.Visit(e); + gen.UnionWith(c.usedVars); + } + } + + index++; + } + + ret = new GenKillWeight(gen, kill); + } + else if (cmd is HavocCmd) + { + HavocCmd /*!*/ + havocCmd = (HavocCmd) cce.NonNull(cmd); + foreach (IdentifierExpr /*!*/ expr in havocCmd.Vars) + { + Contract.Assert(expr != null); + if (expr.Decl != null) + { + kill.Add(expr.Decl); + } + } + + ret = new GenKillWeight(gen, kill); + } + else if (cmd is PredicateCmd) + { + Contract.Assert((cmd is AssertCmd || cmd is AssumeCmd)); + PredicateCmd /*!*/ + predicateCmd = (PredicateCmd) cce.NonNull(cmd); + if (predicateCmd.Expr is LiteralExpr && prog != null && impl != null) + { + LiteralExpr le = (LiteralExpr) predicateCmd.Expr; + if (le.IsFalse) + { + var globals = prog.GlobalVariables; + Contract.Assert(cce.NonNullElements(globals)); + foreach (Variable /*!*/ v in globals) + { + Contract.Assert(v != null); + kill.Add(v); + } + + foreach (Variable /*!*/ v in impl.LocVars) + { + Contract.Assert(v != null); + kill.Add(v); + } + + foreach (Variable /*!*/ v in impl.OutParams) + { + Contract.Assert(v != null); + kill.Add(v); + } + } + } + else + { + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(predicateCmd.Expr); + gen.UnionWith(collector.usedVars); + } + + ret = new GenKillWeight(gen, kill); + } + else if (cmd is CommentCmd) + { + ret = new GenKillWeight(gen, kill); + // comments are just for debugging and don't affect verification + } + else if (cmd is SugaredCmd) + { + SugaredCmd /*!*/ + sugCmd = (SugaredCmd) cmd; + Contract.Assert(sugCmd != null); + ret = GetWeight(sugCmd.GetDesugaring(options), impl, prog); + } + else if (cmd is StateCmd) + { + StateCmd /*!*/ + stCmd = (StateCmd) cmd; + Contract.Assert(stCmd != null); + List /*!*/ + cmds = stCmd.Cmds; + Contract.Assert(cmds != null); + int len = cmds.Count; + ret = GenKillWeight.one(); + for (int i = len - 1; i >= 0; i--) + { + GenKillWeight /*!*/ + w = GetWeight(cmds[i], impl, prog); + Contract.Assert(w != null); + ret = GenKillWeight.extend(w, ret); + } + + foreach (Variable /*!*/ v in stCmd.Locals) + { + Contract.Assert(v != null); + kill.Add(v); + } + + ret = GenKillWeight.extend(new GenKillWeight(gen, kill), ret); + } + else + { + { + Contract.Assert(false); + throw new cce.UnreachableException(); + } + } + + weightCache[cmd] = ret; + return ret; + } + + static Dictionary /*!*/ + weightCacheAfterCall = new Dictionary(); + + static Dictionary /*!*/ + weightCacheBeforeCall = new Dictionary(); + + private static GenKillWeight getWeightAfterCall(Cmd cmd) + { + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + + if (weightCacheAfterCall.ContainsKey(cmd)) + { + return weightCacheAfterCall[cmd]; + } + + HashSet /*!*/ + gen = new HashSet(); + HashSet /*!*/ + kill = new HashSet(); + + Contract.Assert(cmd is CallCmd); + CallCmd /*!*/ + ccmd = cce.NonNull((CallCmd) cmd); + + foreach (IdentifierExpr /*!*/ ie in ccmd.Outs) + { + Contract.Assert(ie != null); + if (ie.Decl != null) + { + kill.Add(ie.Decl); + } + } + + // Variables in ensures are considered as "read" + foreach (Ensures /*!*/ re in cce.NonNull(ccmd.Proc).Ensures) + { + Contract.Assert(re != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(re.Condition); + foreach (Variable /*!*/ v in collector.usedVars) + { + Contract.Assert(v != null); + if (v is GlobalVariable) + { + gen.Add(v); + } + } + } + + GenKillWeight /*!*/ + ret = new GenKillWeight(gen, kill); + Contract.Assert(ret != null); + weightCacheAfterCall[cmd] = ret; + return ret; + } + + private static GenKillWeight getWeightBeforeCall(Cmd cmd) + { + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + Contract.Assert((cmd is CallCmd)); + if (weightCacheBeforeCall.ContainsKey(cmd)) + { + return weightCacheBeforeCall[cmd]; + } + + HashSet /*!*/ + gen = new HashSet(); + HashSet /*!*/ + kill = new HashSet(); + CallCmd /*!*/ + ccmd = cce.NonNull((CallCmd /*!*/) cmd); + + foreach (Expr /*!*/ expr in ccmd.Ins) + { + Contract.Assert(expr != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(expr); + gen.UnionWith(collector.usedVars); + } + + Contract.Assert(ccmd.Proc != null); + + // Variables in requires are considered as "read" + foreach (Requires /*!*/ re in ccmd.Proc.Requires) + { + Contract.Assert(re != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(re.Condition); + foreach (Variable /*!*/ v in collector.usedVars) + { + Contract.Assert(v != null); + if (v is GlobalVariable) + { + gen.Add(v); + } + } + } + + // Old variables in ensures are considered as "read" + foreach (Ensures /*!*/ re in ccmd.Proc.Ensures) + { + Contract.Assert(re != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(re.Condition); + foreach (Variable /*!*/ v in collector.oldVarsUsed) + { + Contract.Assert(v != null); + if (v is GlobalVariable) + { + gen.Add(v); + } + } + } + + GenKillWeight /*!*/ + ret = new GenKillWeight(gen, kill); + Contract.Assert(ret != null); + weightCacheAfterCall[cmd] = ret; + return ret; + } +} \ No newline at end of file diff --git a/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs b/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs new file mode 100644 index 000000000..316cd11f2 --- /dev/null +++ b/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs @@ -0,0 +1,213 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using Microsoft.Boogie.GraphUtil; + +namespace Microsoft.Boogie; + +public class LiveVariableAnalysis +{ + private CoreOptions options; + + public LiveVariableAnalysis(CoreOptions options) + { + this.options = options; + } + + public static void ClearLiveVariables(Implementation impl) + { + Contract.Requires(impl != null); + foreach (Block /*!*/ block in impl.Blocks) + { + Contract.Assert(block != null); + block.liveVarsBefore = null; + } + } + + public void ComputeLiveVariables(Implementation impl) + { + Contract.Requires(impl != null); + Microsoft.Boogie.Helpers.ExtraTraceInformation(options, "Starting live variable analysis"); + Graph dag = Program.GraphFromBlocks(impl.Blocks, false); + IEnumerable sortedNodes; + if (options.ModifyTopologicalSorting) + { + sortedNodes = dag.TopologicalSort(true); + } + else + { + sortedNodes = dag.TopologicalSort(); + } + + foreach (Block /*!*/ block in sortedNodes) + { + Contract.Assert(block != null); + HashSet /*!*/ + liveVarsAfter = new HashSet(); + + // The injected assumption variables should always be considered to be live. + foreach (var v in impl.InjectedAssumptionVariables.Concat(impl.DoomedInjectedAssumptionVariables)) + { + liveVarsAfter.Add(v); + } + + if (block.TransferCmd is GotoCmd) + { + GotoCmd gotoCmd = (GotoCmd) block.TransferCmd; + if (gotoCmd.labelTargets != null) + { + foreach (Block /*!*/ succ in gotoCmd.labelTargets) + { + Contract.Assert(succ != null); + Contract.Assert(succ.liveVarsBefore != null); + liveVarsAfter.UnionWith(succ.liveVarsBefore); + } + } + } + + List cmds = block.Cmds; + int len = cmds.Count; + for (int i = len - 1; i >= 0; i--) + { + if (cmds[i] is CallCmd) + { + Procedure /*!*/ + proc = cce.NonNull(cce.NonNull((CallCmd /*!*/) cmds[i]).Proc); + if (InterProcGenKill.HasSummary(proc.Name)) + { + liveVarsAfter = + InterProcGenKill.PropagateLiveVarsAcrossCall(options, cce.NonNull((CallCmd /*!*/) cmds[i]), liveVarsAfter); + continue; + } + } + + Propagate(cmds[i], liveVarsAfter); + } + + block.liveVarsBefore = liveVarsAfter; + } + } + + // perform in place update of liveSet + public void Propagate(Cmd cmd, HashSet /*!*/ liveSet) + { + Contract.Requires(cmd != null); + Contract.Requires(cce.NonNullElements(liveSet)); + if (cmd is AssignCmd) + { + AssignCmd /*!*/ + assignCmd = (AssignCmd) cce.NonNull(cmd); + // I must first iterate over all the targets and remove the live ones. + // After the removals are done, I must add the variables referred on + // the right side of the removed targets + + AssignCmd simpleAssignCmd = assignCmd.AsSimpleAssignCmd; + HashSet indexSet = new HashSet(); + int index = 0; + foreach (AssignLhs /*!*/ lhs in simpleAssignCmd.Lhss) + { + Contract.Assert(lhs != null); + SimpleAssignLhs salhs = lhs as SimpleAssignLhs; + Contract.Assert(salhs != null); + Variable var = salhs.DeepAssignedVariable; + if (var != null && liveSet.Contains(var)) + { + indexSet.Add(index); + liveSet.Remove(var); + } + + index++; + } + + index = 0; + foreach (Expr /*!*/ expr in simpleAssignCmd.Rhss) + { + Contract.Assert(expr != null); + if (indexSet.Contains(index)) + { + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(expr); + liveSet.UnionWith(collector.usedVars); + } + + index++; + } + } + else if (cmd is HavocCmd) + { + HavocCmd /*!*/ + havocCmd = (HavocCmd) cmd; + foreach (IdentifierExpr /*!*/ expr in havocCmd.Vars) + { + Contract.Assert(expr != null); + if (expr.Decl != null && !(QKeyValue.FindBoolAttribute(expr.Decl.Attributes, "assumption") && + expr.Decl.Name.StartsWith("a##cached##"))) + { + liveSet.Remove(expr.Decl); + } + } + } + else if (cmd is PredicateCmd) + { + Contract.Assert((cmd is AssertCmd || cmd is AssumeCmd)); + PredicateCmd /*!*/ + predicateCmd = (PredicateCmd) cce.NonNull(cmd); + if (predicateCmd.Expr is LiteralExpr) + { + LiteralExpr le = (LiteralExpr) predicateCmd.Expr; + if (le.IsFalse) + { + liveSet.Clear(); + } + } + else + { + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(predicateCmd.Expr); + liveSet.UnionWith(collector.usedVars); + } + } + else if (cmd is CommentCmd) + { + // comments are just for debugging and don't affect verification + } else if (cmd is HideRevealCmd) + { + // reveal references no variables + } else if (cmd is ChangeScope) + { + } + else if (cmd is SugaredCmd) + { + SugaredCmd /*!*/ + sugCmd = (SugaredCmd) cce.NonNull(cmd); + Propagate(sugCmd.GetDesugaring(options), liveSet); + } + else if (cmd is StateCmd) + { + StateCmd /*!*/ + stCmd = (StateCmd) cce.NonNull(cmd); + List /*!*/ + cmds = cce.NonNull(stCmd.Cmds); + int len = cmds.Count; + for (int i = len - 1; i >= 0; i--) + { + Propagate(cmds[i], liveSet); + } + + foreach (Variable /*!*/ v in stCmd.Locals) + { + Contract.Assert(v != null); + liveSet.Remove(v); + } + } + else + { + { + Contract.Assert(false); + throw new cce.UnreachableException(); + } + } + } +} \ No newline at end of file diff --git a/Source/Core/Analysis/ModSetCollector.cs b/Source/Core/Analysis/ModSetCollector.cs new file mode 100644 index 000000000..336781057 --- /dev/null +++ b/Source/Core/Analysis/ModSetCollector.cs @@ -0,0 +1,223 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class ModSetCollector : ReadOnlyVisitor +{ + private CoreOptions options; + private Procedure enclosingProc; + + private Dictionary> modSets; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cce.NonNullDictionaryAndValues(modSets)); + Contract.Invariant(Contract.ForAll(modSets.Values, v => cce.NonNullElements(v))); + } + + public ModSetCollector(CoreOptions options) + { + this.options = options; + modSets = new Dictionary>(); + } + + private bool moreProcessingRequired; + + public void DoModSetAnalysis(Program program) + { + Contract.Requires(program != null); + + if (options.Trace) + { +// Console.WriteLine(); +// Console.WriteLine("Running modset analysis ..."); +// int procCount = 0; +// foreach (Declaration/*!*/ decl in program.TopLevelDeclarations) +// { +// Contract.Assert(decl != null); +// if (decl is Procedure) +// procCount++; +// } +// Console.WriteLine("Number of procedures = {0}", procCount);*/ + } + + HashSet implementedProcs = new HashSet(); + foreach (var impl in program.Implementations) + { + if (impl.Proc != null) + { + implementedProcs.Add(impl.Proc); + } + } + + foreach (var proc in program.Procedures) + { + if (!implementedProcs.Contains(proc)) + { + enclosingProc = proc; + foreach (var expr in proc.Modifies) + { + Contract.Assert(expr != null); + ProcessVariable(expr.Decl); + } + + enclosingProc = null; + } + else + { + modSets.Add(proc, new HashSet()); + } + } + + moreProcessingRequired = true; + while (moreProcessingRequired) + { + moreProcessingRequired = false; + this.Visit(program); + } + + foreach (Procedure x in modSets.Keys) + { + x.Modifies = new List(); + foreach (Variable v in modSets[x]) + { + x.Modifies.Add(new IdentifierExpr(v.tok, v)); + } + } + +#if DEBUG_PRINT + options.OutputWriter.WriteLine("Number of procedures with nonempty modsets = {0}", modSets.Keys.Count); + foreach (Procedure/*!*/ x in modSets.Keys) { + Contract.Assert(x != null); + options.OutputWriter.Write("{0} : ", x.Name); + bool first = true; + foreach (Variable/*!*/ y in modSets[x]) { + Contract.Assert(y != null); + if (first) + first = false; + else + options.OutputWriter.Write(", "); + options.OutputWriter.Write("{0}", y.Name); + } + options.OutputWriter.WriteLine(""); + options.OutputWriter +#endif + } + + public override Implementation VisitImplementation(Implementation node) + { + //Contract.Requires(node != null); + Contract.Ensures(Contract.Result() != null); + enclosingProc = node.Proc; + Implementation /*!*/ + ret = base.VisitImplementation(node); + Contract.Assert(ret != null); + enclosingProc = null; + + return ret; + } + + public override Cmd VisitAssignCmd(AssignCmd assignCmd) + { + Contract.Ensures(Contract.Result() != null); + Cmd ret = base.VisitAssignCmd(assignCmd); + foreach (AssignLhs lhs in assignCmd.Lhss) + { + Contract.Assert(lhs != null); + ProcessVariable(lhs.DeepAssignedVariable); + } + + return ret; + } + + public override Cmd VisitUnpackCmd(UnpackCmd unpackCmd) + { + Contract.Ensures(Contract.Result() != null); + Cmd ret = base.VisitUnpackCmd(unpackCmd); + foreach (var expr in unpackCmd.Lhs.Args) + { + ProcessVariable(((IdentifierExpr)expr).Decl); + } + return ret; + } + + public override Cmd VisitHavocCmd(HavocCmd havocCmd) + { + Contract.Ensures(Contract.Result() != null); + Cmd ret = base.VisitHavocCmd(havocCmd); + foreach (IdentifierExpr expr in havocCmd.Vars) + { + Contract.Assert(expr != null); + ProcessVariable(expr.Decl); + } + + return ret; + } + + public override Cmd VisitCallCmd(CallCmd callCmd) + { + //Contract.Requires(callCmd != null); + Contract.Ensures(Contract.Result() != null); + Cmd ret = base.VisitCallCmd(callCmd); + foreach (IdentifierExpr ie in callCmd.Outs) + { + if (ie != null) + { + ProcessVariable(ie.Decl); + } + } + + Procedure callee = callCmd.Proc; + if (callee == null) + { + return ret; + } + + if (modSets.ContainsKey(callee)) + { + foreach (Variable var in modSets[callee]) + { + ProcessVariable(var); + } + } + + return ret; + } + + private void ProcessVariable(Variable var) + { + Procedure /*!*/ + localProc = cce.NonNull(enclosingProc); + if (var == null) + { + return; + } + + if (!(var is GlobalVariable)) + { + return; + } + + if (!modSets.ContainsKey(localProc)) + { + modSets[localProc] = new HashSet(); + } + + if (modSets[localProc].Contains(var)) + { + return; + } + + moreProcessingRequired = true; + modSets[localProc].Add(var); + } + + public override Expr VisitCodeExpr(CodeExpr node) + { + // don't go into the code expression, since it can only modify variables local to the code expression, + // and the mod-set analysis is interested in global variables + return node; + } +} \ No newline at end of file diff --git a/Source/Core/Analysis/MutableVariableCollector.cs b/Source/Core/Analysis/MutableVariableCollector.cs new file mode 100644 index 000000000..baa66a85e --- /dev/null +++ b/Source/Core/Analysis/MutableVariableCollector.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class MutableVariableCollector : ReadOnlyVisitor +{ + public HashSet UsedVariables = new HashSet(); + + public void AddUsedVariables(HashSet usedVariables) + { + Contract.Requires(usedVariables != null); + + foreach (var v in usedVariables) + { + UsedVariables.Add(v); + } + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) + { + Contract.Ensures(Contract.Result() != null); + + if (node.Decl != null && node.Decl.IsMutable) + { + UsedVariables.Add(node.Decl); + } + + return base.VisitIdentifierExpr(node); + } +} \ No newline at end of file diff --git a/Source/Core/Analysis/UnusedVarEliminator.cs b/Source/Core/Analysis/UnusedVarEliminator.cs new file mode 100644 index 000000000..41291120c --- /dev/null +++ b/Source/Core/Analysis/UnusedVarEliminator.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class UnusedVarEliminator : VariableCollector +{ + public static void Eliminate(Program program) + { + Contract.Requires(program != null); + UnusedVarEliminator elim = new UnusedVarEliminator(); + elim.Visit(program); + } + + private UnusedVarEliminator() + : base() + { + } + + public override Implementation VisitImplementation(Implementation node) + { + //Contract.Requires(node != null); + Contract.Ensures(Contract.Result() != null); + //Console.WriteLine("Procedure {0}", node.Name); + Implementation /*!*/ + impl = base.VisitImplementation(node); + Contract.Assert(impl != null); + //Console.WriteLine("Old number of local variables = {0}", impl.LocVars.Length); + List /*!*/ + vars = new List(); + foreach (Variable /*!*/ var in impl.LocVars) + { + Contract.Assert(var != null); + if (_usedVars.Contains(var)) + { + vars.Add(var); + } + } + + impl.LocVars = vars; + //Console.WriteLine("New number of local variables = {0}", impl.LocVars.Length); + //Console.WriteLine("---------------------------------"); + _usedVars.Clear(); + return impl; + } +} \ No newline at end of file diff --git a/Source/Core/Analysis/VariableCollector.cs b/Source/Core/Analysis/VariableCollector.cs new file mode 100644 index 000000000..9e996b71e --- /dev/null +++ b/Source/Core/Analysis/VariableCollector.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; + +namespace Microsoft.Boogie; + +public class VariableCollector : ReadOnlyVisitor +{ + private bool _ignoreOld; + + protected HashSet _usedVars; + + public IEnumerable usedVars + { + get { return _usedVars.AsEnumerable(); } + } + + protected HashSet _oldVarsUsed; + + public IEnumerable oldVarsUsed + { + get { return _oldVarsUsed.AsEnumerable(); } + } + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cce.NonNullElements(_usedVars)); + Contract.Invariant(cce.NonNullElements(_oldVarsUsed)); + } + + int insideOldExpr; + + public VariableCollector(bool ignoreOld = false) + { + _ignoreOld = ignoreOld; + _usedVars = new HashSet(); + _oldVarsUsed = new HashSet(); + insideOldExpr = 0; + } + + public override Expr VisitOldExpr(OldExpr node) + { + if (!_ignoreOld) + { + insideOldExpr++; + node.Expr = this.VisitExpr(node.Expr); + insideOldExpr--; + } + return node; + } + + public override Expr VisitIdentifierExpr(IdentifierExpr node) + { + if (node.Decl != null) + { + _usedVars.Add(node.Decl); + if (insideOldExpr > 0) + { + _oldVarsUsed.Add(node.Decl); + } + } + return node; + } + + public static IEnumerable Collect(Absy node, bool ignoreOld = false) + { + var collector = new VariableCollector(ignoreOld); + collector.Visit(node); + return collector.usedVars; + } + + public static IEnumerable Collect(IEnumerable nodes, bool ignoreOld = false) + { + var collector = new VariableCollector(ignoreOld); + foreach (var node in nodes) + { + collector.Visit(node); + } + return collector.usedVars; + } +} \ No newline at end of file diff --git a/Source/Core/DeadVarElim.cs b/Source/Core/DeadVarElim.cs deleted file mode 100644 index 65233640b..000000000 --- a/Source/Core/DeadVarElim.cs +++ /dev/null @@ -1,2194 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Boogie.GraphUtil; -using System.Diagnostics.Contracts; - -namespace Microsoft.Boogie -{ - public class UnusedVarEliminator : VariableCollector - { - public static void Eliminate(Program program) - { - Contract.Requires(program != null); - UnusedVarEliminator elim = new UnusedVarEliminator(); - elim.Visit(program); - } - - private UnusedVarEliminator() - : base() - { - } - - public override Implementation VisitImplementation(Implementation node) - { - //Contract.Requires(node != null); - Contract.Ensures(Contract.Result() != null); - //Console.WriteLine("Procedure {0}", node.Name); - Implementation /*!*/ - impl = base.VisitImplementation(node); - Contract.Assert(impl != null); - //Console.WriteLine("Old number of local variables = {0}", impl.LocVars.Length); - List /*!*/ - vars = new List(); - foreach (Variable /*!*/ var in impl.LocVars) - { - Contract.Assert(var != null); - if (_usedVars.Contains(var)) - { - vars.Add(var); - } - } - - impl.LocVars = vars; - //Console.WriteLine("New number of local variables = {0}", impl.LocVars.Length); - //Console.WriteLine("---------------------------------"); - _usedVars.Clear(); - return impl; - } - } - - public class ModSetCollector : ReadOnlyVisitor - { - private CoreOptions options; - private Procedure enclosingProc; - - private Dictionary> modSets; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cce.NonNullDictionaryAndValues(modSets)); - Contract.Invariant(Contract.ForAll(modSets.Values, v => cce.NonNullElements(v))); - } - - public ModSetCollector(CoreOptions options) - { - this.options = options; - modSets = new Dictionary>(); - } - - private bool moreProcessingRequired; - - public void DoModSetAnalysis(Program program) - { - Contract.Requires(program != null); - - if (options.Trace) - { -// Console.WriteLine(); -// Console.WriteLine("Running modset analysis ..."); -// int procCount = 0; -// foreach (Declaration/*!*/ decl in program.TopLevelDeclarations) -// { -// Contract.Assert(decl != null); -// if (decl is Procedure) -// procCount++; -// } -// Console.WriteLine("Number of procedures = {0}", procCount);*/ - } - - HashSet implementedProcs = new HashSet(); - foreach (var impl in program.Implementations) - { - if (impl.Proc != null) - { - implementedProcs.Add(impl.Proc); - } - } - - foreach (var proc in program.Procedures) - { - if (!implementedProcs.Contains(proc)) - { - enclosingProc = proc; - foreach (var expr in proc.Modifies) - { - Contract.Assert(expr != null); - ProcessVariable(expr.Decl); - } - - enclosingProc = null; - } - else - { - modSets.Add(proc, new HashSet()); - } - } - - moreProcessingRequired = true; - while (moreProcessingRequired) - { - moreProcessingRequired = false; - this.Visit(program); - } - - foreach (Procedure x in modSets.Keys) - { - x.Modifies = new List(); - foreach (Variable v in modSets[x]) - { - x.Modifies.Add(new IdentifierExpr(v.tok, v)); - } - } - -#if DEBUG_PRINT - options.OutputWriter.WriteLine("Number of procedures with nonempty modsets = {0}", modSets.Keys.Count); - foreach (Procedure/*!*/ x in modSets.Keys) { - Contract.Assert(x != null); - options.OutputWriter.Write("{0} : ", x.Name); - bool first = true; - foreach (Variable/*!*/ y in modSets[x]) { - Contract.Assert(y != null); - if (first) - first = false; - else - options.OutputWriter.Write(", "); - options.OutputWriter.Write("{0}", y.Name); - } - options.OutputWriter.WriteLine(""); - options.OutputWriter -#endif - } - - public override Implementation VisitImplementation(Implementation node) - { - //Contract.Requires(node != null); - Contract.Ensures(Contract.Result() != null); - enclosingProc = node.Proc; - Implementation /*!*/ - ret = base.VisitImplementation(node); - Contract.Assert(ret != null); - enclosingProc = null; - - return ret; - } - - public override Cmd VisitAssignCmd(AssignCmd assignCmd) - { - Contract.Ensures(Contract.Result() != null); - Cmd ret = base.VisitAssignCmd(assignCmd); - foreach (AssignLhs lhs in assignCmd.Lhss) - { - Contract.Assert(lhs != null); - ProcessVariable(lhs.DeepAssignedVariable); - } - - return ret; - } - - public override Cmd VisitUnpackCmd(UnpackCmd unpackCmd) - { - Contract.Ensures(Contract.Result() != null); - Cmd ret = base.VisitUnpackCmd(unpackCmd); - foreach (var expr in unpackCmd.Lhs.Args) - { - ProcessVariable(((IdentifierExpr)expr).Decl); - } - return ret; - } - - public override Cmd VisitHavocCmd(HavocCmd havocCmd) - { - Contract.Ensures(Contract.Result() != null); - Cmd ret = base.VisitHavocCmd(havocCmd); - foreach (IdentifierExpr expr in havocCmd.Vars) - { - Contract.Assert(expr != null); - ProcessVariable(expr.Decl); - } - - return ret; - } - - public override Cmd VisitCallCmd(CallCmd callCmd) - { - //Contract.Requires(callCmd != null); - Contract.Ensures(Contract.Result() != null); - Cmd ret = base.VisitCallCmd(callCmd); - foreach (IdentifierExpr ie in callCmd.Outs) - { - if (ie != null) - { - ProcessVariable(ie.Decl); - } - } - - Procedure callee = callCmd.Proc; - if (callee == null) - { - return ret; - } - - if (modSets.ContainsKey(callee)) - { - foreach (Variable var in modSets[callee]) - { - ProcessVariable(var); - } - } - - return ret; - } - - private void ProcessVariable(Variable var) - { - Procedure /*!*/ - localProc = cce.NonNull(enclosingProc); - if (var == null) - { - return; - } - - if (!(var is GlobalVariable)) - { - return; - } - - if (!modSets.ContainsKey(localProc)) - { - modSets[localProc] = new HashSet(); - } - - if (modSets[localProc].Contains(var)) - { - return; - } - - moreProcessingRequired = true; - modSets[localProc].Add(var); - } - - public override Expr VisitCodeExpr(CodeExpr node) - { - // don't go into the code expression, since it can only modify variables local to the code expression, - // and the mod-set analysis is interested in global variables - return node; - } - } - - public class MutableVariableCollector : ReadOnlyVisitor - { - public HashSet UsedVariables = new HashSet(); - - public void AddUsedVariables(HashSet usedVariables) - { - Contract.Requires(usedVariables != null); - - foreach (var v in usedVariables) - { - UsedVariables.Add(v); - } - } - - public override Expr VisitIdentifierExpr(IdentifierExpr node) - { - Contract.Ensures(Contract.Result() != null); - - if (node.Decl != null && node.Decl.IsMutable) - { - UsedVariables.Add(node.Decl); - } - - return base.VisitIdentifierExpr(node); - } - } - - public class VariableCollector : ReadOnlyVisitor - { - private bool _ignoreOld; - - protected HashSet _usedVars; - - public IEnumerable usedVars - { - get { return _usedVars.AsEnumerable(); } - } - - protected HashSet _oldVarsUsed; - - public IEnumerable oldVarsUsed - { - get { return _oldVarsUsed.AsEnumerable(); } - } - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cce.NonNullElements(_usedVars)); - Contract.Invariant(cce.NonNullElements(_oldVarsUsed)); - } - - int insideOldExpr; - - public VariableCollector(bool ignoreOld = false) - { - _ignoreOld = ignoreOld; - _usedVars = new HashSet(); - _oldVarsUsed = new HashSet(); - insideOldExpr = 0; - } - - public override Expr VisitOldExpr(OldExpr node) - { - if (!_ignoreOld) - { - insideOldExpr++; - node.Expr = this.VisitExpr(node.Expr); - insideOldExpr--; - } - return node; - } - - public override Expr VisitIdentifierExpr(IdentifierExpr node) - { - if (node.Decl != null) - { - _usedVars.Add(node.Decl); - if (insideOldExpr > 0) - { - _oldVarsUsed.Add(node.Decl); - } - } - return node; - } - - public static IEnumerable Collect(Absy node, bool ignoreOld = false) - { - var collector = new VariableCollector(ignoreOld); - collector.Visit(node); - return collector.usedVars; - } - - public static IEnumerable Collect(IEnumerable nodes, bool ignoreOld = false) - { - var collector = new VariableCollector(ignoreOld); - foreach (var node in nodes) - { - collector.Visit(node); - } - return collector.usedVars; - } - } - - public class BlockCoalescer : ReadOnlyVisitor - { - public static void CoalesceBlocks(Program program) - { - Contract.Requires(program != null); - BlockCoalescer blockCoalescer = new BlockCoalescer(); - blockCoalescer.Visit(program); - } - - private static HashSet /*!*/ ComputeMultiPredecessorBlocks(Implementation /*!*/ impl) - { - Contract.Requires(impl != null); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - HashSet visitedBlocks = new HashSet(); - HashSet multiPredBlocks = new HashSet(); - Stack dfsStack = new Stack(); - dfsStack.Push(impl.Blocks[0]); - while (dfsStack.Count > 0) - { - Block /*!*/ - b = dfsStack.Pop(); - Contract.Assert(b != null); - if (visitedBlocks.Contains(b)) - { - multiPredBlocks.Add(b); - continue; - } - - visitedBlocks.Add(b); - if (b.TransferCmd == null) - { - continue; - } - - if (b.TransferCmd is ReturnCmd) - { - continue; - } - - Contract.Assert(b.TransferCmd is GotoCmd); - GotoCmd gotoCmd = (GotoCmd) b.TransferCmd; - if (gotoCmd.labelTargets == null) - { - continue; - } - - foreach (Block /*!*/ succ in gotoCmd.labelTargets) - { - Contract.Assert(succ != null); - dfsStack.Push(succ); - } - } - - return multiPredBlocks; - } - - public override Implementation VisitImplementation(Implementation impl) - { - //Contract.Requires(impl != null); - Contract.Ensures(Contract.Result() != null); - //Console.WriteLine("Procedure {0}", impl.Name); - //Console.WriteLine("Initial number of blocks = {0}", impl.Blocks.Count); - - HashSet multiPredBlocks = ComputeMultiPredecessorBlocks(impl); - Contract.Assert(cce.NonNullElements(multiPredBlocks)); - HashSet visitedBlocks = new HashSet(); - HashSet removedBlocks = new HashSet(); - Stack dfsStack = new Stack(); - dfsStack.Push(impl.Blocks[0]); - while (dfsStack.Count > 0) - { - Block /*!*/ - b = dfsStack.Pop(); - Contract.Assert(b != null); - if (visitedBlocks.Contains(b)) - { - continue; - } - - visitedBlocks.Add(b); - if (b.TransferCmd == null) - { - continue; - } - - if (b.TransferCmd is ReturnCmd) - { - continue; - } - - Contract.Assert(b.TransferCmd is GotoCmd); - GotoCmd gotoCmd = (GotoCmd) b.TransferCmd; - if (gotoCmd.labelTargets == null) - { - continue; - } - - if (gotoCmd.labelTargets.Count == 1) - { - Block /*!*/ - succ = cce.NonNull(gotoCmd.labelTargets[0]); - if (!multiPredBlocks.Contains(succ)) - { - foreach (Cmd /*!*/ cmd in succ.Cmds) - { - Contract.Assert(cmd != null); - b.Cmds.Add(cmd); - } - - b.TransferCmd = succ.TransferCmd; - if (!b.tok.IsValid && succ.tok.IsValid) - { - b.tok = succ.tok; - b.Label = succ.Label; - } - - removedBlocks.Add(succ); - dfsStack.Push(b); - visitedBlocks.Remove(b); - continue; - } - } - - foreach (Block /*!*/ succ in gotoCmd.labelTargets) - { - Contract.Assert(succ != null); - dfsStack.Push(succ); - } - } - - List newBlocks = new List(); - foreach (Block /*!*/ b in impl.Blocks) - { - Contract.Assert(b != null); - if (visitedBlocks.Contains(b) && !removedBlocks.Contains(b)) - { - newBlocks.Add(b); - } - } - - impl.Blocks = newBlocks; - foreach (Block b in impl.Blocks) - { - if (b.TransferCmd is ReturnCmd) - { - continue; - } - - GotoCmd gotoCmd = b.TransferCmd as GotoCmd; - gotoCmd.labelNames = new List(); - foreach (Block succ in gotoCmd.labelTargets) - { - gotoCmd.labelNames.Add(succ.Label); - } - } - - // Console.WriteLine("Final number of blocks = {0}", impl.Blocks.Count); - return impl; - } - } - - public class LiveVariableAnalysis - { - private CoreOptions options; - - public LiveVariableAnalysis(CoreOptions options) - { - this.options = options; - } - - public static void ClearLiveVariables(Implementation impl) - { - Contract.Requires(impl != null); - foreach (Block /*!*/ block in impl.Blocks) - { - Contract.Assert(block != null); - block.liveVarsBefore = null; - } - } - - public void ComputeLiveVariables(Implementation impl) - { - Contract.Requires(impl != null); - Microsoft.Boogie.Helpers.ExtraTraceInformation(options, "Starting live variable analysis"); - Graph dag = Program.GraphFromBlocks(impl.Blocks, false); - IEnumerable sortedNodes; - if (options.ModifyTopologicalSorting) - { - sortedNodes = dag.TopologicalSort(true); - } - else - { - sortedNodes = dag.TopologicalSort(); - } - - foreach (Block /*!*/ block in sortedNodes) - { - Contract.Assert(block != null); - HashSet /*!*/ - liveVarsAfter = new HashSet(); - - // The injected assumption variables should always be considered to be live. - foreach (var v in impl.InjectedAssumptionVariables.Concat(impl.DoomedInjectedAssumptionVariables)) - { - liveVarsAfter.Add(v); - } - - if (block.TransferCmd is GotoCmd) - { - GotoCmd gotoCmd = (GotoCmd) block.TransferCmd; - if (gotoCmd.labelTargets != null) - { - foreach (Block /*!*/ succ in gotoCmd.labelTargets) - { - Contract.Assert(succ != null); - Contract.Assert(succ.liveVarsBefore != null); - liveVarsAfter.UnionWith(succ.liveVarsBefore); - } - } - } - - List cmds = block.Cmds; - int len = cmds.Count; - for (int i = len - 1; i >= 0; i--) - { - if (cmds[i] is CallCmd) - { - Procedure /*!*/ - proc = cce.NonNull(cce.NonNull((CallCmd /*!*/) cmds[i]).Proc); - if (InterProcGenKill.HasSummary(proc.Name)) - { - liveVarsAfter = - InterProcGenKill.PropagateLiveVarsAcrossCall(options, cce.NonNull((CallCmd /*!*/) cmds[i]), liveVarsAfter); - continue; - } - } - - Propagate(cmds[i], liveVarsAfter); - } - - block.liveVarsBefore = liveVarsAfter; - } - } - - // perform in place update of liveSet - public void Propagate(Cmd cmd, HashSet /*!*/ liveSet) - { - Contract.Requires(cmd != null); - Contract.Requires(cce.NonNullElements(liveSet)); - if (cmd is AssignCmd) - { - AssignCmd /*!*/ - assignCmd = (AssignCmd) cce.NonNull(cmd); - // I must first iterate over all the targets and remove the live ones. - // After the removals are done, I must add the variables referred on - // the right side of the removed targets - - AssignCmd simpleAssignCmd = assignCmd.AsSimpleAssignCmd; - HashSet indexSet = new HashSet(); - int index = 0; - foreach (AssignLhs /*!*/ lhs in simpleAssignCmd.Lhss) - { - Contract.Assert(lhs != null); - SimpleAssignLhs salhs = lhs as SimpleAssignLhs; - Contract.Assert(salhs != null); - Variable var = salhs.DeepAssignedVariable; - if (var != null && liveSet.Contains(var)) - { - indexSet.Add(index); - liveSet.Remove(var); - } - - index++; - } - - index = 0; - foreach (Expr /*!*/ expr in simpleAssignCmd.Rhss) - { - Contract.Assert(expr != null); - if (indexSet.Contains(index)) - { - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(expr); - liveSet.UnionWith(collector.usedVars); - } - - index++; - } - } - else if (cmd is HavocCmd) - { - HavocCmd /*!*/ - havocCmd = (HavocCmd) cmd; - foreach (IdentifierExpr /*!*/ expr in havocCmd.Vars) - { - Contract.Assert(expr != null); - if (expr.Decl != null && !(QKeyValue.FindBoolAttribute(expr.Decl.Attributes, "assumption") && - expr.Decl.Name.StartsWith("a##cached##"))) - { - liveSet.Remove(expr.Decl); - } - } - } - else if (cmd is PredicateCmd) - { - Contract.Assert((cmd is AssertCmd || cmd is AssumeCmd)); - PredicateCmd /*!*/ - predicateCmd = (PredicateCmd) cce.NonNull(cmd); - if (predicateCmd.Expr is LiteralExpr) - { - LiteralExpr le = (LiteralExpr) predicateCmd.Expr; - if (le.IsFalse) - { - liveSet.Clear(); - } - } - else - { - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(predicateCmd.Expr); - liveSet.UnionWith(collector.usedVars); - } - } - else if (cmd is CommentCmd) - { - // comments are just for debugging and don't affect verification - } else if (cmd is HideRevealCmd) - { - // reveal references no variables - } else if (cmd is ChangeScope) - { - } - else if (cmd is SugaredCmd) - { - SugaredCmd /*!*/ - sugCmd = (SugaredCmd) cce.NonNull(cmd); - Propagate(sugCmd.GetDesugaring(options), liveSet); - } - else if (cmd is StateCmd) - { - StateCmd /*!*/ - stCmd = (StateCmd) cce.NonNull(cmd); - List /*!*/ - cmds = cce.NonNull(stCmd.Cmds); - int len = cmds.Count; - for (int i = len - 1; i >= 0; i--) - { - Propagate(cmds[i], liveSet); - } - - foreach (Variable /*!*/ v in stCmd.Locals) - { - Contract.Assert(v != null); - liveSet.Remove(v); - } - } - else - { - { - Contract.Assert(false); - throw new cce.UnreachableException(); - } - } - } - } - - /* - // An idempotent semiring interface - abstract public class Weight { - abstract public Weight! one(); - abstract public Weight! zero(); - abstract public Weight! extend(Weight! w1, Weight! w2); - abstract public Weight! combine(Weight! w1, Weight! w2); - abstract public Weight! isEqual(Weight! w); - abstract public Weight! projectLocals() - } - */ - - // Weight domain for LiveVariableAnalysis (Gen/Kill) - - public class GenKillWeight - { - // lambda S. (S - kill) union gen - HashSet /*!*/ - gen; - - HashSet /*!*/ - kill; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cce.NonNullElements(gen)); - Contract.Invariant(cce.NonNullElements(kill)); - Contract.Invariant(oneWeight != null); - Contract.Invariant(zeroWeight != null); - } - - bool isZero; - - public static GenKillWeight /*!*/ - oneWeight = new GenKillWeight(new HashSet(), new HashSet()); - - public static GenKillWeight /*!*/ - zeroWeight = new GenKillWeight(); - - // initializes to zero - public GenKillWeight() - { - this.isZero = true; - this.gen = new HashSet(); - this.kill = new HashSet(); - } - - public GenKillWeight(HashSet gen, HashSet kill) - { - Contract.Requires(cce.NonNullElements(gen)); - Contract.Requires(cce.NonNullElements(kill)); - Contract.Assert(gen != null); - Contract.Assert(kill != null); - this.gen = gen; - this.kill = kill; - this.isZero = false; - } - - public static GenKillWeight one() - { - Contract.Ensures(Contract.Result() != null); - return oneWeight; - } - - public static GenKillWeight zero() - { - Contract.Ensures(Contract.Result() != null); - return zeroWeight; - } - - public static GenKillWeight extend(GenKillWeight w1, GenKillWeight w2) - { - Contract.Requires(w2 != null); - Contract.Requires(w1 != null); - Contract.Ensures(Contract.Result() != null); - if (w1.isZero || w2.isZero) - { - return zero(); - } - - HashSet t = new HashSet(w2.gen); - t.ExceptWith(w1.kill); - HashSet g = new HashSet(w1.gen); - g.UnionWith(t); - HashSet k = new HashSet(w1.kill); - k.UnionWith(w2.kill); - return new GenKillWeight(g, k); - //return new GenKillWeight(w1.gen.Union(w2.gen.Difference(w1.kill)), w1.kill.Union(w2.kill)); - } - - public static GenKillWeight combine(GenKillWeight w1, GenKillWeight w2) - { - Contract.Requires(w2 != null); - Contract.Requires(w1 != null); - Contract.Ensures(Contract.Result() != null); - if (w1.isZero) - { - return w2; - } - - if (w2.isZero) - { - return w1; - } - - HashSet g = new HashSet(w1.gen); - g.UnionWith(w2.gen); - HashSet k = new HashSet(w1.kill); - k.IntersectWith(w2.kill); - return new GenKillWeight(g, k); - //return new GenKillWeight(w1.gen.Union(w2.gen), w1.kill.Intersection(w2.kill)); - } - - public static GenKillWeight projectLocals(GenKillWeight w) - { - Contract.Requires(w != null); - Contract.Ensures(Contract.Result() != null); - HashSet gen = new HashSet(); - foreach (Variable v in w.gen) - { - if (isGlobal(v)) - { - gen.Add(v); - } - } - - HashSet kill = new HashSet(); - foreach (Variable v in w.kill) - { - if (isGlobal(v)) - { - kill.Add(v); - } - } - - return new GenKillWeight(gen, kill); - } - - public static bool isEqual(GenKillWeight w1, GenKillWeight w2) - { - Contract.Requires(w2 != null); - Contract.Requires(w1 != null); - if (w1.isZero) - { - return w2.isZero; - } - - if (w2.isZero) - { - return w1.isZero; - } - - return (w1.gen.Equals(w2.gen) && w1.kill.Equals(w2.kill)); - } - - private static bool isGlobal(Variable v) - { - Contract.Requires(v != null); - return (v is GlobalVariable); - } - - [Pure] - public override string ToString() - { - Contract.Ensures(Contract.Result() != null); - return string.Format("({0},{1})", gen.ToString(), kill.ToString()); - } - - public HashSet /*!*/ getLiveVars() - { - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - return gen; - } - - public HashSet /*!*/ getLiveVars(HashSet /*!*/ lv) - { - Contract.Requires(cce.NonNullElements(lv)); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - HashSet temp = new HashSet(lv); - temp.ExceptWith(kill); - temp.UnionWith(gen); - return temp; - } - } - - public class ImplementationControlFlowGraph - { - public Graph /*!*/ - graph; - - // Map from procedure to the list of blocks that call that procedure - public Dictionary /*!*/> /*!*/ - procsCalled; - - public HashSet /*!*/ - nodes; - - public Dictionary /*!*/> /*!*/ - succEdges; - - public Dictionary /*!*/> /*!*/ - predEdges; - - private Dictionary /*!*/ - priority; - - public HashSet /*!*/ - srcNodes; - - public HashSet /*!*/ - exitNodes; - - public Dictionary /*!*/ - weightBefore; - - public Dictionary /*!*/ - weightAfter; - - public Dictionary /*!*/> /*!*/ - liveVarsAfter; - - public Dictionary /*!*/> /*!*/ - liveVarsBefore; - - public GenKillWeight /*!*/ - summary; - - private readonly CoreOptions options; - - public Implementation /*!*/ - impl; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cce.NonNullElements(graph.Nodes)); - Contract.Invariant(cce.NonNullDictionaryAndValues(procsCalled)); - Contract.Invariant(cce.NonNullElements(nodes)); - Contract.Invariant(cce.NonNullDictionaryAndValues(succEdges)); - Contract.Invariant(cce.NonNullDictionaryAndValues(predEdges)); - Contract.Invariant(priority != null); - Contract.Invariant(cce.NonNullElements(srcNodes)); - Contract.Invariant(cce.NonNullElements(exitNodes)); - Contract.Invariant(cce.NonNullDictionaryAndValues(weightBefore)); - Contract.Invariant(cce.NonNullDictionaryAndValues(weightAfter)); - Contract.Invariant(cce.NonNullDictionaryAndValues(liveVarsAfter)); - Contract.Invariant(cce.NonNullDictionaryAndValues(liveVarsBefore)); - Contract.Invariant(summary != null); - Contract.Invariant(impl != null); - } - - - [NotDelayed] - public ImplementationControlFlowGraph(CoreOptions options, Implementation impl) - { - Contract.Requires(impl != null); - this.graph = new Graph(); - this.procsCalled = new Dictionary /*!*/>(); - this.nodes = new HashSet(); - this.succEdges = new Dictionary /*!*/>(); - this.predEdges = new Dictionary /*!*/>(); - - this.priority = new Dictionary(); - - this.srcNodes = new HashSet(); - this.exitNodes = new HashSet(); - - this.weightBefore = new Dictionary(); - this.weightAfter = new Dictionary(); - this.liveVarsAfter = new Dictionary /*!*/>(); - this.liveVarsBefore = new Dictionary /*!*/>(); - - summary = GenKillWeight.zero(); - this.options = options; - this.impl = impl; - - Initialize(impl); - } - - private void Initialize(Implementation impl) - { - Contract.Requires(impl != null); - addSource(impl.Blocks[0]); - graph.AddSource(impl.Blocks[0]); - - foreach (Block /*!*/ b in impl.Blocks) - { - Contract.Assert(b != null); - if (b.TransferCmd is ReturnCmd) - { - exitNodes.Add(b); - } - else - { - GotoCmd gc = b.TransferCmd as GotoCmd; - Contract.Assert(gc != null); - Contract.Assert(gc.labelTargets != null); - foreach (Block /*!*/ t in gc.labelTargets) - { - Contract.Assert(t != null); - addEdge(b, t); - graph.AddEdge(b, t); - } - } - - weightBefore[b] = GenKillWeight.zero(); - weightAfter[b] = GenKillWeight.zero(); - - foreach (Cmd /*!*/ c in b.Cmds) - { - Contract.Assert(c != null); - if (c is CallCmd) - { - CallCmd /*!*/ - cc = cce.NonNull((CallCmd /*!*/) c); - Contract.Assert(cc.Proc != null); - string /*!*/ - procName = cc.Proc.Name; - Contract.Assert(procName != null); - if (!procsCalled.ContainsKey(procName)) - { - procsCalled.Add(procName, new List()); - } - - procsCalled[procName].Add(b); - } - } - } - - graph.TarjanTopSort(out var acyclic, out var sortedNodes); - - if (!acyclic) - { - options.OutputWriter.WriteLine("Warning: graph is not a dag"); - } - - int num = sortedNodes.Count; - foreach (Block /*!*/ b in sortedNodes) - { - Contract.Assert(b != null); - priority.Add(b, num); - num--; - } - } - - public int getPriority(Block b) - { - Contract.Requires(b != null); - if (priority.ContainsKey(b)) - { - return priority[b]; - } - - return Int32.MaxValue; - } - - private void addSource(Block b) - { - Contract.Requires(b != null); - registerNode(b); - this.srcNodes.Add(b); - } - - private void addExit(Block b) - { - Contract.Requires(b != null); - registerNode(b); - this.exitNodes.Add(b); - } - - private void registerNode(Block b) - { - Contract.Requires(b != null); - if (!succEdges.ContainsKey(b)) - { - succEdges.Add(b, new HashSet()); - } - - if (!predEdges.ContainsKey(b)) - { - predEdges.Add(b, new HashSet()); - } - - nodes.Add(b); - } - - private void addEdge(Block src, Block tgt) - { - Contract.Requires(tgt != null); - Contract.Requires(src != null); - registerNode(src); - registerNode(tgt); - - succEdges[src].Add(tgt); - predEdges[tgt].Add(src); - } - } - - // Interprocedural Gen/Kill Analysis - public class InterProcGenKill - { - private CoreOptions options; - Program /*!*/ program; - - Dictionary /*!*/ - procICFG; - - Dictionary /*!*/ - name2Proc; - - Dictionary /*!*/> /*!*/ - callers; - - Graph /*!*/ - callGraph; - - Dictionary /*!*/ - procPriority; - - int maxBlocksInProc; - - WorkList /*!*/ - workList; - - Implementation /*!*/ - mainImpl; - - static Dictionary /*!*/> /*!*/ - varsLiveAtExit = new Dictionary /*!*/>(); - - static Dictionary /*!*/> /*!*/ - varsLiveAtEntry = new Dictionary /*!*/>(); - - static Dictionary /*!*/ - varsLiveSummary = new Dictionary(); - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(workList != null); - Contract.Invariant(mainImpl != null); - Contract.Invariant(program != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(procICFG)); - Contract.Invariant(cce.NonNullDictionaryAndValues(name2Proc)); - Contract.Invariant(cce.NonNullDictionaryAndValues(callers) && - Contract.ForAll(callers.Values, v => cce.NonNullElements(v))); - Contract.Invariant(cce.NonNullElements(callGraph.Nodes)); - Contract.Invariant(procPriority != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveAtEntry)); - Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveAtExit) && - Contract.ForAll(varsLiveAtExit.Values, v => cce.NonNullElements(v))); - Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveSummary)); - Contract.Invariant(cce.NonNullDictionaryAndValues(weightCacheAfterCall)); - Contract.Invariant(cce.NonNullDictionaryAndValues(weightCacheBeforeCall)); - } - - - [NotDelayed] - public InterProcGenKill(Implementation impl, Program program, CoreOptions options) - { - Contract.Requires(program != null); - Contract.Requires(impl != null); - this.program = program; - this.options = options; - procICFG = new Dictionary(); - name2Proc = new Dictionary(); - workList = new WorkList(); - this.callers = new Dictionary /*!*/>(); - this.callGraph = new Graph(); - this.procPriority = new Dictionary(); - this.maxBlocksInProc = 0; - this.mainImpl = impl; - - Dictionary /*!*/ - name2Impl = new Dictionary(); - varsLiveAtExit.Clear(); - varsLiveAtEntry.Clear(); - varsLiveSummary.Clear(); - - foreach (var decl in program.TopLevelDeclarations) - { - Contract.Assert(decl != null); - if (decl is Implementation) - { - Implementation /*!*/ - imp = (Implementation /*!*/) cce.NonNull(decl); - name2Impl[imp.Name] = imp; - } - else if (decl is Procedure) - { - Procedure /*!*/ - proc = cce.NonNull(decl as Procedure); - name2Proc[proc.Name] = proc; - } - } - - ImplementationControlFlowGraph /*!*/ - mainImplementationControlFlowGraph = new ImplementationControlFlowGraph(this.options, mainImpl); - Contract.Assert(mainImplementationControlFlowGraph != null); - procICFG.Add(mainImplementationControlFlowGraph.impl.Name, mainImplementationControlFlowGraph); - callGraph.AddSource(mainImplementationControlFlowGraph.impl.Name); - - List /*!*/ - procsToConsider = new List(); - procsToConsider.Add(mainImplementationControlFlowGraph); - - while (procsToConsider.Count != 0) - { - ImplementationControlFlowGraph /*!*/ - p = procsToConsider[0]; - Contract.Assert(p != null); - procsToConsider.RemoveAt(0); - - foreach (string /*!*/ callee in p.procsCalled.Keys) - { - Contract.Assert(callee != null); - if (!name2Impl.ContainsKey(callee)) - { - continue; - } - - callGraph.AddEdge(p.impl.Name, callee); - - if (maxBlocksInProc < p.nodes.Count) - { - maxBlocksInProc = p.nodes.Count; - } - - if (!callers.ContainsKey(callee)) - { - callers.Add(callee, new List()); - } - - foreach (Block /*!*/ b in p.procsCalled[callee]) - { - Contract.Assert(b != null); - callers[callee].Add(new WorkItem(p, b)); - } - - if (procICFG.ContainsKey(callee)) - { - continue; - } - - ImplementationControlFlowGraph /*!*/ - ncfg = new ImplementationControlFlowGraph(this.options, name2Impl[callee]); - Contract.Assert(ncfg != null); - procICFG.Add(callee, ncfg); - procsToConsider.Add(ncfg); - } - } - - callGraph.TarjanTopSort(out var acyclic, out var sortedNodes); - - Contract.Assert(acyclic); - - int cnt = 0; - for (int i = sortedNodes.Count - 1; i >= 0; i--) - { - string s = sortedNodes[i]; - if (s == null) - { - continue; - } - - procPriority.Add(s, cnt); - cnt++; - } - } - - public static HashSet /*!*/ GetVarsLiveAtExit(Implementation impl, Program prog) - { - Contract.Requires(prog != null); - Contract.Requires(impl != null); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - if (varsLiveAtExit.ContainsKey(impl.Name)) - { - return varsLiveAtExit[impl.Name]; - } - - // Return default: all globals and out params - HashSet /*!*/ - lv = new HashSet(); - foreach (Variable /*!*/ v in prog.GlobalVariables) - { - Contract.Assert(v != null); - lv.Add(v); - } - - foreach (Variable /*!*/ v in impl.OutParams) - { - Contract.Assert(v != null); - lv.Add(v); - } - - return lv; - } - - public static HashSet /*!*/ GetVarsLiveAtEntry(Implementation impl, Program prog) - { - Contract.Requires(prog != null); - Contract.Requires(impl != null); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - if (varsLiveAtEntry.ContainsKey(impl.Name)) - { - return varsLiveAtEntry[impl.Name]; - } - - // Return default: all globals and in params - HashSet /*!*/ - lv = new HashSet(); - foreach (Variable /*!*/ v in prog.GlobalVariables) - { - Contract.Assert(v != null); - lv.Add(v); - } - - foreach (Variable /*!*/ v in impl.InParams) - { - Contract.Assert(v != null); - lv.Add(v); - } - - return lv; - } - - public static bool HasSummary(string name) - { - Contract.Requires(name != null); - return varsLiveSummary.ContainsKey(name); - } - - public static HashSet /*!*/ - PropagateLiveVarsAcrossCall(CoreOptions options, CallCmd cmd, HashSet /*!*/ lvAfter) - { - Contract.Requires(cmd != null); - Contract.Requires(cce.NonNullElements(lvAfter)); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - Procedure /*!*/ - proc = cce.NonNull(cmd.Proc); - if (varsLiveSummary.ContainsKey(proc.Name)) - { - GenKillWeight /*!*/ - w1 = getWeightBeforeCall(cmd); - Contract.Assert(w1 != null); - GenKillWeight /*!*/ - w2 = varsLiveSummary[proc.Name]; - Contract.Assert(w2 != null); - GenKillWeight /*!*/ - w3 = getWeightAfterCall(cmd); - Contract.Assert(w3 != null); - GenKillWeight /*!*/ - w = GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); - Contract.Assert(w != null); - return w.getLiveVars(lvAfter); - } - - HashSet /*!*/ - ret = new HashSet(); - ret.UnionWith(lvAfter); - new LiveVariableAnalysis(options).Propagate(cmd, ret); - return ret; - } - - class WorkItem - { - public ImplementationControlFlowGraph /*!*/ - cfg; - - public Block /*!*/ - block; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cfg != null); - Contract.Invariant(block != null); - } - - - public WorkItem(ImplementationControlFlowGraph cfg, Block block) - { - Contract.Requires(block != null); - Contract.Requires(cfg != null); - this.cfg = cfg; - this.block = block; - } - - public GenKillWeight getWeightAfter() - { - Contract.Ensures(Contract.Result() != null); - return cfg.weightAfter[block]; - } - - public bool setWeightBefore(GenKillWeight w) - { - Contract.Requires(w != null); - GenKillWeight /*!*/ - prev = cfg.weightBefore[block]; - Contract.Assert(prev != null); - GenKillWeight /*!*/ - curr = GenKillWeight.combine(w, prev); - Contract.Assert(curr != null); - if (GenKillWeight.isEqual(prev, curr)) - { - return false; - } - - cfg.weightBefore[block] = curr; - return true; - } - - [Pure] - [Reads(ReadsAttribute.Reads.Nothing)] - public override bool Equals(object other) - { - WorkItem /*!*/ - wi = (WorkItem /*!*/) cce.NonNull(other); - return (wi.cfg == cfg && wi.block == block); - } - - [Pure] - public override int GetHashCode() - { - return 0; - } - - public string getLabel() - { - Contract.Ensures(Contract.Result() != null); - return cfg.impl.Name + "::" + block.Label; - } - } - - private void AddToWorkList(WorkItem wi) - { - Contract.Requires(wi != null); - int i = procPriority[wi.cfg.impl.Name]; - int j = wi.cfg.getPriority(wi.block); - int priority = (i * maxBlocksInProc) + j; - - workList.Add(wi, priority); - } - - private void AddToWorkListReverse(WorkItem wi) - { - Contract.Requires(wi != null); - int i = procPriority[wi.cfg.impl.Name]; - int j = wi.cfg.getPriority(wi.block); - int priority = (procPriority.Count - i) * maxBlocksInProc + j; - workList.Add(wi, priority); - } - - class WorkList - { - SortedList /*!*/ - priorities; - - HashSet /*!*/ - labels; - - Dictionary /*!*/> /*!*/ - workList; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(priorities != null); - Contract.Invariant(cce.NonNullElements(labels)); - Contract.Invariant(cce.NonNullDictionaryAndValues(workList) && - Contract.ForAll(workList.Values, v => cce.NonNullElements(v))); - } - - - public WorkList() - { - labels = new HashSet(); - priorities = new SortedList(); - workList = new Dictionary /*!*/>(); - } - - public void Add(WorkItem wi, int priority) - { - Contract.Requires(wi != null); - string /*!*/ - lab = wi.getLabel(); - Contract.Assert(lab != null); - if (labels.Contains(lab)) - { - // Already on worklist - return; - } - - labels.Add(lab); - if (!workList.ContainsKey(priority)) - { - workList.Add(priority, new List()); - } - - workList[priority].Add(wi); - if (!priorities.ContainsKey(priority)) - { - priorities.Add(priority, 0); - } - - priorities[priority] = priorities[priority] + 1; - } - - public WorkItem Get() - { - Contract.Ensures(Contract.Result() != null); - // Get minimum priority - int p = cce.NonNull(priorities.Keys)[0]; - priorities[p] = priorities[p] - 1; - if (priorities[p] == 0) - { - priorities.Remove(p); - } - - // Get a WI with this priority - WorkItem /*!*/ - wi = workList[p][0]; - Contract.Assert(wi != null); - workList[p].RemoveAt(0); - - // update labels - labels.Remove(wi.getLabel()); - return wi; - } - - public int Count - { - get { return labels.Count; } - } - } - - private GenKillWeight getSummary(CallCmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - Contract.Assert(cmd.Proc != null); - string /*!*/ - procName = cmd.Proc.Name; - Contract.Assert(procName != null); - if (procICFG.ContainsKey(procName)) - { - ImplementationControlFlowGraph /*!*/ - cfg = procICFG[procName]; - Contract.Assert(cfg != null); - return GenKillWeight.projectLocals(cfg.summary); - } - - { - Contract.Assert(false); - throw new cce.UnreachableException(); - } - } - - public void ComputeLiveVars(Implementation impl, Program /*!*/ prog) - { - Contract.Requires(prog != null); - Contract.Requires(impl != null); - InterProcGenKill /*!*/ - ipgk = new InterProcGenKill(impl, prog, options); - Contract.Assert(ipgk != null); - ipgk.Compute(); - } - - public void Compute() - { - // Put all exit nodes in the worklist - foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) - { - Contract.Assert(cfg != null); - foreach (Block /*!*/ eb in cfg.exitNodes) - { - Contract.Assert(eb != null); - WorkItem /*!*/ - wi = new WorkItem(cfg, eb); - Contract.Assert(wi != null); - cfg.weightAfter[eb] = GenKillWeight.one(); - AddToWorkList(wi); - } - } - - while (workList.Count != 0) - { - WorkItem /*!*/ - wi = workList.Get(); - Contract.Assert(wi != null); - process(wi); - } - - // Propagate LV to all procedures - foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) - { - Contract.Assert(cfg != null); - foreach (Block /*!*/ b in cfg.nodes) - { - Contract.Assert(b != null); - cfg.liveVarsAfter.Add(b, new HashSet()); - cfg.liveVarsBefore.Add(b, new HashSet()); - } - } - - ImplementationControlFlowGraph /*!*/ - mainCfg = procICFG[mainImpl.Name]; - Contract.Assert(mainCfg != null); - foreach (Block /*!*/ eb in mainCfg.exitNodes) - { - Contract.Assert(eb != null); - WorkItem /*!*/ - wi = new WorkItem(mainCfg, eb); - Contract.Assert(wi != null); - AddToWorkListReverse(wi); - } - - while (workList.Count != 0) - { - WorkItem /*!*/ - wi = workList.Get(); - Contract.Assert(wi != null); - ProcessLv(wi); - } - - // Set live variable info - foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) - { - Contract.Assert(cfg != null); - HashSet /*!*/ - lv = new HashSet(); - foreach (Block /*!*/ eb in cfg.exitNodes) - { - Contract.Assert(eb != null); - lv.UnionWith(cfg.liveVarsAfter[eb]); - } - - varsLiveAtExit.Add(cfg.impl.Name, lv); - lv = new HashSet(); - foreach (Block /*!*/ eb in cfg.srcNodes) - { - Contract.Assert(eb != null); - lv.UnionWith(cfg.liveVarsBefore[eb]); - } - - varsLiveAtEntry.Add(cfg.impl.Name, lv); - varsLiveSummary.Add(cfg.impl.Name, cfg.summary); - } - - /* - foreach(Block/*!*/ - /* b in mainImpl.Blocks){ -Contract.Assert(b != null); -//Set lv = cfg.weightBefore[b].getLiveVars(); -b.liveVarsBefore = procICFG[mainImpl.Name].liveVarsAfter[b]; -//foreach(GlobalVariable/*!*/ - /* v in program.GlobalVariables){Contract.Assert(v != null); -// b.liveVarsBefore.Add(v); -//} -} -*/ - } - - // Called when summaries have already been computed - private void ProcessLv(WorkItem wi) - { - Contract.Requires(wi != null); - ImplementationControlFlowGraph /*!*/ - cfg = wi.cfg; - Contract.Assert(cfg != null); - Block /*!*/ - block = wi.block; - Contract.Assert(block != null); - HashSet /*!*/ - lv = cfg.liveVarsAfter[block]; - Contract.Assert(cce.NonNullElements(lv)); - // Propagate backwards in the block - HashSet /*!*/ - prop = new HashSet(); - prop.UnionWith(lv); - for (int i = block.Cmds.Count - 1; i >= 0; i--) - { - Cmd /*!*/ - cmd = block.Cmds[i]; - Contract.Assert(cmd != null); - if (cmd is CallCmd) - { - string /*!*/ - procName = cce.NonNull(cce.NonNull((CallCmd) cmd).Proc).Name; - Contract.Assert(procName != null); - if (procICFG.ContainsKey(procName)) - { - ImplementationControlFlowGraph /*!*/ - callee = procICFG[procName]; - Contract.Assert(callee != null); - // Inter propagation - // Remove local variables; add return variables - HashSet /*!*/ - elv = new HashSet(); - foreach (Variable /*!*/ v in prop) - { - Contract.Assert(v != null); - if (v is GlobalVariable) - { - elv.Add(v); - } - } - - foreach (Variable /*!*/ v in callee.impl.OutParams) - { - Contract.Assert(v != null); - elv.Add(v); - } - - foreach (Block /*!*/ eb in callee.exitNodes) - { - Contract.Assert(eb != null); - callee.liveVarsAfter[eb].UnionWith(elv); - // TODO: check if modified before inserting - AddToWorkListReverse(new WorkItem(callee, eb)); - } - - // Continue with intra propagation - GenKillWeight /*!*/ - summary = GetWeightCall(cce.NonNull((CallCmd /*!*/) cmd)); - prop = summary.getLiveVars(prop); - } - else - { - new LiveVariableAnalysis(options).Propagate(cmd, prop); - } - } - else - { - new LiveVariableAnalysis(options).Propagate(cmd, prop); - } - } - - cfg.liveVarsBefore[block].UnionWith(prop); - - foreach (Block /*!*/ b in cfg.predEdges[block]) - { - Contract.Assert(b != null); - HashSet /*!*/ - prev = cfg.liveVarsAfter[b]; - Contract.Assert(cce.NonNullElements(prev)); - HashSet /*!*/ - curr = new HashSet(prev); - curr.UnionWith(cfg.liveVarsBefore[block]); - Contract.Assert(cce.NonNullElements(curr)); - if (curr.Count != prev.Count) - { - cfg.liveVarsAfter[b] = curr; - AddToWorkListReverse(new WorkItem(cfg, b)); - } - } - } - - private void process(WorkItem wi) - { - Contract.Requires(wi != null); - GenKillWeight /*!*/ - w = wi.getWeightAfter(); - Contract.Assert(w != null); - - for (int i = wi.block.Cmds.Count - 1; i >= 0; i--) - { - Cmd /*!*/ - c = wi.block.Cmds[i]; - Contract.Assert(c != null); - if (c is CallCmd && procICFG.ContainsKey(cce.NonNull(cce.NonNull((CallCmd) c).Proc).Name)) - { - w = GenKillWeight.extend(GetWeightCall(cce.NonNull((CallCmd) c)), w); - } - else - { - GenKillWeight /*!*/ - cweight = GetWeight(c, wi.cfg.impl, program); - Contract.Assert(cweight != null); - w = GenKillWeight.extend(cweight, w); - } - } - - bool change = wi.setWeightBefore(w); - - if (change && wi.cfg.srcNodes.Contains(wi.block)) - { - GenKillWeight /*!*/ - prev = wi.cfg.summary; - Contract.Assert(prev != null); - GenKillWeight /*!*/ - curr = GenKillWeight.combine(prev, wi.cfg.weightBefore[wi.block]); - Contract.Assert(curr != null); - if (!GenKillWeight.isEqual(prev, curr)) - { - wi.cfg.summary = curr; - // push callers onto the worklist - if (callers.ContainsKey(wi.cfg.impl.Name)) - { - foreach (WorkItem /*!*/ caller in callers[wi.cfg.impl.Name]) - { - Contract.Assert(caller != null); - AddToWorkList(caller); - } - } - } - } - - foreach (Block /*!*/ b in wi.cfg.predEdges[wi.block]) - { - Contract.Assert(b != null); - GenKillWeight /*!*/ - prev = wi.cfg.weightAfter[b]; - Contract.Assert(prev != null); - GenKillWeight /*!*/ - curr = GenKillWeight.combine(prev, w); - Contract.Assert(curr != null); - if (!GenKillWeight.isEqual(prev, curr)) - { - wi.cfg.weightAfter[b] = curr; - AddToWorkList(new WorkItem(wi.cfg, b)); - } - } - } - - static Dictionary /*!*/ - weightCache = new Dictionary(); - - private GenKillWeight GetWeight(Cmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - return GetWeight(cmd, null, null); - } - - private GenKillWeight GetWeightCall(CallCmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - GenKillWeight /*!*/ - w1 = getWeightBeforeCall(cmd); - GenKillWeight /*!*/ - w2 = getSummary(cmd); - GenKillWeight /*!*/ - w3 = getWeightAfterCall(cmd); - Contract.Assert(w1 != null); - Contract.Assert(w2 != null); - Contract.Assert(w3 != null); - return GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); - } - - private GenKillWeight GetWeight(Cmd cmd, Implementation impl, Program prog) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - - if (weightCache.ContainsKey(cmd)) - { - return weightCache[cmd]; - } - - HashSet /*!*/ - gen = new HashSet(); - HashSet /*!*/ - kill = new HashSet(); - GenKillWeight /*!*/ - ret; - - if (cmd is AssignCmd) - { - AssignCmd /*!*/ - assignCmd = (AssignCmd) cmd; - Contract.Assert(cmd != null); - // I must first iterate over all the targets and remove the live ones. - // After the removals are done, I must add the variables referred on - // the right side of the removed targets - foreach (AssignLhs /*!*/ lhs in assignCmd.Lhss) - { - Contract.Assert(lhs != null); - Variable var = lhs.DeepAssignedVariable; - if (var != null) - { - if (lhs is SimpleAssignLhs) - { - // we should only remove non-map target variables because there is an implicit - // read of a map variable in an assignment to it - kill.Add(var); - } - } - } - - int index = 0; - foreach (Expr /*!*/ expr in assignCmd.Rhss) - { - Contract.Assert(expr != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(expr); - gen.UnionWith(collector.usedVars); - AssignLhs lhs = assignCmd.Lhss[index]; - if (lhs is MapAssignLhs) - { - // If the target is a map, then all indices are also read - MapAssignLhs malhs = (MapAssignLhs) lhs; - foreach (Expr e in malhs.Indexes) - { - VariableCollector /*!*/ - c = new VariableCollector(); - c.Visit(e); - gen.UnionWith(c.usedVars); - } - } - - index++; - } - - ret = new GenKillWeight(gen, kill); - } - else if (cmd is HavocCmd) - { - HavocCmd /*!*/ - havocCmd = (HavocCmd) cce.NonNull(cmd); - foreach (IdentifierExpr /*!*/ expr in havocCmd.Vars) - { - Contract.Assert(expr != null); - if (expr.Decl != null) - { - kill.Add(expr.Decl); - } - } - - ret = new GenKillWeight(gen, kill); - } - else if (cmd is PredicateCmd) - { - Contract.Assert((cmd is AssertCmd || cmd is AssumeCmd)); - PredicateCmd /*!*/ - predicateCmd = (PredicateCmd) cce.NonNull(cmd); - if (predicateCmd.Expr is LiteralExpr && prog != null && impl != null) - { - LiteralExpr le = (LiteralExpr) predicateCmd.Expr; - if (le.IsFalse) - { - var globals = prog.GlobalVariables; - Contract.Assert(cce.NonNullElements(globals)); - foreach (Variable /*!*/ v in globals) - { - Contract.Assert(v != null); - kill.Add(v); - } - - foreach (Variable /*!*/ v in impl.LocVars) - { - Contract.Assert(v != null); - kill.Add(v); - } - - foreach (Variable /*!*/ v in impl.OutParams) - { - Contract.Assert(v != null); - kill.Add(v); - } - } - } - else - { - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(predicateCmd.Expr); - gen.UnionWith(collector.usedVars); - } - - ret = new GenKillWeight(gen, kill); - } - else if (cmd is CommentCmd) - { - ret = new GenKillWeight(gen, kill); - // comments are just for debugging and don't affect verification - } - else if (cmd is SugaredCmd) - { - SugaredCmd /*!*/ - sugCmd = (SugaredCmd) cmd; - Contract.Assert(sugCmd != null); - ret = GetWeight(sugCmd.GetDesugaring(options), impl, prog); - } - else if (cmd is StateCmd) - { - StateCmd /*!*/ - stCmd = (StateCmd) cmd; - Contract.Assert(stCmd != null); - List /*!*/ - cmds = stCmd.Cmds; - Contract.Assert(cmds != null); - int len = cmds.Count; - ret = GenKillWeight.one(); - for (int i = len - 1; i >= 0; i--) - { - GenKillWeight /*!*/ - w = GetWeight(cmds[i], impl, prog); - Contract.Assert(w != null); - ret = GenKillWeight.extend(w, ret); - } - - foreach (Variable /*!*/ v in stCmd.Locals) - { - Contract.Assert(v != null); - kill.Add(v); - } - - ret = GenKillWeight.extend(new GenKillWeight(gen, kill), ret); - } - else - { - { - Contract.Assert(false); - throw new cce.UnreachableException(); - } - } - - weightCache[cmd] = ret; - return ret; - } - - static Dictionary /*!*/ - weightCacheAfterCall = new Dictionary(); - - static Dictionary /*!*/ - weightCacheBeforeCall = new Dictionary(); - - private static GenKillWeight getWeightAfterCall(Cmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - - if (weightCacheAfterCall.ContainsKey(cmd)) - { - return weightCacheAfterCall[cmd]; - } - - HashSet /*!*/ - gen = new HashSet(); - HashSet /*!*/ - kill = new HashSet(); - - Contract.Assert(cmd is CallCmd); - CallCmd /*!*/ - ccmd = cce.NonNull((CallCmd) cmd); - - foreach (IdentifierExpr /*!*/ ie in ccmd.Outs) - { - Contract.Assert(ie != null); - if (ie.Decl != null) - { - kill.Add(ie.Decl); - } - } - - // Variables in ensures are considered as "read" - foreach (Ensures /*!*/ re in cce.NonNull(ccmd.Proc).Ensures) - { - Contract.Assert(re != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(re.Condition); - foreach (Variable /*!*/ v in collector.usedVars) - { - Contract.Assert(v != null); - if (v is GlobalVariable) - { - gen.Add(v); - } - } - } - - GenKillWeight /*!*/ - ret = new GenKillWeight(gen, kill); - Contract.Assert(ret != null); - weightCacheAfterCall[cmd] = ret; - return ret; - } - - private static GenKillWeight getWeightBeforeCall(Cmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - Contract.Assert((cmd is CallCmd)); - if (weightCacheBeforeCall.ContainsKey(cmd)) - { - return weightCacheBeforeCall[cmd]; - } - - HashSet /*!*/ - gen = new HashSet(); - HashSet /*!*/ - kill = new HashSet(); - CallCmd /*!*/ - ccmd = cce.NonNull((CallCmd /*!*/) cmd); - - foreach (Expr /*!*/ expr in ccmd.Ins) - { - Contract.Assert(expr != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(expr); - gen.UnionWith(collector.usedVars); - } - - Contract.Assert(ccmd.Proc != null); - - // Variables in requires are considered as "read" - foreach (Requires /*!*/ re in ccmd.Proc.Requires) - { - Contract.Assert(re != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(re.Condition); - foreach (Variable /*!*/ v in collector.usedVars) - { - Contract.Assert(v != null); - if (v is GlobalVariable) - { - gen.Add(v); - } - } - } - - // Old variables in ensures are considered as "read" - foreach (Ensures /*!*/ re in ccmd.Proc.Ensures) - { - Contract.Assert(re != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(re.Condition); - foreach (Variable /*!*/ v in collector.oldVarsUsed) - { - Contract.Assert(v != null); - if (v is GlobalVariable) - { - gen.Add(v); - } - } - } - - GenKillWeight /*!*/ - ret = new GenKillWeight(gen, kill); - Contract.Assert(ret != null); - weightCacheAfterCall[cmd] = ret; - return ret; - } - } -} \ No newline at end of file From 07ad2937640eb0bad8b76a6df9b315af16245077 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 18:16:43 +0200 Subject: [PATCH 12/42] Clean up code in BlockCoalescer and remove duplicate feature in BlockTransformations --- Source/Core/Analysis/BlockCoalescer.cs | 163 +++++++++----------- Source/ExecutionEngine/ExecutionEngine.cs | 4 +- Source/VCGeneration/BlockTransformations.cs | 49 +----- 3 files changed, 84 insertions(+), 132 deletions(-) diff --git a/Source/Core/Analysis/BlockCoalescer.cs b/Source/Core/Analysis/BlockCoalescer.cs index 959ff9275..743ecdcb8 100644 --- a/Source/Core/Analysis/BlockCoalescer.cs +++ b/Source/Core/Analysis/BlockCoalescer.cs @@ -1,3 +1,5 @@ +#nullable enable + using System.Collections.Generic; using System.Diagnostics.Contracts; @@ -7,159 +9,148 @@ public class BlockCoalescer : ReadOnlyVisitor { public static void CoalesceBlocks(Program program) { - Contract.Requires(program != null); - BlockCoalescer blockCoalescer = new BlockCoalescer(); + var blockCoalescer = new BlockCoalescer(); blockCoalescer.Visit(program); } - private static HashSet /*!*/ ComputeMultiPredecessorBlocks(Implementation /*!*/ impl) + private static HashSet ComputeMultiPredecessorBlocks(Block rootBlock) { - Contract.Requires(impl != null); Contract.Ensures(cce.NonNullElements(Contract.Result>())); - HashSet visitedBlocks = new HashSet(); - HashSet multiPredBlocks = new HashSet(); - Stack dfsStack = new Stack(); - dfsStack.Push(impl.Blocks[0]); + var visitedBlocks = new HashSet(); + var result = new HashSet(); + var dfsStack = new Stack(); + dfsStack.Push(rootBlock); while (dfsStack.Count > 0) { - Block /*!*/ - b = dfsStack.Pop(); - Contract.Assert(b != null); - if (visitedBlocks.Contains(b)) + var /*!*/ block = dfsStack.Pop(); + Contract.Assert(block != null); + if (!visitedBlocks.Add(block)) { - multiPredBlocks.Add(b); + result.Add(block); continue; } - visitedBlocks.Add(b); - if (b.TransferCmd == null) + if (block.TransferCmd == null) { continue; } - if (b.TransferCmd is ReturnCmd) + if (block.TransferCmd is ReturnCmd) { continue; } - Contract.Assert(b.TransferCmd is GotoCmd); - GotoCmd gotoCmd = (GotoCmd) b.TransferCmd; + Contract.Assert(block.TransferCmd is GotoCmd); + var gotoCmd = (GotoCmd) block.TransferCmd; if (gotoCmd.labelTargets == null) { continue; } - foreach (Block /*!*/ succ in gotoCmd.labelTargets) + foreach (var /*!*/ succ in gotoCmd.labelTargets) { Contract.Assert(succ != null); dfsStack.Push(succ); } } - return multiPredBlocks; + return result; } public override Implementation VisitImplementation(Implementation impl) { - //Contract.Requires(impl != null); - Contract.Ensures(Contract.Result() != null); - //Console.WriteLine("Procedure {0}", impl.Name); - //Console.WriteLine("Initial number of blocks = {0}", impl.Blocks.Count); - - HashSet multiPredBlocks = ComputeMultiPredecessorBlocks(impl); - Contract.Assert(cce.NonNullElements(multiPredBlocks)); - HashSet visitedBlocks = new HashSet(); - HashSet removedBlocks = new HashSet(); - Stack dfsStack = new Stack(); - dfsStack.Push(impl.Blocks[0]); - while (dfsStack.Count > 0) + var newBlocks = CoalesceFromRootBlock(impl.Blocks); + impl.Blocks = newBlocks; + foreach (var block in impl.Blocks) + { + if (block.TransferCmd is ReturnCmd) + { + continue; + } + + var gotoCmd = (GotoCmd)block.TransferCmd; + gotoCmd.labelNames = new List(); + foreach (var successor in gotoCmd.labelTargets) + { + gotoCmd.labelNames.Add(successor.Label); + } + } + return impl; + } + + public static List CoalesceFromRootBlock(List blocks) + { + var multiPredecessorBlocks = ComputeMultiPredecessorBlocks(blocks[0]); + Contract.Assert(cce.NonNullElements(multiPredecessorBlocks)); + var visitedBlocks = new HashSet(); + var removedBlocks = new HashSet(); + var toVisit = new Stack(); + toVisit.Push(blocks[0]); + while (toVisit.Count > 0) { - Block /*!*/ - b = dfsStack.Pop(); - Contract.Assert(b != null); - if (visitedBlocks.Contains(b)) + var block = toVisit.Pop(); + Contract.Assert(block != null); + if (!visitedBlocks.Add(block)) { continue; } - visitedBlocks.Add(b); - if (b.TransferCmd == null) + if (block.TransferCmd == null) { continue; } - if (b.TransferCmd is ReturnCmd) + if (block.TransferCmd is ReturnCmd) { continue; } - Contract.Assert(b.TransferCmd is GotoCmd); - GotoCmd gotoCmd = (GotoCmd) b.TransferCmd; + Contract.Assert(block.TransferCmd is GotoCmd); + var gotoCmd = (GotoCmd) block.TransferCmd; if (gotoCmd.labelTargets == null) { continue; } - if (gotoCmd.labelTargets.Count == 1) + if (gotoCmd.labelTargets.Count != 1) { - Block /*!*/ - succ = cce.NonNull(gotoCmd.labelTargets[0]); - if (!multiPredBlocks.Contains(succ)) + foreach (var aSuccessor in gotoCmd.labelTargets) { - foreach (Cmd /*!*/ cmd in succ.Cmds) - { - Contract.Assert(cmd != null); - b.Cmds.Add(cmd); - } - - b.TransferCmd = succ.TransferCmd; - if (!b.tok.IsValid && succ.tok.IsValid) - { - b.tok = succ.tok; - b.Label = succ.Label; - } - - removedBlocks.Add(succ); - dfsStack.Push(b); - visitedBlocks.Remove(b); - continue; + Contract.Assert(aSuccessor != null); + toVisit.Push(aSuccessor); } + continue; } - foreach (Block /*!*/ succ in gotoCmd.labelTargets) + var successor = cce.NonNull(gotoCmd.labelTargets[0]); + if (multiPredecessorBlocks.Contains(successor)) { - Contract.Assert(succ != null); - dfsStack.Push(succ); + toVisit.Push(successor); + continue; } - } - List newBlocks = new List(); - foreach (Block /*!*/ b in impl.Blocks) - { - Contract.Assert(b != null); - if (visitedBlocks.Contains(b) && !removedBlocks.Contains(b)) + block.Cmds.AddRange(successor.Cmds); + block.TransferCmd = successor.TransferCmd; + if (!block.tok.IsValid && successor.tok.IsValid) { - newBlocks.Add(b); + block.tok = successor.tok; + block.Label = successor.Label; } + + removedBlocks.Add(successor); + toVisit.Push(block); + visitedBlocks.Remove(block); } - impl.Blocks = newBlocks; - foreach (Block b in impl.Blocks) + var newBlocks = new List(); + foreach (var block in blocks) { - if (b.TransferCmd is ReturnCmd) + Contract.Assert(block != null); + if (visitedBlocks.Contains(block) && !removedBlocks.Contains(block)) { - continue; - } - - GotoCmd gotoCmd = b.TransferCmd as GotoCmd; - gotoCmd.labelNames = new List(); - foreach (Block succ in gotoCmd.labelTargets) - { - gotoCmd.labelNames.Add(succ.Label); + newBlocks.Add(block); } } - - // Console.WriteLine("Final number of blocks = {0}", impl.Blocks.Count); - return impl; + return newBlocks; } } \ No newline at end of file diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 4dcfdb023..cefaf3dfe 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -221,7 +221,7 @@ public void CoalesceBlocks(Program program) Options.OutputWriter.WriteLine("Coalescing blocks..."); } - Microsoft.Boogie.BlockCoalescer.CoalesceBlocks(program); + BlockCoalescer.CoalesceBlocks(program); } } @@ -237,7 +237,7 @@ public void CollectModSets(Program program) public void EliminateDeadVariables(Program program) { - Microsoft.Boogie.UnusedVarEliminator.Eliminate(program); + UnusedVarEliminator.Eliminate(program); } public void PrintBplFile(string filename, Program program, diff --git a/Source/VCGeneration/BlockTransformations.cs b/Source/VCGeneration/BlockTransformations.cs index 9a423f437..2f5d52ee2 100644 --- a/Source/VCGeneration/BlockTransformations.cs +++ b/Source/VCGeneration/BlockTransformations.cs @@ -21,7 +21,10 @@ public static void Optimize(List blocks) { DeleteBlocksNotLeadingToAssertions(blocks); DeleteUselessBlocks(blocks); - MergeOneToOneBlocks(blocks); + + var coalesced = BlockCoalescer.CoalesceFromRootBlock(blocks); + blocks.Clear(); + blocks.AddRange(coalesced); } private static void StopControlFlowAtAssumeFalse(Block b) @@ -59,7 +62,7 @@ public static void DeleteBlocksNotLeadingToAssertions(List blocks) { var gtc = new GotoCmd(exit.tok, exit.labelTargets.Where(l => interestingBlocks.Contains(l)).ToList()); currentBlock.TransferCmd = gtc; - interesting = interesting || gtc.labelTargets.Count != 0; + interesting = gtc.labelTargets.Count != 0; } else { @@ -87,11 +90,6 @@ private static bool ContainsAssert(Block b) return b.Cmds.Exists(IsNonTrivialAssert); } - private static void OptimizeBlocks(List blocks) { - DeleteUselessBlocks(blocks); - MergeOneToOneBlocks(blocks); - } - private static void DeleteUselessBlocks(List blocks) { var toVisit = new HashSet(); var removed = new HashSet(); @@ -149,41 +147,4 @@ private static void DeleteUselessBlocks(List blocks) { } } } - - private static void MergeOneToOneBlocks(List blocks) { - var stack = new Stack(); - foreach (var block in blocks) { - if (!block.Predecessors.Any()) { - stack.Push(block); - } - } - while (stack.Any()) { - var current = stack.Pop(); - if (current.TransferCmd is not GotoCmd gotoCmd) { - continue; - } - - if (gotoCmd.labelTargets.Count != 1) { - foreach (var aNext in gotoCmd.labelTargets) { - stack.Push(aNext); - } - continue; - } - - var next = gotoCmd.labelTargets.Single(); - if (next.Predecessors.Count != 1) { - stack.Push(next); - continue; - } - - blocks.Remove(next); - current.Cmds.AddRange(next.Cmds); - current.TransferCmd = next.TransferCmd; - foreach (var nextTarget in current.Exits()) { - nextTarget.Predecessors.Remove(next); - nextTarget.Predecessors.Add(current); - } - stack.Push(current); - } - } } \ No newline at end of file From e08091b0c71946a95c117a097448e94481cb0973 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 21:54:07 +0200 Subject: [PATCH 13/42] Fix test --- Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs index f0a8b89ea..c35e5bb9a 100644 --- a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs @@ -105,9 +105,9 @@ procedure Procedure(y: int) var tasks = engine.GetVerificationTasks(program); // The first split is empty. Maybe it can be optimized away - Assert.AreEqual(5, tasks.Count); + Assert.AreEqual(4, tasks.Count); - var outcomes = new List { SolverOutcome.Valid, SolverOutcome.Invalid, SolverOutcome.Valid, SolverOutcome.Invalid, SolverOutcome.Valid }; + var outcomes = new List { SolverOutcome.Invalid, SolverOutcome.Valid, SolverOutcome.Invalid, SolverOutcome.Valid }; for (var index = 0; index < outcomes.Count; index++) { var result0 = await tasks[index].TryRun()!.ToTask(); From 83ae3683f3c79e077931b739ae07484f3220199d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 24 Jul 2024 22:01:20 +0200 Subject: [PATCH 14/42] Refactoring --- Source/VCGeneration/BlockTransformations.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/VCGeneration/BlockTransformations.cs b/Source/VCGeneration/BlockTransformations.cs index 2f5d52ee2..783baa778 100644 --- a/Source/VCGeneration/BlockTransformations.cs +++ b/Source/VCGeneration/BlockTransformations.cs @@ -27,16 +27,24 @@ public static void Optimize(List blocks) { blocks.AddRange(coalesced); } - private static void StopControlFlowAtAssumeFalse(Block b) + private static void StopControlFlowAtAssumeFalse(Block block) { - var firstFalseIdx = b.Cmds.FindIndex(IsAssumeFalse); + var firstFalseIdx = block.Cmds.FindIndex(IsAssumeFalse); if (firstFalseIdx == -1) { return; } - b.Cmds = b.Cmds.Take(firstFalseIdx + 1).ToList(); - b.TransferCmd = b.TransferCmd is GotoCmd ? new ReturnCmd(b.tok) : b.TransferCmd; + block.Cmds = block.Cmds.Take(firstFalseIdx + 1).ToList(); + if (block.TransferCmd is not GotoCmd gotoCmd) + { + return; + } + + block.TransferCmd = new ReturnCmd(block.tok); + foreach (var target in gotoCmd.labelTargets) { + target.Predecessors.Remove(block); + } } private static bool IsAssumeFalse (Cmd c) { return c is AssumeCmd { Expr: LiteralExpr { asBool: false } }; } From 9e868e4db61c71f3ed49a3f14c1909ebc4f5c500 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 25 Jul 2024 10:24:32 +0200 Subject: [PATCH 15/42] Remove unused import --- Source/Core/AST/Program.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index 5684c315f..a171b549d 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; -using System.Text; using Microsoft.Boogie.GraphUtil; namespace Microsoft.Boogie; From e522dbff77119af1a3bcbed6591ba14635b6292b Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 25 Jul 2024 13:09:32 +0200 Subject: [PATCH 16/42] Fixes --- Source/Core/AST/Program.cs | 20 ++++--- Source/Core/Analysis/BlockCoalescer.cs | 5 ++ Source/VCGeneration/ManualSplitFinder.cs | 67 ++++++++++++------------ Test/commandline/SplitOnEveryAssert.bpl | 27 +++++----- 4 files changed, 64 insertions(+), 55 deletions(-) diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index a171b549d..a347a4500 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -500,26 +500,30 @@ public static Graph BuildCallGraph(CoreOptions options, Program public static Graph GraphFromBlocks(List blocks, bool forward = true) { - Graph g = new Graph(); + var result = new Graph(); + if (!blocks.Any()) + { + return result; + } void AddEdge(Block a, Block b) { Contract.Assert(a != null && b != null); if (forward) { - g.AddEdge(a, b); + result.AddEdge(a, b); } else { - g.AddEdge(b, a); + result.AddEdge(b, a); } } - g.AddSource(cce.NonNull(blocks[0])); // there is always at least one node in the graph - foreach (Block b in blocks) + result.AddSource(cce.NonNull(blocks[0])); // there is always at least one node in the graph + foreach (var block in blocks) { - if (b.TransferCmd is GotoCmd gtc) + if (block.TransferCmd is GotoCmd gtc) { Contract.Assume(gtc.labelTargets != null); - gtc.labelTargets.ForEach(dest => AddEdge(b, dest)); + gtc.labelTargets.ForEach(dest => AddEdge(block, dest)); } } - return g; + return result; } public static Graph /*!*/ GraphFromImpl(Implementation impl, bool forward = true) diff --git a/Source/Core/Analysis/BlockCoalescer.cs b/Source/Core/Analysis/BlockCoalescer.cs index 743ecdcb8..f1065f65b 100644 --- a/Source/Core/Analysis/BlockCoalescer.cs +++ b/Source/Core/Analysis/BlockCoalescer.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; namespace Microsoft.Boogie; @@ -80,6 +81,10 @@ public override Implementation VisitImplementation(Implementation impl) public static List CoalesceFromRootBlock(List blocks) { + if (!blocks.Any()) + { + return blocks; + } var multiPredecessorBlocks = ComputeMultiPredecessorBlocks(blocks[0]); Contract.Assert(cce.NonNullElements(multiPredecessorBlocks)); var visitedBlocks = new HashSet(); diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index 5b73a1266..581ef0bf8 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -1,3 +1,4 @@ +#nullable enable using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; @@ -20,9 +21,6 @@ public static IEnumerable FocusAndSplit(Program program, VCGenOptio var splitOnEveryAssert = initialSplit.Options.VcsSplitOnEveryAssert; initialSplit.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); - if (splitOnEveryAssert) { - return SplitEachAssert(program, initialSplit); - } var splitPoints = new Dictionary>(); foreach (var block in initialSplit.Blocks) { @@ -41,10 +39,13 @@ public static IEnumerable FocusAndSplit(Program program, VCGenOptio var entryBlockHasSplit = splitPoints.ContainsKey(entryPoint); var firstSplitBlocks = DoPreAssignedManualSplit(initialSplit.Options, initialSplit.Blocks, blockAssignments, -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert); - splits.Add(new ManualSplit(initialSplit.Options, () => { - BlockTransformations.Optimize(firstSplitBlocks); - return firstSplitBlocks; - }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, initialSplit.Token)); + if (firstSplitBlocks != null) + { + splits.Add(new ManualSplit(initialSplit.Options, () => { + BlockTransformations.Optimize(firstSplitBlocks); + return firstSplitBlocks; + }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, initialSplit.Token)); + } foreach (var block in initialSplit.Blocks) { var tokens = splitPoints.GetValueOrDefault(block); if (tokens == null) { @@ -55,36 +56,20 @@ public static IEnumerable FocusAndSplit(Program program, VCGenOptio var token = tokens[i]; bool lastSplitInBlock = i == tokens.Count - 1; var newBlocks = DoPreAssignedManualSplit(initialSplit.Options, initialSplit.Blocks, blockAssignments, i, block, lastSplitInBlock, splitOnEveryAssert); - splits.Add(new ManualSplit(initialSplit.Options, - () => { - BlockTransformations.Optimize(newBlocks); - return newBlocks; - }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, token)); + if (newBlocks != null) + { + splits.Add(new ManualSplit(initialSplit.Options, + () => { + BlockTransformations.Optimize(newBlocks); + return newBlocks; + }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, token)); + } } } } return splits; } - private static List SplitEachAssert(Program program, ManualSplit initialSplit) - { - var result = new List(); - foreach (var block in initialSplit.Blocks) { - foreach (Cmd command in block.Cmds) { - if (command is AssertCmd assertCmd) { - var newBlocks = SplitOnAssert(initialSplit.Options, initialSplit.Blocks, assertCmd); - result.Add(new ManualSplit(initialSplit.Options, - () => { - BlockTransformations.Optimize(newBlocks); - return newBlocks; - }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, assertCmd.tok)); - } - } - } - - return result; - } - private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { return c is PredicateCmd p && QKeyValue.FindBoolAttribute(p.Attributes, "split_here") || c is AssertCmd && splitOnEveryAssert; @@ -137,11 +122,12 @@ private static List SplitOnAssert(VCGenOptions options, List oldBl AddBlockJumps(oldBlocks, oldToNewBlockMap); return newBlocks; } - private static List DoPreAssignedManualSplit(VCGenOptions options, List blocks, + private static List? DoPreAssignedManualSplit(VCGenOptions options, List blocks, Dictionary blockAssignments, int splitNumberWithinBlock, Block containingBlock, bool lastSplitInBlock, bool splitOnEveryAssert) { var newBlocks = new List(blocks.Count); // Copies of the original blocks //var duplicator = new Duplicator(); + var assertionCount = 0; var oldToNewBlockMap = new Dictionary(blocks.Count); // Maps original blocks to their new copies in newBlocks foreach (var currentBlock in blocks) { var newBlock = new Block(); @@ -159,14 +145,23 @@ private static List DoPreAssignedManualSplit(VCGenOptions options, List(); - foreach (Cmd command in currentBlock.Cmds) { + foreach (var command in currentBlock.Cmds) { verify = !ShouldSplitHere(command, splitOnEveryAssert) && verify; + if (verify && command is AssertCmd) + { + assertionCount++; + } newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(options, command)); } newBlock.Cmds = newCmds; @@ -174,6 +169,12 @@ private static List DoPreAssignedManualSplit(VCGenOptions options, List CommandTransformations.AssertIntoAssume(options, x)).ToList(); } } + + if (assertionCount == 0) + { + return null; + } + // Patch the edges between the new blocks AddBlockJumps(blocks, oldToNewBlockMap); return newBlocks; diff --git a/Test/commandline/SplitOnEveryAssert.bpl b/Test/commandline/SplitOnEveryAssert.bpl index 605492ced..cce33f147 100644 --- a/Test/commandline/SplitOnEveryAssert.bpl +++ b/Test/commandline/SplitOnEveryAssert.bpl @@ -3,20 +3,19 @@ // RUN: %OutputCheck --file-to-check "%t" "%s" // CHECK: Verifying Ex ... -// CHECK: checking split 1/12, .* -// CHECK: checking split 2/12, .* -// CHECK: checking split 3/12, .* -// CHECK: checking split 4/12, .* -// CHECK: --> split #4 done, \[.* s\] Invalid -// CHECK: checking split 5/12, .* -// CHECK: checking split 6/12, .* -// CHECK: checking split 7/12, .* -// CHECK: checking split 8/12, .* -// CHECK: checking split 9/12, .* -// CHECK: checking split 10/12, .* -// CHECK: checking split 11/12, .* -// CHECK: checking split 12/12, .* -// CHECK-L: SplitOnEveryAssert.bpl(32,5): Error: this assertion could not be proved +// CHECK: checking split 1/11, .* +// CHECK: checking split 2/11, .* +// CHECK: checking split 3/11, .* +// CHECK: --> split #3 done, \[.* s\] Invalid +// CHECK: checking split 4/11, .* +// CHECK: checking split 5/11, .* +// CHECK: checking split 6/11, .* +// CHECK: checking split 7/11, .* +// CHECK: checking split 8/11, .* +// CHECK: checking split 9/11, .* +// CHECK: checking split 10/11, .* +// CHECK: checking split 11/11, .* +// CHECK-L: SplitOnEveryAssert.bpl(31,5): Error: this assertion could not be proved procedure Ex() returns (y: int) ensures y >= 0; From 9cd2a55d6d7456fcc23a46084005f8f7a687c842 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 25 Jul 2024 14:08:41 +0200 Subject: [PATCH 17/42] Fix --- Source/VCGeneration/BlockTransformations.cs | 3 ++- Source/VCGeneration/ManualSplitFinder.cs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/VCGeneration/BlockTransformations.cs b/Source/VCGeneration/BlockTransformations.cs index 783baa778..b6472e319 100644 --- a/Source/VCGeneration/BlockTransformations.cs +++ b/Source/VCGeneration/BlockTransformations.cs @@ -94,9 +94,10 @@ public static void DeleteBlocksNotLeadingToAssertions(List blocks) private static bool ContainsAssert(Block b) { - bool IsNonTrivialAssert (Cmd c) { return c is AssertCmd ac && !(ac.Expr is LiteralExpr le && le.asBool); } return b.Cmds.Exists(IsNonTrivialAssert); } + + public static bool IsNonTrivialAssert (Cmd c) { return c is AssertCmd ac && !(ac.Expr is LiteralExpr { asBool: true }); } private static void DeleteUselessBlocks(List blocks) { var toVisit = new HashSet(); diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index 581ef0bf8..fc1898745 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -146,7 +146,7 @@ private static List SplitOnAssert(VCGenOptions options, List oldBl verify = splitCount == splitNumberWithinBlock; } - if (verify && command is AssertCmd) + if (verify && BlockTransformations.IsNonTrivialAssert(command)) { assertionCount++; } @@ -158,7 +158,7 @@ private static List SplitOnAssert(VCGenOptions options, List oldBl var newCmds = new List(); foreach (var command in currentBlock.Cmds) { verify = !ShouldSplitHere(command, splitOnEveryAssert) && verify; - if (verify && command is AssertCmd) + if (verify && BlockTransformations.IsNonTrivialAssert(command)) { assertionCount++; } From 6aa492cacb230faa17e0f5e597a3164ed306648d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 26 Jul 2024 16:58:00 +0200 Subject: [PATCH 18/42] Fix tests --- Test/test0/Split/Foo.split.0.bpl.expect | 12 ------------ Test/test0/Split/Foo.split.1.bpl.expect | 12 ------------ Test/test0/Split/Foo.split.2.bpl.expect | 12 ------------ Test/test0/Split/Foo.split.3.bpl.expect | 9 --------- Test/test0/Split/Split.bpl.expect | 2 -- Test/test0/SplitOnEveryAssert.bpl | 26 ++++++++++++------------- 6 files changed, 12 insertions(+), 61 deletions(-) diff --git a/Test/test0/Split/Foo.split.0.bpl.expect b/Test/test0/Split/Foo.split.0.bpl.expect index d00176cde..79578bccc 100644 --- a/Test/test0/Split/Foo.split.0.bpl.expect +++ b/Test/test0/Split/Foo.split.0.bpl.expect @@ -1,25 +1,13 @@ implementation Foo() returns (y: int) { - PreconditionGeneratedEntry: - goto anon0; - anon0: assert 5 + 0 == 5; assert 5 * 5 <= 25; - goto anon5_LoopHead; - - anon5_LoopHead: assume x#AT#0 + y#AT#0 == 5; assume x#AT#0 * x#AT#0 <= 25; - goto anon5_LoopDone; - - anon5_LoopDone: assume {:partition} 0 >= x#AT#0; assume y#AT#2 == y#AT#0; - goto GeneratedUnifiedExit; - - GeneratedUnifiedExit: assert y#AT#2 >= 0; return; } diff --git a/Test/test0/Split/Foo.split.1.bpl.expect b/Test/test0/Split/Foo.split.1.bpl.expect index bc4ea17f5..37297e028 100644 --- a/Test/test0/Split/Foo.split.1.bpl.expect +++ b/Test/test0/Split/Foo.split.1.bpl.expect @@ -1,27 +1,15 @@ implementation Foo() returns (y: int) { - PreconditionGeneratedEntry: - goto anon0; - anon0: assume 5 + 0 == 5; assume 5 * 5 <= 25; - goto anon5_LoopHead; - - anon5_LoopHead: assume x#AT#0 + y#AT#0 == 5; assume x#AT#0 * x#AT#0 <= 25; - goto anon5_LoopBody; - - anon5_LoopBody: assume {:partition} x#AT#0 > 0; assume x#AT#1 == x#AT#0 - 1; assert {:split_here} (x#AT#1 + y#AT#0) * (x#AT#1 + y#AT#0) > 25; assume y#AT#1 == y#AT#0 + 1; - goto anon6_Then; - - anon6_Then: assume {:partition} x#AT#1 < 3; assert 2 < 2; assume y#AT#1 * y#AT#1 > 4; diff --git a/Test/test0/Split/Foo.split.2.bpl.expect b/Test/test0/Split/Foo.split.2.bpl.expect index b07405c80..7f28a2ebf 100644 --- a/Test/test0/Split/Foo.split.2.bpl.expect +++ b/Test/test0/Split/Foo.split.2.bpl.expect @@ -1,27 +1,15 @@ implementation Foo() returns (y: int) { - PreconditionGeneratedEntry: - goto anon0; - anon0: assume 5 + 0 == 5; assume 5 * 5 <= 25; - goto anon5_LoopHead; - - anon5_LoopHead: assume x#AT#0 + y#AT#0 == 5; assume x#AT#0 * x#AT#0 <= 25; - goto anon5_LoopBody; - - anon5_LoopBody: assume {:partition} x#AT#0 > 0; assume x#AT#1 == x#AT#0 - 1; assume (x#AT#1 + y#AT#0) * (x#AT#1 + y#AT#0) > 25; assume y#AT#1 == y#AT#0 + 1; - goto anon6_Else; - - anon6_Else: assume {:partition} 3 <= x#AT#1; assert {:split_here} y#AT#1 * y#AT#1 * y#AT#1 < 8; assert 2 < 2; diff --git a/Test/test0/Split/Foo.split.3.bpl.expect b/Test/test0/Split/Foo.split.3.bpl.expect index 9ef24510f..eb031aa38 100644 --- a/Test/test0/Split/Foo.split.3.bpl.expect +++ b/Test/test0/Split/Foo.split.3.bpl.expect @@ -1,20 +1,11 @@ implementation Foo() returns (y: int) { - PreconditionGeneratedEntry: - goto anon0; - anon0: assume 5 + 0 == 5; assume 5 * 5 <= 25; - goto anon5_LoopHead; - - anon5_LoopHead: assume x#AT#0 + y#AT#0 == 5; assume x#AT#0 * x#AT#0 <= 25; - goto anon5_LoopBody; - - anon5_LoopBody: assume {:partition} x#AT#0 > 0; assume x#AT#1 == x#AT#0 - 1; assume (x#AT#1 + y#AT#0) * (x#AT#1 + y#AT#0) > 25; diff --git a/Test/test0/Split/Split.bpl.expect b/Test/test0/Split/Split.bpl.expect index ad3ef1049..67f8e9921 100644 --- a/Test/test0/Split/Split.bpl.expect +++ b/Test/test0/Split/Split.bpl.expect @@ -1,7 +1,5 @@ Split.bpl(19,5): Error: this assertion could not be proved Execution trace: Split.bpl(12,5): anon0 - Split.bpl(14,3): anon5_LoopHead - Split.bpl(18,7): anon5_LoopBody Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/test0/SplitOnEveryAssert.bpl b/Test/test0/SplitOnEveryAssert.bpl index 85f764c6d..8e3c2ec75 100644 --- a/Test/test0/SplitOnEveryAssert.bpl +++ b/Test/test0/SplitOnEveryAssert.bpl @@ -3,20 +3,18 @@ // RUN: %OutputCheck --file-to-check "%t" "%s" // CHECK: Verifying DoTheSplitting ... -// CHECK: checking split 1/12, .* -// CHECK: checking split 2/12, .* -// CHECK: checking split 3/12, .* -// CHECK: checking split 4/12, .* -// CHECK: --> split #4 done, \[.* s\] Invalid -// CHECK: checking split 5/12, .* -// CHECK: checking split 6/12, .* -// CHECK: checking split 7/12, .* -// CHECK: checking split 8/12, .* -// CHECK: checking split 9/12, .* -// CHECK: checking split 10/12, .* -// CHECK: checking split 11/12, .* -// CHECK: checking split 12/12, .* -// CHECK-L: SplitOnEveryAssert.bpl(37,5): Error: this assertion could not be proved +// CHECK: checking split 1/11, .* +// CHECK: checking split 2/11, .* +// CHECK: checking split 3/11, .* +// CHECK: --> split #3 done, \[.* s\] Invalid +// CHECK: checking split 5/11, .* +// CHECK: checking split 6/11, .* +// CHECK: checking split 7/11, .* +// CHECK: checking split 8/11, .* +// CHECK: checking split 9/11, .* +// CHECK: checking split 10/11, .* +// CHECK: checking split 11/11, .* +// CHECK-L: SplitOnEveryAssert.bpl(35,5): Error: this assertion could not be proved // Verify the second procedure is NOT split. .* is necessary to match the blank line in-between. // CHECK-NEXT: .* From 5eed5f62056da549028aa3ffb5d7a71698e32606 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 8 Aug 2024 15:07:57 +0200 Subject: [PATCH 19/42] Configure a VC timeout for all tests --- Source/ExecutionEngine/VerificationTask.cs | 5 +++-- Source/VCGeneration/SplitAndVerifyWorker.cs | 2 +- Test/lit.site.cfg | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/ExecutionEngine/VerificationTask.cs b/Source/ExecutionEngine/VerificationTask.cs index 297d7619d..75f56f28e 100644 --- a/Source/ExecutionEngine/VerificationTask.cs +++ b/Source/ExecutionEngine/VerificationTask.cs @@ -115,7 +115,7 @@ public void Cancel() { private async IAsyncEnumerable StartRun([EnumeratorCancellation] CancellationToken cancellationToken) { var timeout = Split.Run.Implementation.GetTimeLimit(Split.Options); - + var checkerTask = engine.CheckerPool.FindCheckerFor(ProcessedProgram.Program, Split, CancellationToken.None); if (!checkerTask.IsCompleted) { yield return new Queued(); @@ -127,7 +127,8 @@ private async IAsyncEnumerable StartRun([EnumeratorCancella var collector = new VerificationResultCollector(Split.Options); await engine.LargeThreadTaskFactory.StartNew(() => Split.BeginCheck(Split.Run.OutputWriter, checker, collector, - modelViewInfo, timeout, Split.Run.Implementation.GetResourceLimit(Split.Options), cancellationToken), cancellationToken).Unwrap(); + modelViewInfo, timeout, Split.Run.Implementation.GetResourceLimit(Split.Options), cancellationToken), cancellationToken).Unwrap(). + WaitAsync(TimeSpan.FromSeconds(timeout), cancellationToken); await checker.ProverTask.WaitAsync(cancellationToken); var result = Split.ReadOutcome(0, checker, collector); diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 63b763de0..c5a0333a5 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -150,7 +150,7 @@ private async Task StartCheck(int iteration, Split split, Checker checker, Cance KeepGoing ? options.VcsKeepGoingTimeout : run.Implementation.GetTimeLimit(options); await split.BeginCheck(run.OutputWriter, checker, callback, mvInfo, timeout, - Implementation.GetResourceLimit(options), cancellationToken); + Implementation.GetResourceLimit(options), cancellationToken).WaitAsync(TimeSpan.FromSeconds(timeout), cancellationToken); } private Implementation Implementation => run.Implementation; diff --git a/Test/lit.site.cfg b/Test/lit.site.cfg index fdb6a8681..3c7351a3f 100644 --- a/Test/lit.site.cfg +++ b/Test/lit.site.cfg @@ -93,7 +93,7 @@ if runtime and lit.util.which(runtime) == None: boogieExecutable = runtime + ' ' + quotePath(boogieExecutable) # We do not want absolute or relative paths in error messages, just the basename of the file -boogieExecutable += ' -useBaseNameForFileName' +boogieExecutable += ' -useBaseNameForFileName -timeLimit:30' # Allow user to provide extra arguments to Boogie boogieParams = lit_config.params.get('boogie_params', '') From c79011bbe26af6ebf7055ef44d0b96901b4f13e3 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 8 Aug 2024 15:08:16 +0200 Subject: [PATCH 20/42] Fix concurrency issue in QuantifierInstantiationEngine --- .../VCExpr/QuantifierInstantiationEngine.cs | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/Source/VCExpr/QuantifierInstantiationEngine.cs b/Source/VCExpr/QuantifierInstantiationEngine.cs index fbb26a262..82991c1b4 100644 --- a/Source/VCExpr/QuantifierInstantiationEngine.cs +++ b/Source/VCExpr/QuantifierInstantiationEngine.cs @@ -51,7 +51,7 @@ public QuantifierInstantiationInfo(Dictionary> boundV private string skolemConstantNamePrefix; internal VCExpressionGenerator vcExprGen; private Boogie2VCExprTranslator exprTranslator; - internal static ConcurrentDictionary> labelToTypes = new(); // pool name may map to multiple types + internal static ConcurrentDictionary> labelToTypes = new(); // pool name may map to multiple types public static VCExpr Instantiate(Implementation impl, VCExpressionGenerator vcExprGen, Boogie2VCExprTranslator exprTranslator, VCExpr vcExpr) { @@ -162,30 +162,28 @@ public void AddLambdaInstances(Dictionary>> lambd public static HashSet FindInstantiationHints(Variable v) { var labels = new HashSet(); - var iter = v.Attributes; - while (iter != null) + for(var iter = v.Attributes; iter != null; iter = iter.Next) { - if (iter.Key == "pool") + if (iter.Key != "pool") + { + continue; + } + + var tok = iter.tok; + foreach(var x in iter.Params) { - var tok = iter.tok; - iter.Params.ForEach(x => + if (x is string poolName) { - if (x is string poolName) - { - labels.Add(poolName); - if (!labelToTypes.ContainsKey(poolName)) - { - labelToTypes[poolName] = new(); - } - labelToTypes[poolName].Add(v.TypedIdent.Type); - } - else - { - Console.WriteLine($"{tok.filename}({tok.line},{tok.col}): expected pool name"); - } - }); + labels.Add(poolName); + var types = labelToTypes.GetOrAdd(poolName, _ => new()); + + types.Add(v.TypedIdent.Type); + } + else + { + Console.WriteLine($"{tok.filename}({tok.line},{tok.col}): expected pool name"); + } } - iter = iter.Next; } return labels; } From 2870fb01de5ab6a4126cad24e10e52bbed955c3c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 8 Aug 2024 15:20:25 +0200 Subject: [PATCH 21/42] Add a process limit timeout --- Source/BoogieDriver/BoogieDriver.cs | 5 +- Source/ExecutionEngine/CommandLineOptions.cs | 8 ++ Source/ExecutionEngine/ExecutionEngine.cs | 77 +++++-------- .../ExecutionEngineTests/CancellationTests.cs | 109 ------------------ Source/VCGeneration/ConditionGeneration.cs | 2 +- Test/lit.site.cfg | 2 +- Test/prover/cvc5-offline.smt2 | 32 +++++ 7 files changed, 73 insertions(+), 162 deletions(-) delete mode 100644 Source/UnitTests/ExecutionEngineTests/CancellationTests.cs create mode 100644 Test/prover/cvc5-offline.smt2 diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 979806a68..0a1b5955b 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.IO; +using System.Threading; namespace Microsoft.Boogie { @@ -63,8 +64,10 @@ public static int Main(string[] args) Helpers.ExtraTraceInformation(options, "Becoming sentient"); - var success = executionEngine.ProcessFiles(Console.Out, fileList).Result; + var source = new CancellationTokenSource(); + source.CancelAfter(TimeSpan.FromSeconds(options.ProcessTimeLimit)); + var success = executionEngine.ProcessFiles(Console.Out, fileList, cancellationToken: source.Token).Result; if (options.XmlSink != null) { options.XmlSink.Close(); diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 426c46e0b..ec04e7144 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -479,6 +479,10 @@ public int BracketIdsInVC } } + /// + /// A hidden option that configures a time limit for the whole Boogie CLI invocation + /// + public uint ProcessTimeLimit { get; set; } = 0; public uint TimeLimit { get; set; } = 0; // 0 means no limit public uint ResourceLimit { get; set; } = 0; // default to 0 public uint SmokeTimeout { get; set; } = 10; // default to 10s @@ -1273,6 +1277,10 @@ protected override bool ParseOption(string name, CommandLineParseState ps) case "timeLimit": ps.GetUnsignedNumericArgument(x => TimeLimit = x, null); return true; + + case "processTimeLimit": + ps.GetUnsignedNumericArgument(x => ProcessTimeLimit = x, null); + return true; case "rlimit": ps.GetUnsignedNumericArgument(x => ResourceLimit = x); diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index cc37f985a..a1e026246 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -83,20 +83,17 @@ static readonly ConcurrentDictionary static readonly ConcurrentDictionary ImplIdToCancellationTokenSource = new ConcurrentDictionary(); - static readonly ConcurrentDictionary RequestIdToCancellationTokenSource = - new ConcurrentDictionary(); - private readonly TaskScheduler largeThreadScheduler; private readonly bool disposeScheduler; public async Task ProcessFiles(TextWriter output, IList fileNames, bool lookForSnapshots = true, - string programId = null) { + string programId = null, CancellationToken cancellationToken = default) { Contract.Requires(cce.NonNullElements(fileNames)); if (Options.VerifySeparately && 1 < fileNames.Count) { var success = true; foreach (var fileName in fileNames) { - success &= await ProcessFiles(output, new List { fileName }, lookForSnapshots, fileName); + success &= await ProcessFiles(output, new List { fileName }, lookForSnapshots, fileName, cancellationToken: cancellationToken); } return success; } @@ -108,7 +105,7 @@ public async Task ProcessFiles(TextWriter output, IList fileNames, // BUG: Reusing checkers during snapshots doesn't work, even though it should. We create a new engine (and thus checker pool) to workaround this. using var engine = new ExecutionEngine(Options, Cache, CustomStackSizePoolTaskScheduler.Create(StackSize, Options.VcsCores), true); - success &= await engine.ProcessFiles(output, new List(snapshots), false, programId); + success &= await engine.ProcessFiles(output, new List(snapshots), false, programId, cancellationToken: cancellationToken); } return success; } @@ -120,7 +117,7 @@ public async Task ProcessFiles(TextWriter output, IList fileNames, return true; } - return await ProcessProgram(output, program, bplFileName, programId); + return await ProcessProgram(output, program, bplFileName, programId, cancellationToken: cancellationToken); } [Obsolete("Please inline this method call")] @@ -129,7 +126,7 @@ public bool ProcessProgram(Program program, string bplFileName, return ProcessProgram(Options.OutputWriter, program, bplFileName, programId).Result; } - public async Task ProcessProgram(TextWriter output, Program program, string bplFileName, string programId = null) + public async Task ProcessProgram(TextWriter output, Program program, string bplFileName, string programId = null, CancellationToken cancellationToken = default) { if (programId == null) { @@ -147,7 +144,7 @@ public async Task ProcessProgram(TextWriter output, Program program, strin if (Options.PrintCFGPrefix != null) { foreach (var impl in program.Implementations) { - using StreamWriter sw = new StreamWriter(Options.PrintCFGPrefix + "." + impl.Name + ".dot"); + await using StreamWriter sw = new StreamWriter(Options.PrintCFGPrefix + "." + impl.Name + ".dot"); await sw.WriteAsync(program.ProcessLoops(Options, impl).ToDot()); } } @@ -168,7 +165,7 @@ public async Task ProcessProgram(TextWriter output, Program program, strin Inline(program); var stats = new PipelineStatistics(); - outcome = await InferAndVerify(output, program, stats, 1 < Options.VerifySnapshots ? programId : null); + outcome = await InferAndVerify(output, program, stats, 1 < Options.VerifySnapshots ? programId : null, cancellationToken: cancellationToken); switch (outcome) { case PipelineOutcome.Done: case PipelineOutcome.VerificationCompleted: @@ -530,7 +527,8 @@ public async Task InferAndVerify( Program program, PipelineStatistics stats, string programId = null, - ErrorReporterDelegate er = null, string requestId = null) + ErrorReporterDelegate er = null, string requestId = null, + CancellationToken cancellationToken = default) { Contract.Requires(program != null); Contract.Requires(stats != null); @@ -542,7 +540,7 @@ public async Task InferAndVerify( var start = DateTime.UtcNow; - var processedProgram = await PreProcessProgramVerification(program); + var processedProgram = await PreProcessProgramVerification(program, cancellationToken); foreach (var action in Options.UseResolvedProgram) { action(Options, processedProgram); @@ -555,7 +553,7 @@ public async Task InferAndVerify( if (Options.ContractInfer) { - return await RunHoudini(program, stats, er); + return await RunHoudini(program, stats, er, cancellationToken); } var stablePrioritizedImpls = GetPrioritizedImplementations(program); @@ -565,7 +563,8 @@ public async Task InferAndVerify( out stats.CachingActionCounts); } - var outcome = await VerifyEachImplementation(output, processedProgram, stats, programId, er, requestId, stablePrioritizedImpls); + var outcome = await VerifyEachImplementation(output, processedProgram, stats, + programId, er, stablePrioritizedImpls, cancellationToken); if (1 < Options.VerifySnapshots && programId != null) { @@ -578,7 +577,7 @@ public async Task InferAndVerify( return outcome; } - private Task PreProcessProgramVerification(Program program) + private Task PreProcessProgramVerification(Program program, CancellationToken cancellationToken) { return LargeThreadTaskFactory.StartNew(() => { @@ -613,7 +612,7 @@ private Task PreProcessProgramVerification(Program program) program.DeclarationDependencies = Pruner.ComputeDeclarationDependencies(Options, program); return processedProgram; - }); + }, cancellationToken); } private ProcessedProgram ExtractLoops(Program program) @@ -652,18 +651,16 @@ private Implementation[] GetPrioritizedImplementations(Program program) private async Task VerifyEachImplementation(TextWriter outputWriter, ProcessedProgram processedProgram, PipelineStatistics stats, - string programId, ErrorReporterDelegate er, string requestId, Implementation[] stablePrioritizedImpls) + string programId, ErrorReporterDelegate er, Implementation[] stablePrioritizedImpls, + CancellationToken cancellationToken) { var consoleCollector = new ConcurrentToSequentialWriteManager(outputWriter); - - var cts = new CancellationTokenSource(); - RequestIdToCancellationTokenSource.AddOrUpdate(requestId, cts, (k, ov) => cts); var tasks = stablePrioritizedImpls.Select(async (impl, index) => { await using var taskWriter = consoleCollector.AppendWriter(); var implementation = stablePrioritizedImpls[index]; var result = await EnqueueVerifyImplementation(processedProgram, stats, programId, er, - implementation, cts, taskWriter); + implementation, cancellationToken, taskWriter); var output = result.GetOutput(Options.Printer, this, stats, er); await taskWriter.WriteAsync(output); return result; @@ -681,9 +678,6 @@ private async Task VerifyEachImplementation(TextWriter outputWr Options.Printer.ErrorWriteLine(outputWriter, "Fatal Error: ProverException: {0}", e.Message); outcome = PipelineOutcome.FatalError; } - finally { - CleanupRequest(requestId); - } if (Options.Trace && Options.TrackVerificationCoverage && processedProgram.Program.AllCoveredElements.Any()) { Options.OutputWriter.WriteLine("Proof dependencies of whole program:\n {0}", @@ -711,7 +705,7 @@ public void Error(IToken tok, string msg) } } - public async Task> GetVerificationTasks(Program program) + public async Task> GetVerificationTasks(Program program, CancellationToken cancellationToken = default) { var sink = new CollectingErrorSink(); var resolutionErrors = program.Resolve(Options, sink); @@ -731,7 +725,7 @@ public async Task> GetVerificationTasks(Program CoalesceBlocks(program); Inline(program); - var processedProgram = await PreProcessProgramVerification(program); + var processedProgram = await PreProcessProgramVerification(program, cancellationToken); return GetPrioritizedImplementations(program).SelectMany(implementation => { var writer = TextWriter.Null; @@ -793,7 +787,7 @@ private async Task EnqueueVerifyImplementation( await verifyImplementationSemaphore.WaitAsync(cancellationToken); try { - return await VerifyImplementation(processedProgram, stats, errorReporterDelegate, + return await VerifyImplementationWithCaching(processedProgram, stats, errorReporterDelegate, cancellationToken, implementation, programId, taskWriter); } finally @@ -843,25 +837,7 @@ private void TraceCachingForBenchmarking(PipelineStatistics stats, } } - public static void CancelRequest(string requestId) - { - Contract.Requires(requestId != null); - - if (RequestIdToCancellationTokenSource.TryGetValue(requestId, out var cts)) - { - cts.Cancel(); - } - } - - private static void CleanupRequest(string requestId) - { - if (requestId != null) - { - RequestIdToCancellationTokenSource.TryRemove(requestId, out var old); - } - } - - private async Task VerifyImplementation( + private async Task VerifyImplementationWithCaching( ProcessedProgram processedProgram, PipelineStatistics stats, ErrorReporterDelegate er, @@ -878,7 +854,7 @@ private async Task VerifyImplementation( Options.Printer.Inform("", traceWriter); // newline Options.Printer.Inform($"Verifying {implementation.VerboseName} ...", traceWriter); - var result = await VerifyImplementationWithoutCaching(processedProgram, stats, er, cancellationToken, + var result = await VerifyImplementationWithLargeThread(processedProgram, stats, er, cancellationToken, programId, implementation, traceWriter); if (0 < Options.VerifySnapshots && !string.IsNullOrEmpty(implementation.Checksum)) { @@ -911,7 +887,7 @@ public ImplementationRunResult GetCachedVerificationResult(Implementation impl, return null; } - private Task VerifyImplementationWithoutCaching(ProcessedProgram processedProgram, + private Task VerifyImplementationWithLargeThread(ProcessedProgram processedProgram, PipelineStatistics stats, ErrorReporterDelegate er, CancellationToken cancellationToken, string programId, Implementation impl, TextWriter traceWriter) { @@ -927,7 +903,7 @@ private Task VerifyImplementationWithoutCaching(Process try { (verificationResult.VcOutcome, verificationResult.Errors, verificationResult.RunResults) = - await vcGen.VerifyImplementation2(new ImplementationRun(impl, traceWriter), cancellationToken); + await vcGen.VerifyImplementationDirectly(new ImplementationRun(impl, traceWriter), cancellationToken); processedProgram.PostProcessResult(vcGen, impl, verificationResult); } catch (VCGenException e) @@ -984,7 +960,8 @@ private Task VerifyImplementationWithoutCaching(Process #region Houdini - private async Task RunHoudini(Program program, PipelineStatistics stats, ErrorReporterDelegate er) + private async Task RunHoudini(Program program, PipelineStatistics stats, ErrorReporterDelegate er, + CancellationToken cancellationToken) { Contract.Requires(stats != null); diff --git a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs b/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs deleted file mode 100644 index dee86df4c..000000000 --- a/Source/UnitTests/ExecutionEngineTests/CancellationTests.cs +++ /dev/null @@ -1,109 +0,0 @@ -using System; -using System.IO; -using System.Threading.Tasks; -using Microsoft.Boogie; -using NUnit.Framework; - -namespace ExecutionEngineTests -{ - [TestFixture] - public class CancellationTests - { - public Program GetProgram(ExecutionEngine engine, string code) { - var bplFileName = "1"; - int errorCount = Parser.Parse(code, bplFileName, out Program program, - engine.Options.UseBaseNameForFileName); - Assert.AreEqual(0, errorCount); - - engine.ResolveAndTypecheck(program, bplFileName, out _); - engine.EliminateDeadVariables(program); - engine.CollectModSets(program); - engine.CoalesceBlocks(program); - engine.Inline(program); - return program; - } - - [Test] - public async Task InferAndVerifyCanBeCancelledWhileWaitingForProver() { - var options = CommandLineOptions.FromArguments(TextWriter.Null); - using var executionEngine = ExecutionEngine.CreateWithoutSharedCache(options); - var infiniteProgram = GetProgram(executionEngine, SuperSlow); - var terminatingProgram = GetProgram(executionEngine, Fast); - - // We limit the number of checkers to 1. - options.VcsCores = 1; - - var requestId = ExecutionEngine.FreshRequestId(); - var outcomeTask = - executionEngine.InferAndVerify(Console.Out, infiniteProgram, new PipelineStatistics(), requestId, null, requestId); - await Task.Delay(1000); - ExecutionEngine.CancelRequest(requestId); - var outcome = await outcomeTask; - Assert.AreEqual(PipelineOutcome.Cancelled, outcome); - var requestId2 = ExecutionEngine.FreshRequestId(); - var outcome2 = await executionEngine.InferAndVerify(Console.Out, terminatingProgram, new PipelineStatistics(), requestId2, null, requestId2); - Assert.AreEqual(PipelineOutcome.VerificationCompleted, outcome2); - } - - private const string Fast = @" -procedure easy() ensures 1 + 1 == 0; { -} -"; - - private const string SuperSlow = @" - type LayerType; - function {:identity} LitInt(x: int) : int; - axiom (forall x: int :: {:identity} { LitInt(x): int } LitInt(x): int == x); - const $LZ: LayerType; - function $LS(LayerType) : LayerType; - - function Ack($ly: LayerType, m#0: int, n#0: int) : int; - function Ack#canCall(m#0: int, n#0: int) : bool; - axiom (forall $ly: LayerType, m#0: int, n#0: int :: - { Ack($LS($ly), m#0, n#0) } - Ack($LS($ly), m#0, n#0) - == Ack($ly, m#0, n#0)); - axiom (forall $ly: LayerType, m#0: int, n#0: int :: - { Ack($LS($ly), m#0, n#0) } - Ack#canCall(m#0, n#0) - || (m#0 >= LitInt(0) && n#0 >= LitInt(0)) - ==> (m#0 != LitInt(0) - ==> (n#0 == LitInt(0) ==> Ack#canCall(m#0 - 1, LitInt(1))) - && (n#0 != LitInt(0) - ==> Ack#canCall(m#0, n#0 - 1) - && Ack#canCall(m#0 - 1, Ack($ly, m#0, n#0 - 1)))) - && Ack($LS($ly), m#0, n#0) - == (if m#0 == LitInt(0) - then n#0 + 1 - else (if n#0 == LitInt(0) - then Ack($ly, m#0 - 1, LitInt(1)) - else Ack($ly, m#0 - 1, Ack($ly, m#0, n#0 - 1))))); - axiom (forall $ly: LayerType, m#0: int, n#0: int :: - {:weight 3} { Ack($LS($ly), LitInt(m#0), LitInt(n#0)) } - Ack#canCall(LitInt(m#0), LitInt(n#0)) - || (LitInt(m#0) >= LitInt(0) - && LitInt(n#0) >= LitInt(0)) - ==> (LitInt(m#0) != LitInt(0) - ==> (LitInt(n#0) == LitInt(0) - ==> Ack#canCall(LitInt(m#0 - 1), LitInt(1))) - && (LitInt(n#0) != LitInt(0) - ==> Ack#canCall(LitInt(m#0), LitInt(n#0 - 1)) - && Ack#canCall(LitInt(m#0 - 1), - LitInt(Ack($LS($ly), LitInt(m#0), LitInt(n#0 - 1)))))) - && Ack($LS($ly), LitInt(m#0), LitInt(n#0)) - == (if LitInt(m#0) == LitInt(0) - then n#0 + 1 - else (if LitInt(n#0) == LitInt(0) - then Ack($LS($ly), LitInt(m#0 - 1), LitInt(1)) - else Ack($LS($ly), - LitInt(m#0 - 1), - LitInt(Ack($LS($ly), LitInt(m#0), LitInt(n#0 - 1))))))); - - procedure proof() - { - assume Ack#canCall(LitInt(5), LitInt(5)); - assert LitInt(Ack($LS($LS($LZ)), LitInt(5), LitInt(5))) == LitInt(0); - } -"; - } -} \ No newline at end of file diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index f0f481b8e..44eec8d9b 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -105,7 +105,7 @@ public ConditionGeneration(Program program, CheckerPool checkerPool) /// /// /// - public async Task<(VcOutcome, List errors, List vcResults)> VerifyImplementation2( + public async Task<(VcOutcome, List errors, List vcResults)> VerifyImplementationDirectly( ImplementationRun run, CancellationToken cancellationToken) { Contract.Requires(run != null); diff --git a/Test/lit.site.cfg b/Test/lit.site.cfg index 3c7351a3f..8ea897da8 100644 --- a/Test/lit.site.cfg +++ b/Test/lit.site.cfg @@ -93,7 +93,7 @@ if runtime and lit.util.which(runtime) == None: boogieExecutable = runtime + ' ' + quotePath(boogieExecutable) # We do not want absolute or relative paths in error messages, just the basename of the file -boogieExecutable += ' -useBaseNameForFileName -timeLimit:30' +boogieExecutable += ' -useBaseNameForFileName -timeLimit:30 -processTimeLimit:110' # Allow user to provide extra arguments to Boogie boogieParams = lit_config.params.get('boogie_params', '') diff --git a/Test/prover/cvc5-offline.smt2 b/Test/prover/cvc5-offline.smt2 new file mode 100644 index 000000000..61aaf30b3 --- /dev/null +++ b/Test/prover/cvc5-offline.smt2 @@ -0,0 +1,32 @@ +(set-option :timeout 30000) +(set-option :rlimit 0) +(set-option :smt.mbqi false) +(set-option :model.compact false) +(set-option :model.v2 true) +(set-option :pp.bv_literals false) +(set-option :print-success false) +(set-info :smt-lib-version 2.6) +(set-option :smt.mbqi false) +(set-option :model.compact false) +(set-option :model.v2 true) +(set-option :pp.bv_literals false) +; done setting options + + +(declare-fun tickleBool (Bool) Bool) +(assert (and (tickleBool true) (tickleBool false))) +(declare-fun ControlFlow (Int Int) Int) +(declare-fun x () Int) +(declare-fun y () Int) +(push 1) +(set-info :boogie-vc-id P) +(assert (not + (=> (= (ControlFlow 0 0) 3) (let ((anon0_correct (=> (= (ControlFlow 0 2) (- 0 1)) (= (* x y) (* y x))))) +(let ((PreconditionGeneratedEntry_correct (=> (= (ControlFlow 0 3) 2) anon0_correct))) +PreconditionGeneratedEntry_correct))) +)) +(check-sat) +(get-info :reason-unknown) +(get-info :rlimit) +(get-model) +(pop 1) From 735fee450f154912d5f04069a401c9dd603805a5 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 8 Aug 2024 15:53:48 +0200 Subject: [PATCH 22/42] Make trace output culture invariant --- Source/Provers/SMTLib/TypeDeclCollector.cs | 16 ++++++++-------- Source/VCGeneration/SplitAndVerifyWorker.cs | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Source/Provers/SMTLib/TypeDeclCollector.cs b/Source/Provers/SMTLib/TypeDeclCollector.cs index 6f034db03..dac076b12 100644 --- a/Source/Provers/SMTLib/TypeDeclCollector.cs +++ b/Source/Provers/SMTLib/TypeDeclCollector.cs @@ -15,7 +15,7 @@ public class TypeDeclCollector : BoundVarTraversingVCExprVisitor private UniqueNamer Namer; private HashSet /*!*/ - RegisteredRelations = new HashSet(); + RegisteredRelations = new(); [ContractInvariantMethod] void ObjectInvariant() @@ -43,8 +43,8 @@ protected override bool StandardResult(VCExpr node, bool arg) return true; } - private readonly List!*/> AllDecls = new List(); - private readonly List!*/> IncDecls = new List(); + private readonly List!*/> AllDecls = new(); + private readonly List!*/> IncDecls = new(); // In order to support push/pop interface of the theorem prover, the "known" declarations // must be kept in a stack @@ -75,12 +75,12 @@ private HashSet /*!*/ KnownSelectFunctions } // ------ - private readonly Stack /*!*/> _KnownFunctions = new Stack>(); - private readonly Stack /*!*/> _KnownVariables = new Stack>(); + private readonly Stack /*!*/> _KnownFunctions = new(); + private readonly Stack /*!*/> _KnownVariables = new(); - private readonly Stack /*!*/> _KnownTypes = new Stack>(); - private readonly Stack /*!*/> _KnownStoreFunctions = new Stack>(); - private readonly Stack /*!*/> _KnownSelectFunctions = new Stack>(); + private readonly Stack /*!*/> _KnownTypes = new(); + private readonly Stack /*!*/> _KnownStoreFunctions = new(); + private readonly Stack /*!*/> _KnownSelectFunctions = new(); private void InitializeKnownDecls() { diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index c5a0333a5..2fbe080a9 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -139,8 +140,9 @@ private async Task StartCheck(int iteration, Split split, Checker checker, Cance { var splitNum = split.SplitIndex + 1; var splitIdxStr = options.RandomizeVcIterations > 1 ? $"{splitNum} (iteration {iteration})" : $"{splitNum}"; - run.OutputWriter.WriteLine(" checking split {1}/{2}, {3:0.00}%, {0} ...", - split.Stats, splitIdxStr, total, 100 * provenCost / (provenCost + remainingCost)); + await run.OutputWriter.WriteLineAsync(string.Format(CultureInfo.InvariantCulture, + " checking split {1}/{2}, {3:0.00}%, {0} ...", + split.Stats, splitIdxStr, total, 100 * provenCost / (provenCost + remainingCost))); } callback.OnProgress?.Invoke("VCprove", split.SplitIndex, total, From 830ac55dca8157516b9f834efb88b29b317c5c3c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 8 Aug 2024 16:39:07 +0200 Subject: [PATCH 23/42] Fix Rlimitouts0.bpl --- Source/BoogieDriver/BoogieDriver.cs | 5 ++++- Source/ExecutionEngine/VerificationTask.cs | 10 +++++++--- Source/VCGeneration/SplitAndVerifyWorker.cs | 9 +++++++-- Test/test2/Rlimitouts0.bpl | 2 +- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 0a1b5955b..79b86bdd6 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -65,7 +65,10 @@ public static int Main(string[] args) Helpers.ExtraTraceInformation(options, "Becoming sentient"); var source = new CancellationTokenSource(); - source.CancelAfter(TimeSpan.FromSeconds(options.ProcessTimeLimit)); + if (options.ProcessTimeLimit != 0) + { + source.CancelAfter(TimeSpan.FromSeconds(options.ProcessTimeLimit)); + } var success = executionEngine.ProcessFiles(Console.Out, fileList, cancellationToken: source.Token).Result; if (options.XmlSink != null) diff --git a/Source/ExecutionEngine/VerificationTask.cs b/Source/ExecutionEngine/VerificationTask.cs index 75f56f28e..8ef2793a7 100644 --- a/Source/ExecutionEngine/VerificationTask.cs +++ b/Source/ExecutionEngine/VerificationTask.cs @@ -126,9 +126,13 @@ private async IAsyncEnumerable StartRun([EnumeratorCancella yield return new Running(); var collector = new VerificationResultCollector(Split.Options); - await engine.LargeThreadTaskFactory.StartNew(() => Split.BeginCheck(Split.Run.OutputWriter, checker, collector, - modelViewInfo, timeout, Split.Run.Implementation.GetResourceLimit(Split.Options), cancellationToken), cancellationToken).Unwrap(). - WaitAsync(TimeSpan.FromSeconds(timeout), cancellationToken); + var beginCheckTask = engine.LargeThreadTaskFactory.StartNew(() => Split.BeginCheck(Split.Run.OutputWriter, checker, collector, + modelViewInfo, timeout, Split.Run.Implementation.GetResourceLimit(Split.Options), cancellationToken), cancellationToken).Unwrap(); + if (timeout != 0) + { + beginCheckTask = beginCheckTask.WaitAsync(TimeSpan.FromSeconds(timeout), cancellationToken); + } + await beginCheckTask; await checker.ProverTask.WaitAsync(cancellationToken); var result = Split.ReadOutcome(0, checker, collector); diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 2fbe080a9..fc9ee4287 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -151,8 +151,13 @@ await run.OutputWriter.WriteLineAsync(string.Format(CultureInfo.InvariantCulture var timeout = KeepGoing && split.LastChance ? options.VcsFinalAssertTimeout : KeepGoing ? options.VcsKeepGoingTimeout : run.Implementation.GetTimeLimit(options); - await split.BeginCheck(run.OutputWriter, checker, callback, mvInfo, timeout, - Implementation.GetResourceLimit(options), cancellationToken).WaitAsync(TimeSpan.FromSeconds(timeout), cancellationToken); + var beginCheckTask = split.BeginCheck(run.OutputWriter, checker, callback, mvInfo, timeout, + Implementation.GetResourceLimit(options), cancellationToken); + if (timeout != 0) + { + beginCheckTask = beginCheckTask.WaitAsync(TimeSpan.FromSeconds(timeout), cancellationToken); + } + await beginCheckTask; } private Implementation Implementation => run.Implementation; diff --git a/Test/test2/Rlimitouts0.bpl b/Test/test2/Rlimitouts0.bpl index 7a5e011f2..bae6fc5b3 100644 --- a/Test/test2/Rlimitouts0.bpl +++ b/Test/test2/Rlimitouts0.bpl @@ -1,5 +1,5 @@ // We use boogie here because parallel-boogie doesn't work well with -proverLog -// RUN: %boogie -rlimit:800000 -proverLog:"%t.smt2" "%s" +// RUN: %boogie -processTimeLimit:0 -timeLimit:0 -rlimit:800000 -proverLog:"%t.smt2" "%s" // RUN: %OutputCheck --file-to-check "%t.smt2" "%s" // CHECK-L: (set-option :timeout 4000) // CHECK-L: (set-option :rlimit 800000) From 9d5ef3dcd37e24c87f8eafc6e41ce9cc1e52fcbb Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 8 Aug 2024 16:45:24 +0200 Subject: [PATCH 24/42] Fix tests --- Test/civl/paxos/is.sh | 2 +- Test/livevars/stack_overflow.bpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Test/civl/paxos/is.sh b/Test/civl/paxos/is.sh index bc50a3345..cfa524668 100755 --- a/Test/civl/paxos/is.sh +++ b/Test/civl/paxos/is.sh @@ -1,6 +1,6 @@ #!/bin/bash -# RUN: %parallel-boogie Paxos.bpl PaxosActions.bpl PaxosImpl.bpl PaxosSeq.bpl > "%t" +# RUN: %parallel-boogie Paxos.bpl PaxosActions.bpl PaxosImpl.bpl PaxosSeq.bpl /timeLimit:120 > "%t" # RUN: %diff "%s.expect" "%t" boogie $@ Paxos.bpl PaxosActions.bpl PaxosImpl.bpl PaxosSeq.bpl diff --git a/Test/livevars/stack_overflow.bpl b/Test/livevars/stack_overflow.bpl index ca509a99d..a9103e0a3 100644 --- a/Test/livevars/stack_overflow.bpl +++ b/Test/livevars/stack_overflow.bpl @@ -1,4 +1,4 @@ -// RUN: %parallel-boogie /errorTrace:0 "%s" > "%t" +// RUN: %parallel-boogie /errorTrace:0 /timeLimit:120 "%s" > "%t" // RUN: %diff "%s.expect" "%t" // Stress test that we really don't need ot run multiple times From 43ca15e7bf6a5b51ab263d810bdbde147e852ced Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 8 Aug 2024 23:00:25 +0200 Subject: [PATCH 25/42] Fix indentation --- Source/VCGeneration/SplitAndVerifyWorker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 20a0ba8e7..f06a06edd 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -142,9 +142,9 @@ private async Task StartCheck(int iteration, Split split, Checker checker, Cance var splitIdxStr = options.RandomizeVcIterations > 1 ? $"{splitNum} (iteration {iteration})" : $"{splitNum}"; await run.OutputWriter.WriteLineAsync(string.Format(CultureInfo.InvariantCulture, " checking split {1}/{2}{3}, {4:0.00}%, {0} ...", - split.Stats, splitIdxStr, total, - split is ManualSplit manualSplit ? $" (line {manualSplit.Token.line})" : "", - 100 * provenCost / (provenCost + remainingCost))); + split.Stats, splitIdxStr, total, + // split is ManualSplit manualSplit ? $" (line {manualSplit.Token.line})" : "", + // 100 * provenCost / (provenCost + remainingCost))); } callback.OnProgress?.Invoke("VCprove", split.SplitIndex, total, From f98fe32ea87614592eacd75e9ae419ed1422bee7 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 10:10:00 +0200 Subject: [PATCH 26/42] Fix comp error --- Source/VCGeneration/SplitAndVerifyWorker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index f06a06edd..37fd641d4 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -143,8 +143,8 @@ private async Task StartCheck(int iteration, Split split, Checker checker, Cance await run.OutputWriter.WriteLineAsync(string.Format(CultureInfo.InvariantCulture, " checking split {1}/{2}{3}, {4:0.00}%, {0} ...", split.Stats, splitIdxStr, total, - // split is ManualSplit manualSplit ? $" (line {manualSplit.Token.line})" : "", - // 100 * provenCost / (provenCost + remainingCost))); + split is ManualSplit manualSplit ? $" (line {manualSplit.Token.line})" : "", + 100 * provenCost / (provenCost + remainingCost))); } callback.OnProgress?.Invoke("VCprove", split.SplitIndex, total, From b57c1ed7ff2001746181a2134aaf8d0d8f422e1d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 11:03:18 +0200 Subject: [PATCH 27/42] Increase timeout of KeyboardClass test --- Test/havoc0/KeyboardClassFindMorePorts.bpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/havoc0/KeyboardClassFindMorePorts.bpl b/Test/havoc0/KeyboardClassFindMorePorts.bpl index a5ed4e785..31577360f 100644 --- a/Test/havoc0/KeyboardClassFindMorePorts.bpl +++ b/Test/havoc0/KeyboardClassFindMorePorts.bpl @@ -1,4 +1,4 @@ -// RUN: %parallel-boogie "%s" > "%t" +// RUN: %parallel-boogie "%s" /timeLimit:90 > "%t" // RUN: %diff success.expect "%t" type byte, name; function OneByteToInt(byte) returns (int); From 2b662ef697797004703c0c2480c11293b1cff663 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 12:20:49 +0200 Subject: [PATCH 28/42] Add more cancellation --- Source/BoogieDriver/BoogieDriver.cs | 11 +++++------ Source/ExecutionEngine/ExecutionEngine.cs | 15 ++++++++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 79b86bdd6..3e69ea093 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -21,6 +21,11 @@ public static int Main(string[] args) { return 1; } + var source = new CancellationTokenSource(); + if (options.ProcessTimeLimit != 0) + { + source.CancelAfter(TimeSpan.FromSeconds(options.ProcessTimeLimit)); + } using var executionEngine = ExecutionEngine.CreateWithoutSharedCache(options); if (options.ProcessInfoFlags()) @@ -64,12 +69,6 @@ public static int Main(string[] args) Helpers.ExtraTraceInformation(options, "Becoming sentient"); - var source = new CancellationTokenSource(); - if (options.ProcessTimeLimit != 0) - { - source.CancelAfter(TimeSpan.FromSeconds(options.ProcessTimeLimit)); - } - var success = executionEngine.ProcessFiles(Console.Out, fileList, cancellationToken: source.Token).Result; if (options.XmlSink != null) { diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index a1e026246..4f2529c1f 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -93,6 +93,7 @@ public async Task ProcessFiles(TextWriter output, IList fileNames, if (Options.VerifySeparately && 1 < fileNames.Count) { var success = true; foreach (var fileName in fileNames) { + cancellationToken.ThrowIfCancellationRequested(); success &= await ProcessFiles(output, new List { fileName }, lookForSnapshots, fileName, cancellationToken: cancellationToken); } return success; @@ -102,6 +103,7 @@ public async Task ProcessFiles(TextWriter output, IList fileNames, var snapshotsByVersion = LookForSnapshots(fileNames); var success = true; foreach (var snapshots in snapshotsByVersion) { + cancellationToken.ThrowIfCancellationRequested(); // BUG: Reusing checkers during snapshots doesn't work, even though it should. We create a new engine (and thus checker pool) to workaround this. using var engine = new ExecutionEngine(Options, Cache, CustomStackSizePoolTaskScheduler.Create(StackSize, Options.VcsCores), true); @@ -110,14 +112,21 @@ public async Task ProcessFiles(TextWriter output, IList fileNames, return success; } + return await ProcessProgramFiles(output, fileNames, programId, cancellationToken); + } + + private Task ProcessProgramFiles(TextWriter output, IList fileNames, string programId, + CancellationToken cancellationToken) + { using XmlFileScope xf = new XmlFileScope(Options.XmlSink, fileNames[^1]); Program program = ParseBoogieProgram(fileNames, false); var bplFileName = fileNames[^1]; - if (program == null) { - return true; + if (program == null) + { + return Task.FromResult(true); } - return await ProcessProgram(output, program, bplFileName, programId, cancellationToken: cancellationToken); + return ProcessProgram(output, program, bplFileName, programId, cancellationToken: cancellationToken); } [Obsolete("Please inline this method call")] From 1e9f7ce4083dd957408ce5a18f327e8b09bf1201 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 12:43:03 +0200 Subject: [PATCH 29/42] Interrupt threads when disposing custom stack scheduler --- Source/BoogieDriver/BoogieDriver.cs | 10 +++++++++- .../CustomStackSizePoolTaskScheduler.cs | 2 +- Source/ExecutionEngine/ExecutionEngine.cs | 9 +++------ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 3e69ea093..880620409 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.Contracts; using System.IO; using System.Threading; +using System.Threading.Tasks; namespace Microsoft.Boogie { @@ -24,7 +26,8 @@ public static int Main(string[] args) var source = new CancellationTokenSource(); if (options.ProcessTimeLimit != 0) { - source.CancelAfter(TimeSpan.FromSeconds(options.ProcessTimeLimit)); + var span = TimeSpan.FromSeconds(options.ProcessTimeLimit); + source.CancelAfter(span); } using var executionEngine = ExecutionEngine.CreateWithoutSharedCache(options); @@ -81,6 +84,11 @@ public static int Main(string[] args) Console.ReadLine(); } + if (options.ProcessTimeLimit != 0) + { + Console.WriteLine("Finished execution process, cleaning up"); + } + return success ? 0 : 1; } diff --git a/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs b/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs index 78cb49271..68e101a21 100644 --- a/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs +++ b/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs @@ -96,7 +96,7 @@ public void Dispose() queue.Clear(); foreach (var thread in threads) { - thread.Join(); + thread.Interrupt(); } } } \ No newline at end of file diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 4f2529c1f..d6b2b3a24 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -74,14 +74,11 @@ public static ExecutionEngine CreateWithoutSharedCache(ExecutionEngineOptions op static DateTime FirstRequestStart; - static readonly ConcurrentDictionary - TimePerRequest = new ConcurrentDictionary(); + static readonly ConcurrentDictionary TimePerRequest = new(); - static readonly ConcurrentDictionary StatisticsPerRequest = - new ConcurrentDictionary(); + static readonly ConcurrentDictionary StatisticsPerRequest = new(); - static readonly ConcurrentDictionary ImplIdToCancellationTokenSource = - new ConcurrentDictionary(); + static readonly ConcurrentDictionary ImplIdToCancellationTokenSource = new(); private readonly TaskScheduler largeThreadScheduler; private readonly bool disposeScheduler; From 972c9bed4cba010e45cfe04c0e301a5c94b5bc66 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 15:51:50 +0200 Subject: [PATCH 30/42] Add blame-hang-timeout to unit test --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bd4da57ee..a6dd22691 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -60,7 +60,7 @@ jobs: # Create packages dotnet pack --no-build -c ${{ matrix.configuration }} ${SOLUTION} # Run unit tests - dotnet test --no-build -c ${{ matrix.configuration }} ${SOLUTION} + dotnet test --blame-hang-timeout 120s --no-build -c ${{ matrix.configuration }} ${SOLUTION} # Run lit test suite lit --param ${{ matrix.lit_param }} -v --timeout=120 -D configuration=${{ matrix.configuration }} Test - name: Deploy to nuget From 3d84ae9b0a2456d0fef56940c2ea580d745a6007 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 16:36:45 +0200 Subject: [PATCH 31/42] Fix --- Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs b/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs index 68e101a21..ccae135c1 100644 --- a/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs +++ b/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs @@ -82,7 +82,8 @@ private void RunItem() var task = queue.Dequeue().Result; TryExecuteTask(task); } catch(Exception e) { - if (e.GetBaseException() is OperationCanceledException) { + if (e is ThreadInterruptedException) { } + else if (e.GetBaseException() is OperationCanceledException) { // Async queue cancels tasks when it is disposed, which happens when this scheduler is disposed } else { throw; From 3e9470bfda78888afc9501281c27e4a8f8f958cb Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 16:41:37 +0200 Subject: [PATCH 32/42] Refactoring --- Source/Core/AsyncQueue.cs | 2 +- .../CustomStackSizePoolTaskScheduler.cs | 20 +++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Source/Core/AsyncQueue.cs b/Source/Core/AsyncQueue.cs index f20234e80..b051003c8 100644 --- a/Source/Core/AsyncQueue.cs +++ b/Source/Core/AsyncQueue.cs @@ -47,7 +47,7 @@ public IEnumerable Items } } - public void Clear() { + public void CancelWaitsAndClear() { while (customers.TryDequeue(out var customer)) { customer.TrySetCanceled(); } diff --git a/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs b/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs index ccae135c1..1c7772699 100644 --- a/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs +++ b/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs @@ -78,14 +78,22 @@ private void WorkLoop() private void RunItem() { - try { + try + { var task = queue.Dequeue().Result; TryExecuteTask(task); - } catch(Exception e) { - if (e is ThreadInterruptedException) { } - else if (e.GetBaseException() is OperationCanceledException) { + } + catch (ThreadInterruptedException e) + { + } + catch (Exception e) + { + if (e.GetBaseException() is OperationCanceledException) + { // Async queue cancels tasks when it is disposed, which happens when this scheduler is disposed - } else { + } + else + { throw; } } @@ -94,7 +102,7 @@ private void RunItem() public void Dispose() { disposeTokenSource.Cancel(); - queue.Clear(); + queue.CancelWaitsAndClear(); foreach (var thread in threads) { thread.Interrupt(); From 2e8ca55a114301b29eab786e97b5ac3a9e80668a Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 17:16:00 +0200 Subject: [PATCH 33/42] Fix warning --- Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs b/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs index 1c7772699..bec9e1361 100644 --- a/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs +++ b/Source/ExecutionEngine/CustomStackSizePoolTaskScheduler.cs @@ -83,7 +83,7 @@ private void RunItem() var task = queue.Dequeue().Result; TryExecuteTask(task); } - catch (ThreadInterruptedException e) + catch (ThreadInterruptedException) { } catch (Exception e) From 02b35616942eb78dae0c9f04eed0684bf63cef6c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 9 Aug 2024 17:28:24 +0200 Subject: [PATCH 34/42] Removed output that causes tests to fail --- Source/BoogieDriver/BoogieDriver.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Source/BoogieDriver/BoogieDriver.cs b/Source/BoogieDriver/BoogieDriver.cs index 880620409..0a1ae2fff 100644 --- a/Source/BoogieDriver/BoogieDriver.cs +++ b/Source/BoogieDriver/BoogieDriver.cs @@ -84,11 +84,6 @@ public static int Main(string[] args) Console.ReadLine(); } - if (options.ProcessTimeLimit != 0) - { - Console.WriteLine("Finished execution process, cleaning up"); - } - return success ? 0 : 1; } From dabf0ef05ae43173d359562e1735a1c9750c5ebd Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 10 Aug 2024 10:18:41 +0200 Subject: [PATCH 35/42] Delete accident --- Test/prover/cvc5-offline.smt2 | 32 -------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 Test/prover/cvc5-offline.smt2 diff --git a/Test/prover/cvc5-offline.smt2 b/Test/prover/cvc5-offline.smt2 deleted file mode 100644 index 61aaf30b3..000000000 --- a/Test/prover/cvc5-offline.smt2 +++ /dev/null @@ -1,32 +0,0 @@ -(set-option :timeout 30000) -(set-option :rlimit 0) -(set-option :smt.mbqi false) -(set-option :model.compact false) -(set-option :model.v2 true) -(set-option :pp.bv_literals false) -(set-option :print-success false) -(set-info :smt-lib-version 2.6) -(set-option :smt.mbqi false) -(set-option :model.compact false) -(set-option :model.v2 true) -(set-option :pp.bv_literals false) -; done setting options - - -(declare-fun tickleBool (Bool) Bool) -(assert (and (tickleBool true) (tickleBool false))) -(declare-fun ControlFlow (Int Int) Int) -(declare-fun x () Int) -(declare-fun y () Int) -(push 1) -(set-info :boogie-vc-id P) -(assert (not - (=> (= (ControlFlow 0 0) 3) (let ((anon0_correct (=> (= (ControlFlow 0 2) (- 0 1)) (= (* x y) (* y x))))) -(let ((PreconditionGeneratedEntry_correct (=> (= (ControlFlow 0 3) 2) anon0_correct))) -PreconditionGeneratedEntry_correct))) -)) -(check-sat) -(get-info :reason-unknown) -(get-info :rlimit) -(get-model) -(pop 1) From 00ef399402f00bcb2499b5f68543ac8ae13509f6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 10 Aug 2024 10:19:05 +0200 Subject: [PATCH 36/42] Add process time limit --- Test/test2/Rlimitouts0.bpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/test2/Rlimitouts0.bpl b/Test/test2/Rlimitouts0.bpl index bae6fc5b3..db3fc72bc 100644 --- a/Test/test2/Rlimitouts0.bpl +++ b/Test/test2/Rlimitouts0.bpl @@ -1,5 +1,5 @@ // We use boogie here because parallel-boogie doesn't work well with -proverLog -// RUN: %boogie -processTimeLimit:0 -timeLimit:0 -rlimit:800000 -proverLog:"%t.smt2" "%s" +// RUN: %boogie -timeLimit:0 -rlimit:800000 -proverLog:"%t.smt2" "%s" // RUN: %OutputCheck --file-to-check "%t.smt2" "%s" // CHECK-L: (set-option :timeout 4000) // CHECK-L: (set-option :rlimit 800000) From df7861bf066efed6c7256688ea52e101dae1f17b Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Sat, 10 Aug 2024 16:34:08 +0200 Subject: [PATCH 37/42] Increase timeout for treiber --- Test/civl/samples/treiber-stack.bpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/civl/samples/treiber-stack.bpl b/Test/civl/samples/treiber-stack.bpl index b9e426474..758526305 100644 --- a/Test/civl/samples/treiber-stack.bpl +++ b/Test/civl/samples/treiber-stack.bpl @@ -1,4 +1,4 @@ -// RUN: %parallel-boogie -lib:base -lib:node -vcsSplitOnEveryAssert "%s" > "%t" +// RUN: %parallel-boogie -lib:base -lib:node -timeLimit:90 -vcsSplitOnEveryAssert "%s" > "%t" // RUN: %diff "%s.expect" "%t" datatype LocPiece { Left(), Right() } From 3fe0fc53f04ca16370cf628f7c3e7f4a066cfeb7 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 12 Aug 2024 12:30:42 +0200 Subject: [PATCH 38/42] Infinite timeout for treiber-stack --- Test/civl/samples/treiber-stack.bpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/civl/samples/treiber-stack.bpl b/Test/civl/samples/treiber-stack.bpl index 758526305..21340742e 100644 --- a/Test/civl/samples/treiber-stack.bpl +++ b/Test/civl/samples/treiber-stack.bpl @@ -1,4 +1,4 @@ -// RUN: %parallel-boogie -lib:base -lib:node -timeLimit:90 -vcsSplitOnEveryAssert "%s" > "%t" +// RUN: %parallel-boogie -lib:base -lib:node -timeLimit:0 -vcsSplitOnEveryAssert "%s" > "%t" // RUN: %diff "%s.expect" "%t" datatype LocPiece { Left(), Right() } From 4c2cfeec6472ed30c423b32471f00976c0e62f90 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 13 Aug 2024 13:44:30 +0200 Subject: [PATCH 39/42] Reduce changes --- .../LiveVariableAnalysis/InterProcGenKill.cs | 1643 +++++++++-------- 1 file changed, 822 insertions(+), 821 deletions(-) diff --git a/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs b/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs index 0d92163d7..c65d18c5b 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs @@ -1,1058 +1,1059 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.Contracts; using Microsoft.Boogie.GraphUtil; -namespace Microsoft.Boogie; - -public class InterProcGenKill +namespace Microsoft.Boogie { - private CoreOptions options; - Program /*!*/ program; + public class InterProcGenKill + { + private CoreOptions options; + Program /*!*/ program; - Dictionary /*!*/ - procICFG; + Dictionary /*!*/ + procICFG; - Dictionary /*!*/ - name2Proc; + Dictionary /*!*/ + name2Proc; - Dictionary /*!*/> /*!*/ - callers; + Dictionary /*!*/> /*!*/ + callers; - Graph /*!*/ - callGraph; + Graph /*!*/ + callGraph; - Dictionary /*!*/ - procPriority; + Dictionary /*!*/ + procPriority; - int maxBlocksInProc; + int maxBlocksInProc; - WorkList /*!*/ - workList; + WorkList /*!*/ + workList; - Implementation /*!*/ - mainImpl; + Implementation /*!*/ + mainImpl; - static Dictionary /*!*/> /*!*/ - varsLiveAtExit = new Dictionary /*!*/>(); + static Dictionary /*!*/> /*!*/ + varsLiveAtExit = new Dictionary /*!*/>(); - static Dictionary /*!*/> /*!*/ - varsLiveAtEntry = new Dictionary /*!*/>(); + static Dictionary /*!*/> /*!*/ + varsLiveAtEntry = new Dictionary /*!*/>(); - static Dictionary /*!*/ - varsLiveSummary = new Dictionary(); + static Dictionary /*!*/ + varsLiveSummary = new Dictionary(); - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(workList != null); - Contract.Invariant(mainImpl != null); - Contract.Invariant(program != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(procICFG)); - Contract.Invariant(cce.NonNullDictionaryAndValues(name2Proc)); - Contract.Invariant(cce.NonNullDictionaryAndValues(callers) && - Contract.ForAll(callers.Values, v => cce.NonNullElements(v))); - Contract.Invariant(cce.NonNullElements(callGraph.Nodes)); - Contract.Invariant(procPriority != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveAtEntry)); - Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveAtExit) && - Contract.ForAll(varsLiveAtExit.Values, v => cce.NonNullElements(v))); - Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveSummary)); - Contract.Invariant(cce.NonNullDictionaryAndValues(weightCacheAfterCall)); - Contract.Invariant(cce.NonNullDictionaryAndValues(weightCacheBeforeCall)); - } + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(workList != null); + Contract.Invariant(mainImpl != null); + Contract.Invariant(program != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(procICFG)); + Contract.Invariant(cce.NonNullDictionaryAndValues(name2Proc)); + Contract.Invariant(cce.NonNullDictionaryAndValues(callers) && + Contract.ForAll(callers.Values, v => cce.NonNullElements(v))); + Contract.Invariant(cce.NonNullElements(callGraph.Nodes)); + Contract.Invariant(procPriority != null); + Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveAtEntry)); + Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveAtExit) && + Contract.ForAll(varsLiveAtExit.Values, v => cce.NonNullElements(v))); + Contract.Invariant(cce.NonNullDictionaryAndValues(varsLiveSummary)); + Contract.Invariant(cce.NonNullDictionaryAndValues(weightCacheAfterCall)); + Contract.Invariant(cce.NonNullDictionaryAndValues(weightCacheBeforeCall)); + } - [NotDelayed] - public InterProcGenKill(Implementation impl, Program program, CoreOptions options) - { - Contract.Requires(program != null); - Contract.Requires(impl != null); - this.program = program; - this.options = options; - procICFG = new Dictionary(); - name2Proc = new Dictionary(); - workList = new WorkList(); - this.callers = new Dictionary /*!*/>(); - this.callGraph = new Graph(); - this.procPriority = new Dictionary(); - this.maxBlocksInProc = 0; - this.mainImpl = impl; - - Dictionary /*!*/ - name2Impl = new Dictionary(); - varsLiveAtExit.Clear(); - varsLiveAtEntry.Clear(); - varsLiveSummary.Clear(); - - foreach (var decl in program.TopLevelDeclarations) + [NotDelayed] + public InterProcGenKill(Implementation impl, Program program, CoreOptions options) { - Contract.Assert(decl != null); - if (decl is Implementation) - { - Implementation /*!*/ - imp = (Implementation /*!*/) cce.NonNull(decl); - name2Impl[imp.Name] = imp; - } - else if (decl is Procedure) + Contract.Requires(program != null); + Contract.Requires(impl != null); + this.program = program; + this.options = options; + procICFG = new Dictionary(); + name2Proc = new Dictionary(); + workList = new WorkList(); + this.callers = new Dictionary /*!*/>(); + this.callGraph = new Graph(); + this.procPriority = new Dictionary(); + this.maxBlocksInProc = 0; + this.mainImpl = impl; + + Dictionary /*!*/ + name2Impl = new Dictionary(); + varsLiveAtExit.Clear(); + varsLiveAtEntry.Clear(); + varsLiveSummary.Clear(); + + foreach (var decl in program.TopLevelDeclarations) { - Procedure /*!*/ - proc = cce.NonNull(decl as Procedure); - name2Proc[proc.Name] = proc; + Contract.Assert(decl != null); + if (decl is Implementation) + { + Implementation /*!*/ + imp = (Implementation /*!*/) cce.NonNull(decl); + name2Impl[imp.Name] = imp; + } + else if (decl is Procedure) + { + Procedure /*!*/ + proc = cce.NonNull(decl as Procedure); + name2Proc[proc.Name] = proc; + } } - } - - ImplementationControlFlowGraph /*!*/ - mainImplementationControlFlowGraph = new ImplementationControlFlowGraph(this.options, mainImpl); - Contract.Assert(mainImplementationControlFlowGraph != null); - procICFG.Add(mainImplementationControlFlowGraph.impl.Name, mainImplementationControlFlowGraph); - callGraph.AddSource(mainImplementationControlFlowGraph.impl.Name); - - List /*!*/ - procsToConsider = new List(); - procsToConsider.Add(mainImplementationControlFlowGraph); - while (procsToConsider.Count != 0) - { ImplementationControlFlowGraph /*!*/ - p = procsToConsider[0]; - Contract.Assert(p != null); - procsToConsider.RemoveAt(0); + mainImplementationControlFlowGraph = new ImplementationControlFlowGraph(this.options, mainImpl); + Contract.Assert(mainImplementationControlFlowGraph != null); + procICFG.Add(mainImplementationControlFlowGraph.impl.Name, mainImplementationControlFlowGraph); + callGraph.AddSource(mainImplementationControlFlowGraph.impl.Name); + + List /*!*/ + procsToConsider = new List(); + procsToConsider.Add(mainImplementationControlFlowGraph); - foreach (string /*!*/ callee in p.procsCalled.Keys) + while (procsToConsider.Count != 0) { - Contract.Assert(callee != null); - if (!name2Impl.ContainsKey(callee)) + ImplementationControlFlowGraph /*!*/ + p = procsToConsider[0]; + Contract.Assert(p != null); + procsToConsider.RemoveAt(0); + + foreach (string /*!*/ callee in p.procsCalled.Keys) { - continue; - } + Contract.Assert(callee != null); + if (!name2Impl.ContainsKey(callee)) + { + continue; + } - callGraph.AddEdge(p.impl.Name, callee); + callGraph.AddEdge(p.impl.Name, callee); - if (maxBlocksInProc < p.nodes.Count) - { - maxBlocksInProc = p.nodes.Count; - } + if (maxBlocksInProc < p.nodes.Count) + { + maxBlocksInProc = p.nodes.Count; + } - if (!callers.ContainsKey(callee)) - { - callers.Add(callee, new List()); - } + if (!callers.ContainsKey(callee)) + { + callers.Add(callee, new List()); + } - foreach (Block /*!*/ b in p.procsCalled[callee]) - { - Contract.Assert(b != null); - callers[callee].Add(new WorkItem(p, b)); + foreach (Block /*!*/ b in p.procsCalled[callee]) + { + Contract.Assert(b != null); + callers[callee].Add(new WorkItem(p, b)); + } + + if (procICFG.ContainsKey(callee)) + { + continue; + } + + ImplementationControlFlowGraph /*!*/ + ncfg = new ImplementationControlFlowGraph(this.options, name2Impl[callee]); + Contract.Assert(ncfg != null); + procICFG.Add(callee, ncfg); + procsToConsider.Add(ncfg); } + } + + callGraph.TarjanTopSort(out var acyclic, out var sortedNodes); + + Contract.Assert(acyclic); - if (procICFG.ContainsKey(callee)) + int cnt = 0; + for (int i = sortedNodes.Count - 1; i >= 0; i--) + { + string s = sortedNodes[i]; + if (s == null) { continue; } - ImplementationControlFlowGraph /*!*/ - ncfg = new ImplementationControlFlowGraph(this.options, name2Impl[callee]); - Contract.Assert(ncfg != null); - procICFG.Add(callee, ncfg); - procsToConsider.Add(ncfg); + procPriority.Add(s, cnt); + cnt++; } } - callGraph.TarjanTopSort(out var acyclic, out var sortedNodes); - - Contract.Assert(acyclic); - - int cnt = 0; - for (int i = sortedNodes.Count - 1; i >= 0; i--) + public static HashSet /*!*/ GetVarsLiveAtExit(Implementation impl, Program prog) { - string s = sortedNodes[i]; - if (s == null) + Contract.Requires(prog != null); + Contract.Requires(impl != null); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + if (varsLiveAtExit.ContainsKey(impl.Name)) { - continue; + return varsLiveAtExit[impl.Name]; } - procPriority.Add(s, cnt); - cnt++; - } - } + // Return default: all globals and out params + HashSet /*!*/ + lv = new HashSet(); + foreach (Variable /*!*/ v in prog.GlobalVariables) + { + Contract.Assert(v != null); + lv.Add(v); + } - public static HashSet /*!*/ GetVarsLiveAtExit(Implementation impl, Program prog) - { - Contract.Requires(prog != null); - Contract.Requires(impl != null); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - if (varsLiveAtExit.ContainsKey(impl.Name)) - { - return varsLiveAtExit[impl.Name]; - } + foreach (Variable /*!*/ v in impl.OutParams) + { + Contract.Assert(v != null); + lv.Add(v); + } - // Return default: all globals and out params - HashSet /*!*/ - lv = new HashSet(); - foreach (Variable /*!*/ v in prog.GlobalVariables) - { - Contract.Assert(v != null); - lv.Add(v); + return lv; } - foreach (Variable /*!*/ v in impl.OutParams) + public static HashSet /*!*/ GetVarsLiveAtEntry(Implementation impl, Program prog) { - Contract.Assert(v != null); - lv.Add(v); - } + Contract.Requires(prog != null); + Contract.Requires(impl != null); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + if (varsLiveAtEntry.ContainsKey(impl.Name)) + { + return varsLiveAtEntry[impl.Name]; + } - return lv; - } + // Return default: all globals and in params + HashSet /*!*/ + lv = new HashSet(); + foreach (Variable /*!*/ v in prog.GlobalVariables) + { + Contract.Assert(v != null); + lv.Add(v); + } - public static HashSet /*!*/ GetVarsLiveAtEntry(Implementation impl, Program prog) - { - Contract.Requires(prog != null); - Contract.Requires(impl != null); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - if (varsLiveAtEntry.ContainsKey(impl.Name)) - { - return varsLiveAtEntry[impl.Name]; - } + foreach (Variable /*!*/ v in impl.InParams) + { + Contract.Assert(v != null); + lv.Add(v); + } - // Return default: all globals and in params - HashSet /*!*/ - lv = new HashSet(); - foreach (Variable /*!*/ v in prog.GlobalVariables) - { - Contract.Assert(v != null); - lv.Add(v); + return lv; } - foreach (Variable /*!*/ v in impl.InParams) + public static bool HasSummary(string name) { - Contract.Assert(v != null); - lv.Add(v); + Contract.Requires(name != null); + return varsLiveSummary.ContainsKey(name); } - return lv; - } + public static HashSet /*!*/ + PropagateLiveVarsAcrossCall(CoreOptions options, CallCmd cmd, HashSet /*!*/ lvAfter) + { + Contract.Requires(cmd != null); + Contract.Requires(cce.NonNullElements(lvAfter)); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + Procedure /*!*/ + proc = cce.NonNull(cmd.Proc); + if (varsLiveSummary.ContainsKey(proc.Name)) + { + GenKillWeight /*!*/ + w1 = getWeightBeforeCall(cmd); + Contract.Assert(w1 != null); + GenKillWeight /*!*/ + w2 = varsLiveSummary[proc.Name]; + Contract.Assert(w2 != null); + GenKillWeight /*!*/ + w3 = getWeightAfterCall(cmd); + Contract.Assert(w3 != null); + GenKillWeight /*!*/ + w = GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); + Contract.Assert(w != null); + return w.getLiveVars(lvAfter); + } - public static bool HasSummary(string name) - { - Contract.Requires(name != null); - return varsLiveSummary.ContainsKey(name); - } + HashSet /*!*/ + ret = new HashSet(); + ret.UnionWith(lvAfter); + new LiveVariableAnalysis(options).Propagate(cmd, ret); + return ret; + } - public static HashSet /*!*/ - PropagateLiveVarsAcrossCall(CoreOptions options, CallCmd cmd, HashSet /*!*/ lvAfter) - { - Contract.Requires(cmd != null); - Contract.Requires(cce.NonNullElements(lvAfter)); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - Procedure /*!*/ - proc = cce.NonNull(cmd.Proc); - if (varsLiveSummary.ContainsKey(proc.Name)) + class WorkItem { - GenKillWeight /*!*/ - w1 = getWeightBeforeCall(cmd); - Contract.Assert(w1 != null); - GenKillWeight /*!*/ - w2 = varsLiveSummary[proc.Name]; - Contract.Assert(w2 != null); - GenKillWeight /*!*/ - w3 = getWeightAfterCall(cmd); - Contract.Assert(w3 != null); - GenKillWeight /*!*/ - w = GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); - Contract.Assert(w != null); - return w.getLiveVars(lvAfter); - } + public ImplementationControlFlowGraph /*!*/ + cfg; - HashSet /*!*/ - ret = new HashSet(); - ret.UnionWith(lvAfter); - new LiveVariableAnalysis(options).Propagate(cmd, ret); - return ret; - } + public Block /*!*/ + block; - class WorkItem - { - public ImplementationControlFlowGraph /*!*/ - cfg; + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cfg != null); + Contract.Invariant(block != null); + } - public Block /*!*/ - block; - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cfg != null); - Contract.Invariant(block != null); - } + public WorkItem(ImplementationControlFlowGraph cfg, Block block) + { + Contract.Requires(block != null); + Contract.Requires(cfg != null); + this.cfg = cfg; + this.block = block; + } + public GenKillWeight getWeightAfter() + { + Contract.Ensures(Contract.Result() != null); + return cfg.weightAfter[block]; + } - public WorkItem(ImplementationControlFlowGraph cfg, Block block) - { - Contract.Requires(block != null); - Contract.Requires(cfg != null); - this.cfg = cfg; - this.block = block; - } + public bool setWeightBefore(GenKillWeight w) + { + Contract.Requires(w != null); + GenKillWeight /*!*/ + prev = cfg.weightBefore[block]; + Contract.Assert(prev != null); + GenKillWeight /*!*/ + curr = GenKillWeight.combine(w, prev); + Contract.Assert(curr != null); + if (GenKillWeight.isEqual(prev, curr)) + { + return false; + } - public GenKillWeight getWeightAfter() - { - Contract.Ensures(Contract.Result() != null); - return cfg.weightAfter[block]; - } + cfg.weightBefore[block] = curr; + return true; + } - public bool setWeightBefore(GenKillWeight w) - { - Contract.Requires(w != null); - GenKillWeight /*!*/ - prev = cfg.weightBefore[block]; - Contract.Assert(prev != null); - GenKillWeight /*!*/ - curr = GenKillWeight.combine(w, prev); - Contract.Assert(curr != null); - if (GenKillWeight.isEqual(prev, curr)) + [Pure] + [Reads(ReadsAttribute.Reads.Nothing)] + public override bool Equals(object other) { - return false; + WorkItem /*!*/ + wi = (WorkItem /*!*/) cce.NonNull(other); + return (wi.cfg == cfg && wi.block == block); } - cfg.weightBefore[block] = curr; - return true; - } + [Pure] + public override int GetHashCode() + { + return 0; + } - [Pure] - [Reads(ReadsAttribute.Reads.Nothing)] - public override bool Equals(object other) - { - WorkItem /*!*/ - wi = (WorkItem /*!*/) cce.NonNull(other); - return (wi.cfg == cfg && wi.block == block); + public string getLabel() + { + Contract.Ensures(Contract.Result() != null); + return cfg.impl.Name + "::" + block.Label; + } } - [Pure] - public override int GetHashCode() + private void AddToWorkList(WorkItem wi) { - return 0; + Contract.Requires(wi != null); + int i = procPriority[wi.cfg.impl.Name]; + int j = wi.cfg.getPriority(wi.block); + int priority = (i * maxBlocksInProc) + j; + + workList.Add(wi, priority); } - public string getLabel() + private void AddToWorkListReverse(WorkItem wi) { - Contract.Ensures(Contract.Result() != null); - return cfg.impl.Name + "::" + block.Label; + Contract.Requires(wi != null); + int i = procPriority[wi.cfg.impl.Name]; + int j = wi.cfg.getPriority(wi.block); + int priority = (procPriority.Count - i) * maxBlocksInProc + j; + workList.Add(wi, priority); } - } - private void AddToWorkList(WorkItem wi) - { - Contract.Requires(wi != null); - int i = procPriority[wi.cfg.impl.Name]; - int j = wi.cfg.getPriority(wi.block); - int priority = (i * maxBlocksInProc) + j; + class WorkList + { + SortedList /*!*/ + priorities; - workList.Add(wi, priority); - } + HashSet /*!*/ + labels; - private void AddToWorkListReverse(WorkItem wi) - { - Contract.Requires(wi != null); - int i = procPriority[wi.cfg.impl.Name]; - int j = wi.cfg.getPriority(wi.block); - int priority = (procPriority.Count - i) * maxBlocksInProc + j; - workList.Add(wi, priority); - } + Dictionary /*!*/> /*!*/ + workList; - class WorkList - { - SortedList /*!*/ - priorities; + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(priorities != null); + Contract.Invariant(cce.NonNullElements(labels)); + Contract.Invariant(cce.NonNullDictionaryAndValues(workList) && + Contract.ForAll(workList.Values, v => cce.NonNullElements(v))); + } - HashSet /*!*/ - labels; - Dictionary /*!*/> /*!*/ - workList; + public WorkList() + { + labels = new HashSet(); + priorities = new SortedList(); + workList = new Dictionary /*!*/>(); + } - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(priorities != null); - Contract.Invariant(cce.NonNullElements(labels)); - Contract.Invariant(cce.NonNullDictionaryAndValues(workList) && - Contract.ForAll(workList.Values, v => cce.NonNullElements(v))); - } + public void Add(WorkItem wi, int priority) + { + Contract.Requires(wi != null); + string /*!*/ + lab = wi.getLabel(); + Contract.Assert(lab != null); + if (labels.Contains(lab)) + { + // Already on worklist + return; + } + labels.Add(lab); + if (!workList.ContainsKey(priority)) + { + workList.Add(priority, new List()); + } - public WorkList() - { - labels = new HashSet(); - priorities = new SortedList(); - workList = new Dictionary /*!*/>(); - } + workList[priority].Add(wi); + if (!priorities.ContainsKey(priority)) + { + priorities.Add(priority, 0); + } - public void Add(WorkItem wi, int priority) - { - Contract.Requires(wi != null); - string /*!*/ - lab = wi.getLabel(); - Contract.Assert(lab != null); - if (labels.Contains(lab)) - { - // Already on worklist - return; + priorities[priority] = priorities[priority] + 1; } - labels.Add(lab); - if (!workList.ContainsKey(priority)) + public WorkItem Get() { - workList.Add(priority, new List()); + Contract.Ensures(Contract.Result() != null); + // Get minimum priority + int p = cce.NonNull(priorities.Keys)[0]; + priorities[p] = priorities[p] - 1; + if (priorities[p] == 0) + { + priorities.Remove(p); + } + + // Get a WI with this priority + WorkItem /*!*/ + wi = workList[p][0]; + Contract.Assert(wi != null); + workList[p].RemoveAt(0); + + // update labels + labels.Remove(wi.getLabel()); + return wi; } - workList[priority].Add(wi); - if (!priorities.ContainsKey(priority)) + public int Count { - priorities.Add(priority, 0); + get { return labels.Count; } } - - priorities[priority] = priorities[priority] + 1; } - public WorkItem Get() + private GenKillWeight getSummary(CallCmd cmd) { - Contract.Ensures(Contract.Result() != null); - // Get minimum priority - int p = cce.NonNull(priorities.Keys)[0]; - priorities[p] = priorities[p] - 1; - if (priorities[p] == 0) + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + Contract.Assert(cmd.Proc != null); + string /*!*/ + procName = cmd.Proc.Name; + Contract.Assert(procName != null); + if (procICFG.ContainsKey(procName)) { - priorities.Remove(p); + ImplementationControlFlowGraph /*!*/ + cfg = procICFG[procName]; + Contract.Assert(cfg != null); + return GenKillWeight.projectLocals(cfg.summary); } - // Get a WI with this priority - WorkItem /*!*/ - wi = workList[p][0]; - Contract.Assert(wi != null); - workList[p].RemoveAt(0); - - // update labels - labels.Remove(wi.getLabel()); - return wi; - } - - public int Count - { - get { return labels.Count; } + { + Contract.Assert(false); + throw new cce.UnreachableException(); + } } - } - private GenKillWeight getSummary(CallCmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - Contract.Assert(cmd.Proc != null); - string /*!*/ - procName = cmd.Proc.Name; - Contract.Assert(procName != null); - if (procICFG.ContainsKey(procName)) + public void ComputeLiveVars(Implementation impl, Program /*!*/ prog) { - ImplementationControlFlowGraph /*!*/ - cfg = procICFG[procName]; - Contract.Assert(cfg != null); - return GenKillWeight.projectLocals(cfg.summary); + Contract.Requires(prog != null); + Contract.Requires(impl != null); + InterProcGenKill /*!*/ + ipgk = new InterProcGenKill(impl, prog, options); + Contract.Assert(ipgk != null); + ipgk.Compute(); } + public void Compute() { - Contract.Assert(false); - throw new cce.UnreachableException(); - } - } - - public void ComputeLiveVars(Implementation impl, Program /*!*/ prog) - { - Contract.Requires(prog != null); - Contract.Requires(impl != null); - InterProcGenKill /*!*/ - ipgk = new InterProcGenKill(impl, prog, options); - Contract.Assert(ipgk != null); - ipgk.Compute(); - } + // Put all exit nodes in the worklist + foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) + { + Contract.Assert(cfg != null); + foreach (Block /*!*/ eb in cfg.exitNodes) + { + Contract.Assert(eb != null); + WorkItem /*!*/ + wi = new WorkItem(cfg, eb); + Contract.Assert(wi != null); + cfg.weightAfter[eb] = GenKillWeight.one(); + AddToWorkList(wi); + } + } - public void Compute() - { - // Put all exit nodes in the worklist - foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) - { - Contract.Assert(cfg != null); - foreach (Block /*!*/ eb in cfg.exitNodes) + while (workList.Count != 0) { - Contract.Assert(eb != null); WorkItem /*!*/ - wi = new WorkItem(cfg, eb); + wi = workList.Get(); Contract.Assert(wi != null); - cfg.weightAfter[eb] = GenKillWeight.one(); - AddToWorkList(wi); + process(wi); } - } - while (workList.Count != 0) - { - WorkItem /*!*/ - wi = workList.Get(); - Contract.Assert(wi != null); - process(wi); - } - - // Propagate LV to all procedures - foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) - { - Contract.Assert(cfg != null); - foreach (Block /*!*/ b in cfg.nodes) + // Propagate LV to all procedures + foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) { - Contract.Assert(b != null); - cfg.liveVarsAfter.Add(b, new HashSet()); - cfg.liveVarsBefore.Add(b, new HashSet()); + Contract.Assert(cfg != null); + foreach (Block /*!*/ b in cfg.nodes) + { + Contract.Assert(b != null); + cfg.liveVarsAfter.Add(b, new HashSet()); + cfg.liveVarsBefore.Add(b, new HashSet()); + } } - } - - ImplementationControlFlowGraph /*!*/ - mainCfg = procICFG[mainImpl.Name]; - Contract.Assert(mainCfg != null); - foreach (Block /*!*/ eb in mainCfg.exitNodes) - { - Contract.Assert(eb != null); - WorkItem /*!*/ - wi = new WorkItem(mainCfg, eb); - Contract.Assert(wi != null); - AddToWorkListReverse(wi); - } - - while (workList.Count != 0) - { - WorkItem /*!*/ - wi = workList.Get(); - Contract.Assert(wi != null); - ProcessLv(wi); - } - // Set live variable info - foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) - { - Contract.Assert(cfg != null); - HashSet /*!*/ - lv = new HashSet(); - foreach (Block /*!*/ eb in cfg.exitNodes) + ImplementationControlFlowGraph /*!*/ + mainCfg = procICFG[mainImpl.Name]; + Contract.Assert(mainCfg != null); + foreach (Block /*!*/ eb in mainCfg.exitNodes) { Contract.Assert(eb != null); - lv.UnionWith(cfg.liveVarsAfter[eb]); + WorkItem /*!*/ + wi = new WorkItem(mainCfg, eb); + Contract.Assert(wi != null); + AddToWorkListReverse(wi); } - varsLiveAtExit.Add(cfg.impl.Name, lv); - lv = new HashSet(); - foreach (Block /*!*/ eb in cfg.srcNodes) + while (workList.Count != 0) { - Contract.Assert(eb != null); - lv.UnionWith(cfg.liveVarsBefore[eb]); + WorkItem /*!*/ + wi = workList.Get(); + Contract.Assert(wi != null); + ProcessLv(wi); } - varsLiveAtEntry.Add(cfg.impl.Name, lv); - varsLiveSummary.Add(cfg.impl.Name, cfg.summary); - } + // Set live variable info + foreach (ImplementationControlFlowGraph /*!*/ cfg in procICFG.Values) + { + Contract.Assert(cfg != null); + HashSet /*!*/ + lv = new HashSet(); + foreach (Block /*!*/ eb in cfg.exitNodes) + { + Contract.Assert(eb != null); + lv.UnionWith(cfg.liveVarsAfter[eb]); + } - /* - foreach(Block/*!*/ - /* b in mainImpl.Blocks){ -Contract.Assert(b != null); -//Set lv = cfg.weightBefore[b].getLiveVars(); -b.liveVarsBefore = procICFG[mainImpl.Name].liveVarsAfter[b]; -//foreach(GlobalVariable/*!*/ - /* v in program.GlobalVariables){Contract.Assert(v != null); -// b.liveVarsBefore.Add(v); -//} -} -*/ + varsLiveAtExit.Add(cfg.impl.Name, lv); + lv = new HashSet(); + foreach (Block /*!*/ eb in cfg.srcNodes) + { + Contract.Assert(eb != null); + lv.UnionWith(cfg.liveVarsBefore[eb]); + } + + varsLiveAtEntry.Add(cfg.impl.Name, lv); + varsLiveSummary.Add(cfg.impl.Name, cfg.summary); + } + + /* + foreach(Block/*!*/ + /* b in mainImpl.Blocks){ + Contract.Assert(b != null); + //Set lv = cfg.weightBefore[b].getLiveVars(); + b.liveVarsBefore = procICFG[mainImpl.Name].liveVarsAfter[b]; + //foreach(GlobalVariable/*!*/ + /* v in program.GlobalVariables){Contract.Assert(v != null); + // b.liveVarsBefore.Add(v); + //} } + */ + } - // Called when summaries have already been computed - private void ProcessLv(WorkItem wi) - { - Contract.Requires(wi != null); - ImplementationControlFlowGraph /*!*/ - cfg = wi.cfg; - Contract.Assert(cfg != null); - Block /*!*/ - block = wi.block; - Contract.Assert(block != null); - HashSet /*!*/ - lv = cfg.liveVarsAfter[block]; - Contract.Assert(cce.NonNullElements(lv)); - // Propagate backwards in the block - HashSet /*!*/ - prop = new HashSet(); - prop.UnionWith(lv); - for (int i = block.Cmds.Count - 1; i >= 0; i--) + // Called when summaries have already been computed + private void ProcessLv(WorkItem wi) { - Cmd /*!*/ - cmd = block.Cmds[i]; - Contract.Assert(cmd != null); - if (cmd is CallCmd) + Contract.Requires(wi != null); + ImplementationControlFlowGraph /*!*/ + cfg = wi.cfg; + Contract.Assert(cfg != null); + Block /*!*/ + block = wi.block; + Contract.Assert(block != null); + HashSet /*!*/ + lv = cfg.liveVarsAfter[block]; + Contract.Assert(cce.NonNullElements(lv)); + // Propagate backwards in the block + HashSet /*!*/ + prop = new HashSet(); + prop.UnionWith(lv); + for (int i = block.Cmds.Count - 1; i >= 0; i--) { - string /*!*/ - procName = cce.NonNull(cce.NonNull((CallCmd) cmd).Proc).Name; - Contract.Assert(procName != null); - if (procICFG.ContainsKey(procName)) + Cmd /*!*/ + cmd = block.Cmds[i]; + Contract.Assert(cmd != null); + if (cmd is CallCmd) { - ImplementationControlFlowGraph /*!*/ - callee = procICFG[procName]; - Contract.Assert(callee != null); - // Inter propagation - // Remove local variables; add return variables - HashSet /*!*/ - elv = new HashSet(); - foreach (Variable /*!*/ v in prop) + string /*!*/ + procName = cce.NonNull(cce.NonNull((CallCmd) cmd).Proc).Name; + Contract.Assert(procName != null); + if (procICFG.ContainsKey(procName)) { - Contract.Assert(v != null); - if (v is GlobalVariable) + ImplementationControlFlowGraph /*!*/ + callee = procICFG[procName]; + Contract.Assert(callee != null); + // Inter propagation + // Remove local variables; add return variables + HashSet /*!*/ + elv = new HashSet(); + foreach (Variable /*!*/ v in prop) + { + Contract.Assert(v != null); + if (v is GlobalVariable) + { + elv.Add(v); + } + } + + foreach (Variable /*!*/ v in callee.impl.OutParams) { + Contract.Assert(v != null); elv.Add(v); } - } - foreach (Variable /*!*/ v in callee.impl.OutParams) - { - Contract.Assert(v != null); - elv.Add(v); - } + foreach (Block /*!*/ eb in callee.exitNodes) + { + Contract.Assert(eb != null); + callee.liveVarsAfter[eb].UnionWith(elv); + // TODO: check if modified before inserting + AddToWorkListReverse(new WorkItem(callee, eb)); + } - foreach (Block /*!*/ eb in callee.exitNodes) + // Continue with intra propagation + GenKillWeight /*!*/ + summary = GetWeightCall(cce.NonNull((CallCmd /*!*/) cmd)); + prop = summary.getLiveVars(prop); + } + else { - Contract.Assert(eb != null); - callee.liveVarsAfter[eb].UnionWith(elv); - // TODO: check if modified before inserting - AddToWorkListReverse(new WorkItem(callee, eb)); + new LiveVariableAnalysis(options).Propagate(cmd, prop); } - - // Continue with intra propagation - GenKillWeight /*!*/ - summary = GetWeightCall(cce.NonNull((CallCmd /*!*/) cmd)); - prop = summary.getLiveVars(prop); } else { new LiveVariableAnalysis(options).Propagate(cmd, prop); } } - else - { - new LiveVariableAnalysis(options).Propagate(cmd, prop); - } - } - cfg.liveVarsBefore[block].UnionWith(prop); + cfg.liveVarsBefore[block].UnionWith(prop); - foreach (Block /*!*/ b in cfg.predEdges[block]) - { - Contract.Assert(b != null); - HashSet /*!*/ - prev = cfg.liveVarsAfter[b]; - Contract.Assert(cce.NonNullElements(prev)); - HashSet /*!*/ - curr = new HashSet(prev); - curr.UnionWith(cfg.liveVarsBefore[block]); - Contract.Assert(cce.NonNullElements(curr)); - if (curr.Count != prev.Count) + foreach (Block /*!*/ b in cfg.predEdges[block]) { - cfg.liveVarsAfter[b] = curr; - AddToWorkListReverse(new WorkItem(cfg, b)); + Contract.Assert(b != null); + HashSet /*!*/ + prev = cfg.liveVarsAfter[b]; + Contract.Assert(cce.NonNullElements(prev)); + HashSet /*!*/ + curr = new HashSet(prev); + curr.UnionWith(cfg.liveVarsBefore[block]); + Contract.Assert(cce.NonNullElements(curr)); + if (curr.Count != prev.Count) + { + cfg.liveVarsAfter[b] = curr; + AddToWorkListReverse(new WorkItem(cfg, b)); + } } } - } - - private void process(WorkItem wi) - { - Contract.Requires(wi != null); - GenKillWeight /*!*/ - w = wi.getWeightAfter(); - Contract.Assert(w != null); - for (int i = wi.block.Cmds.Count - 1; i >= 0; i--) + private void process(WorkItem wi) { - Cmd /*!*/ - c = wi.block.Cmds[i]; - Contract.Assert(c != null); - if (c is CallCmd && procICFG.ContainsKey(cce.NonNull(cce.NonNull((CallCmd) c).Proc).Name)) - { - w = GenKillWeight.extend(GetWeightCall(cce.NonNull((CallCmd) c)), w); - } - else + Contract.Requires(wi != null); + GenKillWeight /*!*/ + w = wi.getWeightAfter(); + Contract.Assert(w != null); + + for (int i = wi.block.Cmds.Count - 1; i >= 0; i--) { - GenKillWeight /*!*/ - cweight = GetWeight(c, wi.cfg.impl, program); - Contract.Assert(cweight != null); - w = GenKillWeight.extend(cweight, w); + Cmd /*!*/ + c = wi.block.Cmds[i]; + Contract.Assert(c != null); + if (c is CallCmd && procICFG.ContainsKey(cce.NonNull(cce.NonNull((CallCmd) c).Proc).Name)) + { + w = GenKillWeight.extend(GetWeightCall(cce.NonNull((CallCmd) c)), w); + } + else + { + GenKillWeight /*!*/ + cweight = GetWeight(c, wi.cfg.impl, program); + Contract.Assert(cweight != null); + w = GenKillWeight.extend(cweight, w); + } } - } - bool change = wi.setWeightBefore(w); + bool change = wi.setWeightBefore(w); - if (change && wi.cfg.srcNodes.Contains(wi.block)) - { - GenKillWeight /*!*/ - prev = wi.cfg.summary; - Contract.Assert(prev != null); - GenKillWeight /*!*/ - curr = GenKillWeight.combine(prev, wi.cfg.weightBefore[wi.block]); - Contract.Assert(curr != null); - if (!GenKillWeight.isEqual(prev, curr)) + if (change && wi.cfg.srcNodes.Contains(wi.block)) { - wi.cfg.summary = curr; - // push callers onto the worklist - if (callers.ContainsKey(wi.cfg.impl.Name)) + GenKillWeight /*!*/ + prev = wi.cfg.summary; + Contract.Assert(prev != null); + GenKillWeight /*!*/ + curr = GenKillWeight.combine(prev, wi.cfg.weightBefore[wi.block]); + Contract.Assert(curr != null); + if (!GenKillWeight.isEqual(prev, curr)) { - foreach (WorkItem /*!*/ caller in callers[wi.cfg.impl.Name]) + wi.cfg.summary = curr; + // push callers onto the worklist + if (callers.ContainsKey(wi.cfg.impl.Name)) { - Contract.Assert(caller != null); - AddToWorkList(caller); + foreach (WorkItem /*!*/ caller in callers[wi.cfg.impl.Name]) + { + Contract.Assert(caller != null); + AddToWorkList(caller); + } } } } - } - foreach (Block /*!*/ b in wi.cfg.predEdges[wi.block]) - { - Contract.Assert(b != null); - GenKillWeight /*!*/ - prev = wi.cfg.weightAfter[b]; - Contract.Assert(prev != null); - GenKillWeight /*!*/ - curr = GenKillWeight.combine(prev, w); - Contract.Assert(curr != null); - if (!GenKillWeight.isEqual(prev, curr)) + foreach (Block /*!*/ b in wi.cfg.predEdges[wi.block]) { - wi.cfg.weightAfter[b] = curr; - AddToWorkList(new WorkItem(wi.cfg, b)); + Contract.Assert(b != null); + GenKillWeight /*!*/ + prev = wi.cfg.weightAfter[b]; + Contract.Assert(prev != null); + GenKillWeight /*!*/ + curr = GenKillWeight.combine(prev, w); + Contract.Assert(curr != null); + if (!GenKillWeight.isEqual(prev, curr)) + { + wi.cfg.weightAfter[b] = curr; + AddToWorkList(new WorkItem(wi.cfg, b)); + } } } - } - - static Dictionary /*!*/ - weightCache = new Dictionary(); - private GenKillWeight GetWeight(Cmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - return GetWeight(cmd, null, null); - } - - private GenKillWeight GetWeightCall(CallCmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - GenKillWeight /*!*/ - w1 = getWeightBeforeCall(cmd); - GenKillWeight /*!*/ - w2 = getSummary(cmd); - GenKillWeight /*!*/ - w3 = getWeightAfterCall(cmd); - Contract.Assert(w1 != null); - Contract.Assert(w2 != null); - Contract.Assert(w3 != null); - return GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); - } - - private GenKillWeight GetWeight(Cmd cmd, Implementation impl, Program prog) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); + static Dictionary /*!*/ + weightCache = new Dictionary(); - if (weightCache.ContainsKey(cmd)) + private GenKillWeight GetWeight(Cmd cmd) { - return weightCache[cmd]; + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + return GetWeight(cmd, null, null); } - HashSet /*!*/ - gen = new HashSet(); - HashSet /*!*/ - kill = new HashSet(); - GenKillWeight /*!*/ - ret; + private GenKillWeight GetWeightCall(CallCmd cmd) + { + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + GenKillWeight /*!*/ + w1 = getWeightBeforeCall(cmd); + GenKillWeight /*!*/ + w2 = getSummary(cmd); + GenKillWeight /*!*/ + w3 = getWeightAfterCall(cmd); + Contract.Assert(w1 != null); + Contract.Assert(w2 != null); + Contract.Assert(w3 != null); + return GenKillWeight.extend(w1, GenKillWeight.extend(w2, w3)); + } - if (cmd is AssignCmd) + private GenKillWeight GetWeight(Cmd cmd, Implementation impl, Program prog) { - AssignCmd /*!*/ - assignCmd = (AssignCmd) cmd; - Contract.Assert(cmd != null); - // I must first iterate over all the targets and remove the live ones. - // After the removals are done, I must add the variables referred on - // the right side of the removed targets - foreach (AssignLhs /*!*/ lhs in assignCmd.Lhss) + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + + if (weightCache.ContainsKey(cmd)) { - Contract.Assert(lhs != null); - Variable var = lhs.DeepAssignedVariable; - if (var != null) + return weightCache[cmd]; + } + + HashSet /*!*/ + gen = new HashSet(); + HashSet /*!*/ + kill = new HashSet(); + GenKillWeight /*!*/ + ret; + + if (cmd is AssignCmd) + { + AssignCmd /*!*/ + assignCmd = (AssignCmd) cmd; + Contract.Assert(cmd != null); + // I must first iterate over all the targets and remove the live ones. + // After the removals are done, I must add the variables referred on + // the right side of the removed targets + foreach (AssignLhs /*!*/ lhs in assignCmd.Lhss) { - if (lhs is SimpleAssignLhs) + Contract.Assert(lhs != null); + Variable var = lhs.DeepAssignedVariable; + if (var != null) { - // we should only remove non-map target variables because there is an implicit - // read of a map variable in an assignment to it - kill.Add(var); + if (lhs is SimpleAssignLhs) + { + // we should only remove non-map target variables because there is an implicit + // read of a map variable in an assignment to it + kill.Add(var); + } } } - } - int index = 0; - foreach (Expr /*!*/ expr in assignCmd.Rhss) - { - Contract.Assert(expr != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(expr); - gen.UnionWith(collector.usedVars); - AssignLhs lhs = assignCmd.Lhss[index]; - if (lhs is MapAssignLhs) + int index = 0; + foreach (Expr /*!*/ expr in assignCmd.Rhss) { - // If the target is a map, then all indices are also read - MapAssignLhs malhs = (MapAssignLhs) lhs; - foreach (Expr e in malhs.Indexes) + Contract.Assert(expr != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(expr); + gen.UnionWith(collector.usedVars); + AssignLhs lhs = assignCmd.Lhss[index]; + if (lhs is MapAssignLhs) { - VariableCollector /*!*/ - c = new VariableCollector(); - c.Visit(e); - gen.UnionWith(c.usedVars); + // If the target is a map, then all indices are also read + MapAssignLhs malhs = (MapAssignLhs) lhs; + foreach (Expr e in malhs.Indexes) + { + VariableCollector /*!*/ + c = new VariableCollector(); + c.Visit(e); + gen.UnionWith(c.usedVars); + } } + + index++; } - index++; + ret = new GenKillWeight(gen, kill); } - - ret = new GenKillWeight(gen, kill); - } - else if (cmd is HavocCmd) - { - HavocCmd /*!*/ - havocCmd = (HavocCmd) cce.NonNull(cmd); - foreach (IdentifierExpr /*!*/ expr in havocCmd.Vars) + else if (cmd is HavocCmd) { - Contract.Assert(expr != null); - if (expr.Decl != null) + HavocCmd /*!*/ + havocCmd = (HavocCmd) cce.NonNull(cmd); + foreach (IdentifierExpr /*!*/ expr in havocCmd.Vars) { - kill.Add(expr.Decl); + Contract.Assert(expr != null); + if (expr.Decl != null) + { + kill.Add(expr.Decl); + } } - } - ret = new GenKillWeight(gen, kill); - } - else if (cmd is PredicateCmd) - { - Contract.Assert((cmd is AssertCmd || cmd is AssumeCmd)); - PredicateCmd /*!*/ - predicateCmd = (PredicateCmd) cce.NonNull(cmd); - if (predicateCmd.Expr is LiteralExpr && prog != null && impl != null) + ret = new GenKillWeight(gen, kill); + } + else if (cmd is PredicateCmd) { - LiteralExpr le = (LiteralExpr) predicateCmd.Expr; - if (le.IsFalse) + Contract.Assert((cmd is AssertCmd || cmd is AssumeCmd)); + PredicateCmd /*!*/ + predicateCmd = (PredicateCmd) cce.NonNull(cmd); + if (predicateCmd.Expr is LiteralExpr && prog != null && impl != null) { - var globals = prog.GlobalVariables; - Contract.Assert(cce.NonNullElements(globals)); - foreach (Variable /*!*/ v in globals) + LiteralExpr le = (LiteralExpr) predicateCmd.Expr; + if (le.IsFalse) { - Contract.Assert(v != null); - kill.Add(v); - } + var globals = prog.GlobalVariables; + Contract.Assert(cce.NonNullElements(globals)); + foreach (Variable /*!*/ v in globals) + { + Contract.Assert(v != null); + kill.Add(v); + } - foreach (Variable /*!*/ v in impl.LocVars) - { - Contract.Assert(v != null); - kill.Add(v); - } + foreach (Variable /*!*/ v in impl.LocVars) + { + Contract.Assert(v != null); + kill.Add(v); + } - foreach (Variable /*!*/ v in impl.OutParams) - { - Contract.Assert(v != null); - kill.Add(v); + foreach (Variable /*!*/ v in impl.OutParams) + { + Contract.Assert(v != null); + kill.Add(v); + } } } + else + { + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(predicateCmd.Expr); + gen.UnionWith(collector.usedVars); + } + + ret = new GenKillWeight(gen, kill); } - else + else if (cmd is CommentCmd) { - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(predicateCmd.Expr); - gen.UnionWith(collector.usedVars); + ret = new GenKillWeight(gen, kill); + // comments are just for debugging and don't affect verification } - - ret = new GenKillWeight(gen, kill); - } - else if (cmd is CommentCmd) - { - ret = new GenKillWeight(gen, kill); - // comments are just for debugging and don't affect verification - } - else if (cmd is SugaredCmd) - { - SugaredCmd /*!*/ - sugCmd = (SugaredCmd) cmd; - Contract.Assert(sugCmd != null); - ret = GetWeight(sugCmd.GetDesugaring(options), impl, prog); - } - else if (cmd is StateCmd) - { - StateCmd /*!*/ - stCmd = (StateCmd) cmd; - Contract.Assert(stCmd != null); - List /*!*/ - cmds = stCmd.Cmds; - Contract.Assert(cmds != null); - int len = cmds.Count; - ret = GenKillWeight.one(); - for (int i = len - 1; i >= 0; i--) + else if (cmd is SugaredCmd) { - GenKillWeight /*!*/ - w = GetWeight(cmds[i], impl, prog); - Contract.Assert(w != null); - ret = GenKillWeight.extend(w, ret); + SugaredCmd /*!*/ + sugCmd = (SugaredCmd) cmd; + Contract.Assert(sugCmd != null); + ret = GetWeight(sugCmd.GetDesugaring(options), impl, prog); } - - foreach (Variable /*!*/ v in stCmd.Locals) + else if (cmd is StateCmd) { - Contract.Assert(v != null); - kill.Add(v); - } + StateCmd /*!*/ + stCmd = (StateCmd) cmd; + Contract.Assert(stCmd != null); + List /*!*/ + cmds = stCmd.Cmds; + Contract.Assert(cmds != null); + int len = cmds.Count; + ret = GenKillWeight.one(); + for (int i = len - 1; i >= 0; i--) + { + GenKillWeight /*!*/ + w = GetWeight(cmds[i], impl, prog); + Contract.Assert(w != null); + ret = GenKillWeight.extend(w, ret); + } - ret = GenKillWeight.extend(new GenKillWeight(gen, kill), ret); - } - else - { + foreach (Variable /*!*/ v in stCmd.Locals) + { + Contract.Assert(v != null); + kill.Add(v); + } + + ret = GenKillWeight.extend(new GenKillWeight(gen, kill), ret); + } + else { - Contract.Assert(false); - throw new cce.UnreachableException(); + { + Contract.Assert(false); + throw new cce.UnreachableException(); + } } - } - - weightCache[cmd] = ret; - return ret; - } - static Dictionary /*!*/ - weightCacheAfterCall = new Dictionary(); + weightCache[cmd] = ret; + return ret; + } - static Dictionary /*!*/ - weightCacheBeforeCall = new Dictionary(); + static Dictionary /*!*/ + weightCacheAfterCall = new Dictionary(); - private static GenKillWeight getWeightAfterCall(Cmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); + static Dictionary /*!*/ + weightCacheBeforeCall = new Dictionary(); - if (weightCacheAfterCall.ContainsKey(cmd)) + private static GenKillWeight getWeightAfterCall(Cmd cmd) { - return weightCacheAfterCall[cmd]; - } + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); - HashSet /*!*/ - gen = new HashSet(); - HashSet /*!*/ - kill = new HashSet(); + if (weightCacheAfterCall.ContainsKey(cmd)) + { + return weightCacheAfterCall[cmd]; + } - Contract.Assert(cmd is CallCmd); - CallCmd /*!*/ - ccmd = cce.NonNull((CallCmd) cmd); + HashSet /*!*/ + gen = new HashSet(); + HashSet /*!*/ + kill = new HashSet(); - foreach (IdentifierExpr /*!*/ ie in ccmd.Outs) - { - Contract.Assert(ie != null); - if (ie.Decl != null) + Contract.Assert(cmd is CallCmd); + CallCmd /*!*/ + ccmd = cce.NonNull((CallCmd) cmd); + + foreach (IdentifierExpr /*!*/ ie in ccmd.Outs) { - kill.Add(ie.Decl); + Contract.Assert(ie != null); + if (ie.Decl != null) + { + kill.Add(ie.Decl); + } } - } - // Variables in ensures are considered as "read" - foreach (Ensures /*!*/ re in cce.NonNull(ccmd.Proc).Ensures) - { - Contract.Assert(re != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(re.Condition); - foreach (Variable /*!*/ v in collector.usedVars) + // Variables in ensures are considered as "read" + foreach (Ensures /*!*/ re in cce.NonNull(ccmd.Proc).Ensures) { - Contract.Assert(v != null); - if (v is GlobalVariable) + Contract.Assert(re != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(re.Condition); + foreach (Variable /*!*/ v in collector.usedVars) { - gen.Add(v); + Contract.Assert(v != null); + if (v is GlobalVariable) + { + gen.Add(v); + } } } - } - GenKillWeight /*!*/ - ret = new GenKillWeight(gen, kill); - Contract.Assert(ret != null); - weightCacheAfterCall[cmd] = ret; - return ret; - } + GenKillWeight /*!*/ + ret = new GenKillWeight(gen, kill); + Contract.Assert(ret != null); + weightCacheAfterCall[cmd] = ret; + return ret; + } - private static GenKillWeight getWeightBeforeCall(Cmd cmd) - { - Contract.Requires(cmd != null); - Contract.Ensures(Contract.Result() != null); - Contract.Assert((cmd is CallCmd)); - if (weightCacheBeforeCall.ContainsKey(cmd)) + private static GenKillWeight getWeightBeforeCall(Cmd cmd) { - return weightCacheBeforeCall[cmd]; - } + Contract.Requires(cmd != null); + Contract.Ensures(Contract.Result() != null); + Contract.Assert((cmd is CallCmd)); + if (weightCacheBeforeCall.ContainsKey(cmd)) + { + return weightCacheBeforeCall[cmd]; + } - HashSet /*!*/ - gen = new HashSet(); - HashSet /*!*/ - kill = new HashSet(); - CallCmd /*!*/ - ccmd = cce.NonNull((CallCmd /*!*/) cmd); + HashSet /*!*/ + gen = new HashSet(); + HashSet /*!*/ + kill = new HashSet(); + CallCmd /*!*/ + ccmd = cce.NonNull((CallCmd /*!*/) cmd); - foreach (Expr /*!*/ expr in ccmd.Ins) - { - Contract.Assert(expr != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(expr); - gen.UnionWith(collector.usedVars); - } + foreach (Expr /*!*/ expr in ccmd.Ins) + { + Contract.Assert(expr != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(expr); + gen.UnionWith(collector.usedVars); + } - Contract.Assert(ccmd.Proc != null); + Contract.Assert(ccmd.Proc != null); - // Variables in requires are considered as "read" - foreach (Requires /*!*/ re in ccmd.Proc.Requires) - { - Contract.Assert(re != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(re.Condition); - foreach (Variable /*!*/ v in collector.usedVars) + // Variables in requires are considered as "read" + foreach (Requires /*!*/ re in ccmd.Proc.Requires) { - Contract.Assert(v != null); - if (v is GlobalVariable) + Contract.Assert(re != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(re.Condition); + foreach (Variable /*!*/ v in collector.usedVars) { - gen.Add(v); + Contract.Assert(v != null); + if (v is GlobalVariable) + { + gen.Add(v); + } } } - } - // Old variables in ensures are considered as "read" - foreach (Ensures /*!*/ re in ccmd.Proc.Ensures) - { - Contract.Assert(re != null); - VariableCollector /*!*/ - collector = new VariableCollector(); - collector.Visit(re.Condition); - foreach (Variable /*!*/ v in collector.oldVarsUsed) + // Old variables in ensures are considered as "read" + foreach (Ensures /*!*/ re in ccmd.Proc.Ensures) { - Contract.Assert(v != null); - if (v is GlobalVariable) + Contract.Assert(re != null); + VariableCollector /*!*/ + collector = new VariableCollector(); + collector.Visit(re.Condition); + foreach (Variable /*!*/ v in collector.oldVarsUsed) { - gen.Add(v); + Contract.Assert(v != null); + if (v is GlobalVariable) + { + gen.Add(v); + } } } - } - GenKillWeight /*!*/ - ret = new GenKillWeight(gen, kill); - Contract.Assert(ret != null); - weightCacheAfterCall[cmd] = ret; - return ret; + GenKillWeight /*!*/ + ret = new GenKillWeight(gen, kill); + Contract.Assert(ret != null); + weightCacheAfterCall[cmd] = ret; + return ret; + } } } \ No newline at end of file From 24d5accf78fd82dbe5a81bd39a4c59e5cb83e7b4 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 13 Aug 2024 13:46:08 +0200 Subject: [PATCH 40/42] Undo changes --- .../LiveVariableAnalysis/GenKillWeight.cs | 293 +++++++++--------- .../LiveVariableAnalysis/InterProcGenKill.cs | 13 - 2 files changed, 146 insertions(+), 160 deletions(-) diff --git a/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs b/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs index 3a4dc7288..a225e6562 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs @@ -1,191 +1,190 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; -namespace Microsoft.Boogie +namespace Microsoft.Boogie; + +/* +// An idempotent semiring interface +abstract public class Weight { + abstract public Weight! one(); + abstract public Weight! zero(); + abstract public Weight! extend(Weight! w1, Weight! w2); + abstract public Weight! combine(Weight! w1, Weight! w2); + abstract public Weight! isEqual(Weight! w); + abstract public Weight! projectLocals() +} +*/ + +// Weight domain for LiveVariableAnalysis (Gen/Kill) + +public class GenKillWeight { - /* - // An idempotent semiring interface - abstract public class Weight { - abstract public Weight! one(); - abstract public Weight! zero(); - abstract public Weight! extend(Weight! w1, Weight! w2); - abstract public Weight! combine(Weight! w1, Weight! w2); - abstract public Weight! isEqual(Weight! w); - abstract public Weight! projectLocals() - } - */ + // lambda S. (S - kill) union gen + HashSet /*!*/ + gen; - // Weight domain for LiveVariableAnalysis (Gen/Kill) + HashSet /*!*/ + kill; - public class GenKillWeight + [ContractInvariantMethod] + void ObjectInvariant() { - // lambda S. (S - kill) union gen - HashSet /*!*/ - gen; + Contract.Invariant(cce.NonNullElements(gen)); + Contract.Invariant(cce.NonNullElements(kill)); + Contract.Invariant(oneWeight != null); + Contract.Invariant(zeroWeight != null); + } - HashSet /*!*/ - kill; + bool isZero; - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cce.NonNullElements(gen)); - Contract.Invariant(cce.NonNullElements(kill)); - Contract.Invariant(oneWeight != null); - Contract.Invariant(zeroWeight != null); - } + public static GenKillWeight /*!*/ + oneWeight = new GenKillWeight(new HashSet(), new HashSet()); - bool isZero; + public static GenKillWeight /*!*/ + zeroWeight = new GenKillWeight(); - public static GenKillWeight /*!*/ - oneWeight = new GenKillWeight(new HashSet(), new HashSet()); + // initializes to zero + public GenKillWeight() + { + this.isZero = true; + this.gen = new HashSet(); + this.kill = new HashSet(); + } - public static GenKillWeight /*!*/ - zeroWeight = new GenKillWeight(); + public GenKillWeight(HashSet gen, HashSet kill) + { + Contract.Requires(cce.NonNullElements(gen)); + Contract.Requires(cce.NonNullElements(kill)); + Contract.Assert(gen != null); + Contract.Assert(kill != null); + this.gen = gen; + this.kill = kill; + this.isZero = false; + } - // initializes to zero - public GenKillWeight() - { - this.isZero = true; - this.gen = new HashSet(); - this.kill = new HashSet(); - } + public static GenKillWeight one() + { + Contract.Ensures(Contract.Result() != null); + return oneWeight; + } - public GenKillWeight(HashSet gen, HashSet kill) - { - Contract.Requires(cce.NonNullElements(gen)); - Contract.Requires(cce.NonNullElements(kill)); - Contract.Assert(gen != null); - Contract.Assert(kill != null); - this.gen = gen; - this.kill = kill; - this.isZero = false; - } + public static GenKillWeight zero() + { + Contract.Ensures(Contract.Result() != null); + return zeroWeight; + } - public static GenKillWeight one() + public static GenKillWeight extend(GenKillWeight w1, GenKillWeight w2) + { + Contract.Requires(w2 != null); + Contract.Requires(w1 != null); + Contract.Ensures(Contract.Result() != null); + if (w1.isZero || w2.isZero) { - Contract.Ensures(Contract.Result() != null); - return oneWeight; + return zero(); } - public static GenKillWeight zero() - { - Contract.Ensures(Contract.Result() != null); - return zeroWeight; - } + HashSet t = new HashSet(w2.gen); + t.ExceptWith(w1.kill); + HashSet g = new HashSet(w1.gen); + g.UnionWith(t); + HashSet k = new HashSet(w1.kill); + k.UnionWith(w2.kill); + return new GenKillWeight(g, k); + //return new GenKillWeight(w1.gen.Union(w2.gen.Difference(w1.kill)), w1.kill.Union(w2.kill)); + } - public static GenKillWeight extend(GenKillWeight w1, GenKillWeight w2) + public static GenKillWeight combine(GenKillWeight w1, GenKillWeight w2) + { + Contract.Requires(w2 != null); + Contract.Requires(w1 != null); + Contract.Ensures(Contract.Result() != null); + if (w1.isZero) { - Contract.Requires(w2 != null); - Contract.Requires(w1 != null); - Contract.Ensures(Contract.Result() != null); - if (w1.isZero || w2.isZero) - { - return zero(); - } - - HashSet t = new HashSet(w2.gen); - t.ExceptWith(w1.kill); - HashSet g = new HashSet(w1.gen); - g.UnionWith(t); - HashSet k = new HashSet(w1.kill); - k.UnionWith(w2.kill); - return new GenKillWeight(g, k); - //return new GenKillWeight(w1.gen.Union(w2.gen.Difference(w1.kill)), w1.kill.Union(w2.kill)); + return w2; } - public static GenKillWeight combine(GenKillWeight w1, GenKillWeight w2) + if (w2.isZero) { - Contract.Requires(w2 != null); - Contract.Requires(w1 != null); - Contract.Ensures(Contract.Result() != null); - if (w1.isZero) - { - return w2; - } - - if (w2.isZero) - { - return w1; - } - - HashSet g = new HashSet(w1.gen); - g.UnionWith(w2.gen); - HashSet k = new HashSet(w1.kill); - k.IntersectWith(w2.kill); - return new GenKillWeight(g, k); - //return new GenKillWeight(w1.gen.Union(w2.gen), w1.kill.Intersection(w2.kill)); + return w1; } - public static GenKillWeight projectLocals(GenKillWeight w) - { - Contract.Requires(w != null); - Contract.Ensures(Contract.Result() != null); - HashSet gen = new HashSet(); - foreach (Variable v in w.gen) - { - if (isGlobal(v)) - { - gen.Add(v); - } - } + HashSet g = new HashSet(w1.gen); + g.UnionWith(w2.gen); + HashSet k = new HashSet(w1.kill); + k.IntersectWith(w2.kill); + return new GenKillWeight(g, k); + //return new GenKillWeight(w1.gen.Union(w2.gen), w1.kill.Intersection(w2.kill)); + } - HashSet kill = new HashSet(); - foreach (Variable v in w.kill) + public static GenKillWeight projectLocals(GenKillWeight w) + { + Contract.Requires(w != null); + Contract.Ensures(Contract.Result() != null); + HashSet gen = new HashSet(); + foreach (Variable v in w.gen) + { + if (isGlobal(v)) { - if (isGlobal(v)) - { - kill.Add(v); - } + gen.Add(v); } - - return new GenKillWeight(gen, kill); } - public static bool isEqual(GenKillWeight w1, GenKillWeight w2) + HashSet kill = new HashSet(); + foreach (Variable v in w.kill) { - Contract.Requires(w2 != null); - Contract.Requires(w1 != null); - if (w1.isZero) + if (isGlobal(v)) { - return w2.isZero; + kill.Add(v); } - - if (w2.isZero) - { - return w1.isZero; - } - - return (w1.gen.Equals(w2.gen) && w1.kill.Equals(w2.kill)); } - private static bool isGlobal(Variable v) - { - Contract.Requires(v != null); - return (v is GlobalVariable); - } + return new GenKillWeight(gen, kill); + } - [Pure] - public override string ToString() + public static bool isEqual(GenKillWeight w1, GenKillWeight w2) + { + Contract.Requires(w2 != null); + Contract.Requires(w1 != null); + if (w1.isZero) { - Contract.Ensures(Contract.Result() != null); - return string.Format("({0},{1})", gen.ToString(), kill.ToString()); + return w2.isZero; } - public HashSet /*!*/ getLiveVars() + if (w2.isZero) { - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - return gen; + return w1.isZero; } - public HashSet /*!*/ getLiveVars(HashSet /*!*/ lv) - { - Contract.Requires(cce.NonNullElements(lv)); - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - HashSet temp = new HashSet(lv); - temp.ExceptWith(kill); - temp.UnionWith(gen); - return temp; - } + return (w1.gen.Equals(w2.gen) && w1.kill.Equals(w2.kill)); + } + + private static bool isGlobal(Variable v) + { + Contract.Requires(v != null); + return (v is GlobalVariable); + } + + [Pure] + public override string ToString() + { + Contract.Ensures(Contract.Result() != null); + return string.Format("({0},{1})", gen.ToString(), kill.ToString()); + } + + public HashSet /*!*/ getLiveVars() + { + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + return gen; + } + + public HashSet /*!*/ getLiveVars(HashSet /*!*/ lv) + { + Contract.Requires(cce.NonNullElements(lv)); + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + HashSet temp = new HashSet(lv); + temp.ExceptWith(kill); + temp.UnionWith(gen); + return temp; } } \ No newline at end of file diff --git a/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs b/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs index c65d18c5b..ae531c810 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs @@ -553,19 +553,6 @@ public void Compute() varsLiveAtEntry.Add(cfg.impl.Name, lv); varsLiveSummary.Add(cfg.impl.Name, cfg.summary); } - - /* - foreach(Block/*!*/ - /* b in mainImpl.Blocks){ - Contract.Assert(b != null); - //Set lv = cfg.weightBefore[b].getLiveVars(); - b.liveVarsBefore = procICFG[mainImpl.Name].liveVarsAfter[b]; - //foreach(GlobalVariable/*!*/ - /* v in program.GlobalVariables){Contract.Assert(v != null); - // b.liveVarsBefore.Add(v); - //} - } - */ } // Called when summaries have already been computed From 7f3d836188150affdb494317edf1b15194917aa6 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 13 Aug 2024 13:47:04 +0200 Subject: [PATCH 41/42] Reduce changes --- Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs | 1 - Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs b/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs index a225e6562..60d2578d3 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/GenKillWeight.cs @@ -16,7 +16,6 @@ abstract public class Weight { */ // Weight domain for LiveVariableAnalysis (Gen/Kill) - public class GenKillWeight { // lambda S. (S - kill) union gen diff --git a/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs b/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs index ae531c810..147221dd9 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/InterProcGenKill.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -using System.Diagnostics.Contracts; using Microsoft.Boogie.GraphUtil; +using System.Diagnostics.Contracts; namespace Microsoft.Boogie { From 100ee6c79234bb273dcb2ac5d473fe28d4975909 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 15 Aug 2024 18:16:49 +0200 Subject: [PATCH 42/42] Cleanup --- Source/Core/AST/AbsyCmd.cs | 2 +- Source/VCGeneration/ManualSplitFinder.cs | 1 - Source/VCGeneration/OldBlockTransformations.cs | 9 --------- 3 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 Source/VCGeneration/OldBlockTransformations.cs diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 3a0bc2f09..85d35413d 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -3497,7 +3497,7 @@ void ObjectInvariant() { Contract.Invariant(labelNames == null || labelTargets == null || labelNames.Count == labelTargets.Count); } - + [NotDelayed] public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq) : base(tok) diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index d32354273..5fe10f268 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; -using System.Threading.Tasks.Dataflow; using Microsoft.Boogie; using VC; diff --git a/Source/VCGeneration/OldBlockTransformations.cs b/Source/VCGeneration/OldBlockTransformations.cs deleted file mode 100644 index 63d1f1f34..000000000 --- a/Source/VCGeneration/OldBlockTransformations.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.Contracts; -using System.Linq; -using Microsoft.Boogie; - -namespace VCGeneration; - -class OldBlockTransformations { -} \ No newline at end of file