From 576e153396468784f0785d115debed5ae820d251 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 15:06:08 +0200 Subject: [PATCH 01/70] IsolatePaths option seems to work, although it still duplicates errors --- Source/ExecutionEngine/CommandLineOptions.cs | 11 +++++ Source/VCGeneration/ManualSplitFinder.cs | 45 +++++++++++++++++-- Source/VCGeneration/VCGenOptions.cs | 1 + .../VerificationConditionGenerator.cs | 20 ++++----- Test/pruning/isolatePaths.bpl | 14 ++++++ Test/pruning/isolatePaths.bpl.expect | 0 6 files changed, 77 insertions(+), 14 deletions(-) create mode 100644 Test/pruning/isolatePaths.bpl create mode 100644 Test/pruning/isolatePaths.bpl.expect diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 171797990..c97396c65 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -450,6 +450,7 @@ public bool TrustSequentialization { } public bool RemoveEmptyBlocks { get; set; } = true; + public bool IsolatePaths { get; set; } public bool CoalesceBlocks { get; set; } = true; public bool PruneInfeasibleEdges { get; set; } = true; @@ -969,6 +970,16 @@ protected override bool ParseOption(string name, CommandLineParseState ps) return true; } + case "isolatePaths": + { + int cb = 0; + if (ps.GetIntArgument(x => cb = x, 2)) + { + CoalesceBlocks = cb == 1; + } + + return true; + } case "coalesceBlocks": { int cb = 0; diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index 5fe10f268..fbb65fe6f 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.Contracts; using System.Linq; using Microsoft.Boogie; @@ -10,13 +11,48 @@ namespace VCGeneration; public static class ManualSplitFinder { 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(s => FindManualSplits(program, s)); + var paths = options.IsolatePaths || QKeyValue.FindBoolAttribute(run.Implementation.Attributes, "isolate_paths") + ? PathSplits(options, run, gotoCmdOrigins, par) + : FocusAttribute.FocusImpl(options, run, gotoCmdOrigins, par); + return paths.SelectMany(s => FindManualSplits(program, s)); + } + + private static List PathSplits(VCGenOptions options, ImplementationRun run, + Dictionary gotoCmdOrigins, VerificationConditionGenerator par) + { + var result = new List(); + var firstBlock = run.Implementation.Blocks[0]; + var paths = new Stack>(); + paths.Push(ImmutableStack.Empty.Push(firstBlock)); + while (paths.Any()) + { + var current = paths.Pop(); + var last = current.Peek(); + if (last.TransferCmd is not GotoCmd gotoCmd) + { + result.Add(new ManualSplit(options, () => + { + var masterBlock = new Block(firstBlock.tok) + { + Label = firstBlock.Label, + Cmds = current.Reverse().SelectMany(block => block.Cmds).ToList(), + TransferCmd = current.Peek().TransferCmd + }; + return new List { masterBlock }; + }, gotoCmdOrigins, par, run, run.Implementation.tok)); + continue; + } + + foreach (var target in gotoCmd.labelTargets) + { + paths.Push(current.Push(target)); + } + } + + return result; } 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); @@ -120,6 +156,7 @@ private static List SplitOnAssert(VCGenOptions options, List oldBl AddBlockJumps(oldBlocks, oldToNewBlockMap); return newBlocks; } + private static List? DoPreAssignedManualSplit(VCGenOptions options, List blocks, Dictionary blockAssignments, int splitNumberWithinBlock, Block containingBlock, bool lastSplitInBlock, bool splitOnEveryAssert) { diff --git a/Source/VCGeneration/VCGenOptions.cs b/Source/VCGeneration/VCGenOptions.cs index 6fc1ca5f5..d7129f617 100644 --- a/Source/VCGeneration/VCGenOptions.cs +++ b/Source/VCGeneration/VCGenOptions.cs @@ -6,4 +6,5 @@ public interface VCGenOptions : SMTLibOptions bool AlwaysAssumeFreeLoopInvariants { get; } int LiveVariableAnalysis { get; } bool RemoveEmptyBlocks { get; } + bool IsolatePaths { get; } } \ No newline at end of file diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index c8bf280c3..378e0bd0d 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -2153,34 +2153,34 @@ VCExpr LetVC(List blocks, IEnumerable sortedNodes = dag.TopologicalSort(); Contract.Assert(sortedNodes != null); - Dictionary blockVariables = new Dictionary(); + var blockVariables = new Dictionary(); List bindings = new List(); VCExpressionGenerator gen = proverCtxt.ExprGen; Contract.Assert(gen != null); foreach (Block block in sortedNodes) { - VCExpr SuccCorrect; - GotoCmd gotocmd = block.TransferCmd as GotoCmd; + VCExpr succCorrect; + var gotocmd = block.TransferCmd as GotoCmd; if (gotocmd == null) { ReturnExprCmd re = block.TransferCmd as ReturnExprCmd; if (re == null) { - SuccCorrect = VCExpressionGenerator.True; + succCorrect = VCExpressionGenerator.True; } else { - SuccCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr); + succCorrect = proverCtxt.BoogieExprTranslator.Translate(re.Expr); if (isPositiveContext) { - SuccCorrect = gen.Not(SuccCorrect); + succCorrect = gen.Not(succCorrect); } } } else { Contract.Assert(gotocmd.labelTargets != null); - List SuccCorrectVars = new List(gotocmd.labelTargets.Count); + var succCorrectVars = new List(gotocmd.labelTargets.Count); foreach (Block successor in gotocmd.labelTargets) { Contract.Assert(successor != null); @@ -2194,14 +2194,14 @@ VCExpr LetVC(List blocks, s = gen.Implies(controlTransferExpr, s); } - SuccCorrectVars.Add(s); + succCorrectVars.Add(s); } - SuccCorrect = gen.NAry(VCExpressionGenerator.AndOp, SuccCorrectVars); + succCorrect = gen.NAry(VCExpressionGenerator.AndOp, succCorrectVars); } VCContext context = new VCContext(Options, absyIds, proverCtxt, controlFlowVariableExpr, isPositiveContext); - VCExpr vc = Wlp.Block(block, SuccCorrect, context); + VCExpr vc = Wlp.Block(block, succCorrect, context); assertionCount += context.AssertionCount; VCExprVar v = gen.Variable(block.Label + "_correct", Bpl.Type.Bool); diff --git a/Test/pruning/isolatePaths.bpl b/Test/pruning/isolatePaths.bpl new file mode 100644 index 000000000..6a168f523 --- /dev/null +++ b/Test/pruning/isolatePaths.bpl @@ -0,0 +1,14 @@ +// RUN: %parallel-boogie "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure {:isolate_paths} NoDuplicateErrors() +{ + var x: int; + if (*) { + x := 3; + } + else { + x := 2; + } + assert x == 1; +} \ No newline at end of file diff --git a/Test/pruning/isolatePaths.bpl.expect b/Test/pruning/isolatePaths.bpl.expect new file mode 100644 index 000000000..e69de29bb From 8d0bee67994f1af23a269a95a35ea42a461a031a Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 15:23:27 +0200 Subject: [PATCH 02/70] Refactoring --- Source/Core/AST/AbsyCmd.cs | 6 +- Source/ExecutionEngine/ExecutionEngine.cs | 1 + Source/Houdini/Houdini.cs | 9 +- Source/Houdini/HoudiniSession.cs | 6 +- Source/VCGeneration/ConditionGeneration.cs | 4 +- .../Counterexample/AssertCounterexample.cs | 46 +++ .../Counterexample/CallCounterexample.cs | 59 +++ .../CalleeCounterexampleInfo.cs | 24 ++ .../{ => Counterexample}/Counterexample.cs | 386 +----------------- .../Counterexample/CounterexampleComparer.cs | 79 ++++ .../Counterexample/ReturnCounterexample.cs | 61 +++ .../Counterexample/TraceLocation.cs | 36 ++ .../Counterexample/VerifierCallback.cs | 71 ++++ Source/VCGeneration/StratifiedVC.cs | 2 +- .../VerificationConditionGenerator.cs | 41 +- .../VerificationResultCollector.cs | 12 +- 16 files changed, 434 insertions(+), 409 deletions(-) create mode 100644 Source/VCGeneration/Counterexample/AssertCounterexample.cs create mode 100644 Source/VCGeneration/Counterexample/CallCounterexample.cs create mode 100644 Source/VCGeneration/Counterexample/CalleeCounterexampleInfo.cs rename Source/VCGeneration/{ => Counterexample}/Counterexample.cs (55%) create mode 100644 Source/VCGeneration/Counterexample/CounterexampleComparer.cs create mode 100644 Source/VCGeneration/Counterexample/ReturnCounterexample.cs create mode 100644 Source/VCGeneration/Counterexample/TraceLocation.cs create mode 100644 Source/VCGeneration/Counterexample/VerifierCallback.cs diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index fb84e2a35..485a88877 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -3166,11 +3166,9 @@ public LoopInvMaintainedAssertCmd(IToken /*!*/ tok, Expr /*!*/ expr, AssertCmd o /// public class AssertRequiresCmd : AssertCmd { - public CallCmd /*!*/ - Call; + public CallCmd /*!*/ Call; - public Requires /*!*/ - Requires; + public Requires /*!*/ Requires; [ContractInvariantMethod] void ObjectInvariant() diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 23e42ec75..cde400b3e 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1310,6 +1310,7 @@ public void ProcessErrors(OutputPrinter printer, return; } + errors = errors.DistinctBy(e => e.AssertCmd).ToList(); errors.Sort(new CounterexampleComparer()); foreach (Counterexample error in errors) { diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index 11a51b434..14f68ce7a 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -1451,8 +1451,7 @@ protected void DebugRefutedCandidates(Implementation curFunc, List assignment, int errorLimit) { - collector.examples.Clear(); + collector.Examples.Clear(); if (Options.Trace) { @@ -278,7 +278,7 @@ private VCExpr BuildAxiom(ProverInterface proverInterface, Dictionary CheckerPool.Options; diff --git a/Source/VCGeneration/Counterexample/AssertCounterexample.cs b/Source/VCGeneration/Counterexample/AssertCounterexample.cs new file mode 100644 index 000000000..3959b75da --- /dev/null +++ b/Source/VCGeneration/Counterexample/AssertCounterexample.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using VC; + +namespace Microsoft.Boogie; + +public class AssertCounterexample : Counterexample +{ + [Peer] public readonly AssertCmd FailingAssert; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(FailingAssert != null); + } + + + public AssertCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertCmd failingAssert, Model model, VC.ModelViewInfo mvInfo, + ProverContext context, ProofRun proofRun) + : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun, failingAssert) + { + Contract.Requires(trace != null); + Contract.Requires(failingAssert != null); + Contract.Requires(context != null); + this.FailingAssert = failingAssert; + } + + protected override Cmd ModelFailingCommand => FailingAssert; + + public override int GetLocation() + { + return FailingAssert.tok.line * 1000 + FailingAssert.tok.col; + } + + public override byte[] Checksum + { + get { return FailingAssert.Checksum; } + } + + public override Counterexample Clone() + { + var ret = new AssertCounterexample(Options, Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context, ProofRun); + ret.CalleeCounterexamples = CalleeCounterexamples; + return ret; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Counterexample/CallCounterexample.cs b/Source/VCGeneration/Counterexample/CallCounterexample.cs new file mode 100644 index 000000000..c404cac65 --- /dev/null +++ b/Source/VCGeneration/Counterexample/CallCounterexample.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using VC; + +namespace Microsoft.Boogie; + +public class CallCounterexample : Counterexample +{ + public readonly CallCmd FailingCall; + public readonly Requires FailingRequires; + public readonly AssertRequiresCmd FailingAssert; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(FailingCall != null); + Contract.Invariant(FailingRequires != null); + } + + + public CallCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertRequiresCmd assertRequiresCmd, Model model, + VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, byte[] checksum = null) + : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun, assertRequiresCmd) + { + var failingRequires = assertRequiresCmd.Requires; + var failingCall = assertRequiresCmd.Call; + Contract.Requires(!failingRequires.Free); + Contract.Requires(trace != null); + Contract.Requires(context != null); + Contract.Requires(failingCall != null); + Contract.Requires(failingRequires != null); + this.FailingCall = failingCall; + this.FailingRequires = failingRequires; + this.FailingAssert = assertRequiresCmd; + this.checksum = checksum; + this.SugaredCmdChecksum = failingCall.Checksum; + } + + protected override Cmd ModelFailingCommand => FailingCall; + + public override int GetLocation() + { + return FailingCall.tok.line * 1000 + FailingCall.tok.col; + } + + byte[] checksum; + + public override byte[] Checksum + { + get { return checksum; } + } + + public override Counterexample Clone() + { + var ret = new CallCounterexample(Options, Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context, ProofRun, Checksum); + ret.CalleeCounterexamples = CalleeCounterexamples; + return ret; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Counterexample/CalleeCounterexampleInfo.cs b/Source/VCGeneration/Counterexample/CalleeCounterexampleInfo.cs new file mode 100644 index 000000000..cde621743 --- /dev/null +++ b/Source/VCGeneration/Counterexample/CalleeCounterexampleInfo.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class CalleeCounterexampleInfo +{ + public readonly Counterexample Counterexample; + + public readonly List /*!>!*/ Args; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cce.NonNullElements(Args)); + } + + public CalleeCounterexampleInfo(Counterexample cex, List!*/> x) + { + Contract.Requires(cce.NonNullElements(x)); + Counterexample = cex; + Args = x; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Counterexample.cs b/Source/VCGeneration/Counterexample/Counterexample.cs similarity index 55% rename from Source/VCGeneration/Counterexample.cs rename to Source/VCGeneration/Counterexample/Counterexample.cs index 31cafb18c..f22def9ad 100644 --- a/Source/VCGeneration/Counterexample.cs +++ b/Source/VCGeneration/Counterexample/Counterexample.cs @@ -10,60 +10,6 @@ namespace Microsoft.Boogie { - public class CalleeCounterexampleInfo - { - public Counterexample counterexample; - - public List /*!>!*/ - args; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cce.NonNullElements(args)); - } - - public CalleeCounterexampleInfo(Counterexample cex, List!*/> x) - { - Contract.Requires(cce.NonNullElements(x)); - counterexample = cex; - args = x; - } - } - - public class TraceLocation : IEquatable - { - public int numBlock; - public int numInstr; - - public TraceLocation(int numBlock, int numInstr) - { - this.numBlock = numBlock; - this.numInstr = numInstr; - } - - public override bool Equals(object obj) - { - TraceLocation that = obj as TraceLocation; - if (that == null) - { - return false; - } - - return (numBlock == that.numBlock && numInstr == that.numInstr); - } - - public bool Equals(TraceLocation that) - { - return (numBlock == that.numBlock && numInstr == that.numInstr); - } - - public override int GetHashCode() - { - return numBlock.GetHashCode() ^ 131 * numInstr.GetHashCode(); - } - } - public abstract class Counterexample { @@ -72,34 +18,36 @@ void ObjectInvariant() { Contract.Invariant(Trace != null); Contract.Invariant(Context != null); - Contract.Invariant(cce.NonNullDictionaryAndValues(calleeCounterexamples)); + Contract.Invariant(cce.NonNullDictionaryAndValues(CalleeCounterexamples)); } public ProofRun ProofRun { get; } - protected readonly VCGenOptions options; + protected readonly VCGenOptions Options; [Peer] public List Trace; - public List AugmentedTrace; + public readonly List AugmentedTrace; + public AssertCmd AssertCmd { get; } public Model Model { get; } - public ModelViewInfo MvInfo; - public ProverContext Context; + public readonly ModelViewInfo MvInfo; + public readonly ProverContext Context; public abstract byte[] Checksum { get; } public byte[] SugaredCmdChecksum; public bool IsAuxiliaryCexForDiagnosingTimeouts; - public Dictionary calleeCounterexamples; + public Dictionary CalleeCounterexamples; internal Counterexample(VCGenOptions options, List trace, List augmentedTrace, Model model, - VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun) + VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, AssertCmd assertCmd) { Contract.Requires(trace != null); Contract.Requires(context != null); - this.options = options; + this.Options = options; this.Trace = trace; this.Model = model; this.MvInfo = mvInfo; this.Context = context; this.ProofRun = proofRun; - this.calleeCounterexamples = new Dictionary(); + this.AssertCmd = assertCmd; + this.CalleeCounterexamples = new Dictionary(); // the call to instance method GetModelValue in the following code requires the fields Model and Context to be initialized if (augmentedTrace != null) { @@ -114,13 +62,13 @@ internal Counterexample(VCGenOptions options, List trace, List au public void AddCalleeCounterexample(TraceLocation loc, CalleeCounterexampleInfo cex) { Contract.Requires(cex != null); - calleeCounterexamples[loc] = cex; + CalleeCounterexamples[loc] = cex; } public void AddCalleeCounterexample(int numBlock, int numInstr, CalleeCounterexampleInfo cex) { Contract.Requires(cex != null); - calleeCounterexamples[new TraceLocation(numBlock, numInstr)] = cex; + CalleeCounterexamples[new TraceLocation(numBlock, numInstr)] = cex; } public void AddCalleeCounterexample(Dictionary cs) @@ -179,7 +127,7 @@ public void Print(int indent, TextWriter tw, Action blockAction = null) // for ErrorTrace == 1 restrict the output; // do not print tokens with -17:-4 as their location because they have been // introduced in the translation and do not give any useful feedback to the user - if (!(options.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4)) + if (!(Options.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4)) { if (blockAction != null) { @@ -191,21 +139,21 @@ public void Print(int indent, TextWriter tw, Action blockAction = null) for (int numInstr = 0; numInstr < b.Cmds.Count; numInstr++) { var loc = new TraceLocation(numBlock, numInstr); - if (calleeCounterexamples.ContainsKey(loc)) + if (CalleeCounterexamples.ContainsKey(loc)) { var cmd = GetTraceCmd(loc); var calleeName = GetCalledProcName(cmd); if (calleeName.StartsWith(VC.StratifiedVerificationConditionGeneratorBase.recordProcName) && - options.StratifiedInlining > 0) + Options.StratifiedInlining > 0) { - Contract.Assert(calleeCounterexamples[loc].args.Count == 1); - var arg = calleeCounterexamples[loc].args[0]; + Contract.Assert(CalleeCounterexamples[loc].Args.Count == 1); + var arg = CalleeCounterexamples[loc].Args[0]; tw.WriteLine("{0}value = {1}", ind, arg.ToString()); } else { tw.WriteLine("{1}Inlined call to procedure {0} begins", calleeName, ind); - calleeCounterexamples[loc].counterexample.Print(indent + 4, tw); + CalleeCounterexamples[loc].Counterexample.Print(indent + 4, tw); tw.WriteLine("{1}Inlined call to procedure {0} ends", calleeName, ind); } } @@ -219,8 +167,8 @@ public void PrintModel(TextWriter tw, Counterexample counterexample) { Contract.Requires(counterexample != null); - var filenameTemplate = options.ModelViewFile; - if (Model == null || filenameTemplate == null || options.StratifiedInlining > 0) + var filenameTemplate = Options.ModelViewFile; + if (Model == null || filenameTemplate == null || Options.StratifiedInlining > 0) { return; } @@ -478,296 +426,4 @@ public ErrorInformation CreateErrorInformation(VcOutcome vcOutcome, bool forceBp return errorInfo; } } - - public class CounterexampleComparer : IComparer, IEqualityComparer - { - private int Compare(List bs1, List bs2) - { - if (bs1.Count < bs2.Count) - { - return -1; - } - else if (bs2.Count < bs1.Count) - { - return 1; - } - - for (int i = 0; i < bs1.Count; i++) - { - var b1 = bs1[i]; - var b2 = bs2[i]; - if (b1.tok.pos < b2.tok.pos) - { - return -1; - } - else if (b2.tok.pos < b1.tok.pos) - { - return 1; - } - } - - return 0; - } - - public int Compare(Counterexample c1, Counterexample c2) - { - //Contract.Requires(c1 != null); - //Contract.Requires(c2 != null); - if (c1.GetLocation() == c2.GetLocation()) - { - var c = Compare(c1.Trace, c2.Trace); - if (c != 0) - { - return c; - } - - // TODO(wuestholz): Generalize this to compare all Descriptions of the counterexample. - var a1 = c1 as AssertCounterexample; - var a2 = c2 as AssertCounterexample; - if (a1 != null && a2 != null) { - var s1 = a1.FailingAssert.Description?.FailureDescription; - var s2 = a2.FailingAssert.Description?.FailureDescription; - if (s1 != null && s2 != null) - { - return s1.CompareTo(s2); - } - } - - return 0; - } - - if (c1.GetLocation() > c2.GetLocation()) - { - return 1; - } - - return -1; - } - - public bool Equals(Counterexample x, Counterexample y) - { - return Compare(x, y) == 0; - } - - public int GetHashCode(Counterexample obj) - { - return 0; - } - } - - public class AssertCounterexample : Counterexample - { - [Peer] public AssertCmd FailingAssert; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(FailingAssert != null); - } - - - public AssertCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertCmd failingAssert, Model model, VC.ModelViewInfo mvInfo, - ProverContext context, ProofRun proofRun) - : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun) - { - Contract.Requires(trace != null); - Contract.Requires(failingAssert != null); - Contract.Requires(context != null); - this.FailingAssert = failingAssert; - } - - protected override Cmd ModelFailingCommand => FailingAssert; - - public override int GetLocation() - { - return FailingAssert.tok.line * 1000 + FailingAssert.tok.col; - } - - public override byte[] Checksum - { - get { return FailingAssert.Checksum; } - } - - public override Counterexample Clone() - { - var ret = new AssertCounterexample(options, Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context, ProofRun); - ret.calleeCounterexamples = calleeCounterexamples; - return ret; - } - } - - public class CallCounterexample : Counterexample - { - public CallCmd FailingCall; - public Requires FailingRequires; - public AssertRequiresCmd FailingAssert; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(FailingCall != null); - Contract.Invariant(FailingRequires != null); - } - - - public CallCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertRequiresCmd assertRequiresCmd, Model model, - VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, byte[] checksum = null) - : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun) - { - var failingRequires = assertRequiresCmd.Requires; - var failingCall = assertRequiresCmd.Call; - Contract.Requires(!failingRequires.Free); - Contract.Requires(trace != null); - Contract.Requires(context != null); - Contract.Requires(failingCall != null); - Contract.Requires(failingRequires != null); - this.FailingCall = failingCall; - this.FailingRequires = failingRequires; - this.FailingAssert = assertRequiresCmd; - this.checksum = checksum; - this.SugaredCmdChecksum = failingCall.Checksum; - } - - protected override Cmd ModelFailingCommand => FailingCall; - - public override int GetLocation() - { - return FailingCall.tok.line * 1000 + FailingCall.tok.col; - } - - byte[] checksum; - - public override byte[] Checksum - { - get { return checksum; } - } - - public override Counterexample Clone() - { - var ret = new CallCounterexample(options, Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context, ProofRun, Checksum); - ret.calleeCounterexamples = calleeCounterexamples; - return ret; - } - } - - public class ReturnCounterexample : Counterexample - { - public TransferCmd FailingReturn; - public Ensures FailingEnsures; - public AssertEnsuresCmd FailingAssert; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(FailingEnsures != null); - Contract.Invariant(FailingReturn != null); - } - - - public ReturnCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertEnsuresCmd assertEnsuresCmd, TransferCmd failingReturn, Model model, - VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, byte[] checksum) - : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun) - { - var failingEnsures = assertEnsuresCmd.Ensures; - Contract.Requires(trace != null); - Contract.Requires(context != null); - Contract.Requires(failingReturn != null); - Contract.Requires(failingEnsures != null); - Contract.Requires(!failingEnsures.Free); - this.FailingReturn = failingReturn; - this.FailingEnsures = failingEnsures; - this.FailingAssert = assertEnsuresCmd; - this.checksum = checksum; - } - - protected override Cmd ModelFailingCommand => null; - - public override int GetLocation() - { - return FailingReturn.tok.line * 1000 + FailingReturn.tok.col; - } - - byte[] checksum; - - /// - /// Returns the checksum of the corresponding assertion. - /// - public override byte[] Checksum - { - get { return checksum; } - } - - public override Counterexample Clone() - { - var ret = new ReturnCounterexample(options, Trace, AugmentedTrace, FailingAssert, FailingReturn, Model, MvInfo, Context, ProofRun, checksum); - ret.calleeCounterexamples = calleeCounterexamples; - return ret; - } - } - - public class VerifierCallback - { - private CoreOptions.ProverWarnings printProverWarnings; - - public VerifierCallback(CoreOptions.ProverWarnings printProverWarnings) - { - this.printProverWarnings = printProverWarnings; - } - - // reason == null means this is genuine counterexample returned by the prover - // other reason means it's time out/memory out/crash - public virtual void OnCounterexample(Counterexample ce, string /*?*/ reason) - { - Contract.Requires(ce != null); - } - - // called in case resource is exceeded and we don't have counterexample - public virtual void OnTimeout(string reason) - { - Contract.Requires(reason != null); - } - - public virtual void OnOutOfMemory(string reason) - { - Contract.Requires(reason != null); - } - - public virtual void OnOutOfResource(string reason) - { - Contract.Requires(reason != null); - } - - public delegate void ProgressDelegate(string phase, int step, int totalSteps, double progressEstimate); - - public virtual ProgressDelegate OnProgress => null; - - public virtual void OnUnreachableCode(ImplementationRun run) - { - Contract.Requires(run != null); - } - - public virtual void OnWarning(CoreOptions options, string msg) - { - Contract.Requires(msg != null); - switch (printProverWarnings) - { - case CoreOptions.ProverWarnings.None: - break; - case CoreOptions.ProverWarnings.Stdout: - options.OutputWriter.WriteLine("Prover warning: " + msg); - break; - case CoreOptions.ProverWarnings.Stderr: - Console.Error.WriteLine("Prover warning: " + msg); - break; - default: - Contract.Assume(false); - throw new cce.UnreachableException(); // unexpected case - } - } - - public virtual void OnVCResult(VerificationRunResult result) - { - Contract.Requires(result != null); - } - } } diff --git a/Source/VCGeneration/Counterexample/CounterexampleComparer.cs b/Source/VCGeneration/Counterexample/CounterexampleComparer.cs new file mode 100644 index 000000000..3e6c23770 --- /dev/null +++ b/Source/VCGeneration/Counterexample/CounterexampleComparer.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; + +namespace Microsoft.Boogie; + +public class CounterexampleComparer : IComparer, IEqualityComparer +{ + private int Compare(List bs1, List bs2) + { + if (bs1.Count < bs2.Count) + { + return -1; + } + else if (bs2.Count < bs1.Count) + { + return 1; + } + + for (int i = 0; i < bs1.Count; i++) + { + var b1 = bs1[i]; + var b2 = bs2[i]; + if (b1.tok.pos < b2.tok.pos) + { + return -1; + } + else if (b2.tok.pos < b1.tok.pos) + { + return 1; + } + } + + return 0; + } + + public int Compare(Counterexample c1, Counterexample c2) + { + //Contract.Requires(c1 != null); + //Contract.Requires(c2 != null); + if (c1.GetLocation() == c2.GetLocation()) + { + var c = Compare(c1.Trace, c2.Trace); + if (c != 0) + { + return c; + } + + // TODO(wuestholz): Generalize this to compare all Descriptions of the counterexample. + var a1 = c1 as AssertCounterexample; + var a2 = c2 as AssertCounterexample; + if (a1 != null && a2 != null) { + var s1 = a1.FailingAssert.Description?.FailureDescription; + var s2 = a2.FailingAssert.Description?.FailureDescription; + if (s1 != null && s2 != null) + { + return s1.CompareTo(s2); + } + } + + return 0; + } + + if (c1.GetLocation() > c2.GetLocation()) + { + return 1; + } + + return -1; + } + + public bool Equals(Counterexample x, Counterexample y) + { + return Compare(x, y) == 0; + } + + public int GetHashCode(Counterexample obj) + { + return 0; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Counterexample/ReturnCounterexample.cs b/Source/VCGeneration/Counterexample/ReturnCounterexample.cs new file mode 100644 index 000000000..30057c4bf --- /dev/null +++ b/Source/VCGeneration/Counterexample/ReturnCounterexample.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using VC; + +namespace Microsoft.Boogie; + +public class ReturnCounterexample : Counterexample +{ + public TransferCmd FailingReturn; + public readonly Ensures FailingEnsures; + public readonly AssertEnsuresCmd FailingAssert; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(FailingEnsures != null); + Contract.Invariant(FailingReturn != null); + } + + + public ReturnCounterexample(VCGenOptions options, List trace, List augmentedTrace, + AssertEnsuresCmd assertEnsuresCmd, TransferCmd failingReturn, Model model, + VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, byte[] checksum) + : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun, assertEnsuresCmd) + { + var failingEnsures = assertEnsuresCmd.Ensures; + Contract.Requires(trace != null); + Contract.Requires(context != null); + Contract.Requires(failingReturn != null); + Contract.Requires(failingEnsures != null); + Contract.Requires(!failingEnsures.Free); + this.FailingReturn = failingReturn; + this.FailingEnsures = failingEnsures; + this.FailingAssert = assertEnsuresCmd; + this.checksum = checksum; + } + + protected override Cmd ModelFailingCommand => null; + + public override int GetLocation() + { + return FailingReturn.tok.line * 1000 + FailingReturn.tok.col; + } + + byte[] checksum; + + /// + /// Returns the checksum of the corresponding assertion. + /// + public override byte[] Checksum + { + get { return checksum; } + } + + public override Counterexample Clone() + { + var ret = new ReturnCounterexample(Options, Trace, AugmentedTrace, FailingAssert, FailingReturn, Model, MvInfo, Context, ProofRun, checksum); + ret.CalleeCounterexamples = CalleeCounterexamples; + return ret; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Counterexample/TraceLocation.cs b/Source/VCGeneration/Counterexample/TraceLocation.cs new file mode 100644 index 000000000..96b3412ca --- /dev/null +++ b/Source/VCGeneration/Counterexample/TraceLocation.cs @@ -0,0 +1,36 @@ +using System; + +namespace Microsoft.Boogie; + +public class TraceLocation : IEquatable +{ + public int numBlock; + public int numInstr; + + public TraceLocation(int numBlock, int numInstr) + { + this.numBlock = numBlock; + this.numInstr = numInstr; + } + + public override bool Equals(object obj) + { + TraceLocation that = obj as TraceLocation; + if (that == null) + { + return false; + } + + return (numBlock == that.numBlock && numInstr == that.numInstr); + } + + public bool Equals(TraceLocation that) + { + return (numBlock == that.numBlock && numInstr == that.numInstr); + } + + public override int GetHashCode() + { + return numBlock.GetHashCode() ^ 131 * numInstr.GetHashCode(); + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Counterexample/VerifierCallback.cs b/Source/VCGeneration/Counterexample/VerifierCallback.cs new file mode 100644 index 000000000..032ed1de4 --- /dev/null +++ b/Source/VCGeneration/Counterexample/VerifierCallback.cs @@ -0,0 +1,71 @@ +using System; +using System.Diagnostics.Contracts; +using VC; + +namespace Microsoft.Boogie; + +public class VerifierCallback +{ + private CoreOptions.ProverWarnings printProverWarnings; + + public VerifierCallback(CoreOptions.ProverWarnings printProverWarnings) + { + this.printProverWarnings = printProverWarnings; + } + + // reason == null means this is genuine counterexample returned by the prover + // other reason means it's time out/memory out/crash + public virtual void OnCounterexample(Counterexample ce, string /*?*/ reason) + { + Contract.Requires(ce != null); + } + + // called in case resource is exceeded and we don't have counterexample + public virtual void OnTimeout(string reason) + { + Contract.Requires(reason != null); + } + + public virtual void OnOutOfMemory(string reason) + { + Contract.Requires(reason != null); + } + + public virtual void OnOutOfResource(string reason) + { + Contract.Requires(reason != null); + } + + public delegate void ProgressDelegate(string phase, int step, int totalSteps, double progressEstimate); + + public virtual ProgressDelegate OnProgress => null; + + public virtual void OnUnreachableCode(ImplementationRun run) + { + Contract.Requires(run != null); + } + + public virtual void OnWarning(CoreOptions options, string msg) + { + Contract.Requires(msg != null); + switch (printProverWarnings) + { + case CoreOptions.ProverWarnings.None: + break; + case CoreOptions.ProverWarnings.Stdout: + options.OutputWriter.WriteLine("Prover warning: " + msg); + break; + case CoreOptions.ProverWarnings.Stderr: + Console.Error.WriteLine("Prover warning: " + msg); + break; + default: + Contract.Assume(false); + throw new cce.UnreachableException(); // unexpected case + } + } + + public virtual void OnVCResult(VerificationRunResult result) + { + Contract.Requires(result != null); + } +} \ No newline at end of file diff --git a/Source/VCGeneration/StratifiedVC.cs b/Source/VCGeneration/StratifiedVC.cs index 19cfe5426..5419e56be 100644 --- a/Source/VCGeneration/StratifiedVC.cs +++ b/Source/VCGeneration/StratifiedVC.cs @@ -964,7 +964,7 @@ public override Counterexample ExtractLoopTrace(Counterexample cex, string mainP return ExtractLoopTraceRec( new CalleeCounterexampleInfo(cex, new List()), - mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; + mainProcName, inlinedProcs, extractLoopMappingInfo).Counterexample; } protected override bool ProcIsLoop(string procname) diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index 378e0bd0d..06602cff0 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -1740,7 +1740,7 @@ public virtual Counterexample ExtractLoopTrace(Counterexample cex, string mainPr return ExtractLoopTraceRec( new CalleeCounterexampleInfo(cex, new List()), - mainProcName, inlinedProcs, extractLoopMappingInfo).counterexample; + mainProcName, inlinedProcs, extractLoopMappingInfo).Counterexample; } protected CalleeCounterexampleInfo ExtractLoopTraceRec( @@ -1749,16 +1749,16 @@ protected CalleeCounterexampleInfo ExtractLoopTraceRec( Dictionary> extractLoopMappingInfo) { Contract.Requires(currProc != null); - if (cexInfo.counterexample == null) + if (cexInfo.Counterexample == null) { return cexInfo; } - var cex = cexInfo.counterexample; + var cex = cexInfo.Counterexample; // Go through all blocks in the trace, map them back to blocks in the original program (if there is one) var ret = cex.Clone(); ret.Trace = new List(); - ret.calleeCounterexamples = new Dictionary(); + ret.CalleeCounterexamples = new Dictionary(); for (int numBlock = 0; numBlock < cex.Trace.Count; numBlock++) { @@ -1774,7 +1774,7 @@ protected CalleeCounterexampleInfo ExtractLoopTraceRec( { Cmd cmd = block.Cmds[numInstr]; var loc = new TraceLocation(numBlock, numInstr); - if (!cex.calleeCounterexamples.ContainsKey(loc)) + if (!cex.CalleeCounterexamples.ContainsKey(loc)) { if (GetCallee(cex.GetTraceCmd(loc), inlinedProcs) != null) { @@ -1786,7 +1786,7 @@ protected CalleeCounterexampleInfo ExtractLoopTraceRec( string callee = cex.GetCalledProcName(cex.GetTraceCmd(loc)); Contract.Assert(callee != null); - var calleeTrace = cex.calleeCounterexamples[loc]; + var calleeTrace = cex.CalleeCounterexamples[loc]; Debug.Assert(calleeTrace != null); var origTrace = ExtractLoopTraceRec(calleeTrace, callee, inlinedProcs, extractLoopMappingInfo); @@ -1796,25 +1796,25 @@ protected CalleeCounterexampleInfo ExtractLoopTraceRec( // Absorb the trace into the current trace int currLen = ret.Trace.Count; - ret.Trace.AddRange(origTrace.counterexample.Trace); + ret.Trace.AddRange(origTrace.Counterexample.Trace); - foreach (var kvp in origTrace.counterexample.calleeCounterexamples) + foreach (var kvp in origTrace.Counterexample.CalleeCounterexamples) { var newloc = new TraceLocation(kvp.Key.numBlock + currLen, kvp.Key.numInstr); - ret.calleeCounterexamples.Add(newloc, kvp.Value); + ret.CalleeCounterexamples.Add(newloc, kvp.Value); } } else { var origLoc = new TraceLocation(ret.Trace.Count - 1, GetCallCmdPosition(origBlock, callCnt, inlinedProcs, callee)); - ret.calleeCounterexamples.Add(origLoc, origTrace); + ret.CalleeCounterexamples.Add(origLoc, origTrace); callCnt++; } } } - return new CalleeCounterexampleInfo(ret, cexInfo.args); + return new CalleeCounterexampleInfo(ret, cexInfo.Args); } // return the position of the i^th CallCmd in the block (count only those Calls that call a procedure in inlinedProcs). @@ -2014,23 +2014,20 @@ public static Counterexample AssertCmdToCloneCounterexample(VCGenOptions options Contract.Ensures(Contract.Result() != null); Counterexample cc; - if (assrt is AssertRequiresCmd) + if (assrt is AssertRequiresCmd assertRequiresCmd) { - var aa = (AssertRequiresCmd)assrt; - cc = new CallCounterexample(options, cex.Trace, cex.AugmentedTrace, aa, cex.Model, cex.MvInfo, cex.Context, - cex.ProofRun, aa.Checksum); + cc = new CallCounterexample(options, cex.Trace, cex.AugmentedTrace, assertRequiresCmd, cex.Model, cex.MvInfo, cex.Context, + cex.ProofRun, assertRequiresCmd.Checksum); } - else if (assrt is AssertEnsuresCmd && cex is ReturnCounterexample) + else if (assrt is AssertEnsuresCmd assertEnsuresCmd && cex is ReturnCounterexample returnCounterexample) { - var aa = (AssertEnsuresCmd)assrt; - var oldCex = (ReturnCounterexample)cex; // The first three parameters of ReturnCounterexample are: List trace, List augmentedTrace, TransferCmd failingReturn, Ensures failingEnsures. // We have the "aa" version of failingEnsures, namely aa.Ensures. The first and third parameters take more work to reconstruct. // (The code here assumes the labels of blocks remain the same. If that's not the case, then it is possible that the trace // computed does not lead to where the error is, but at least the error will not be masked.) List reconstructedTrace = null; Block prevBlock = null; - foreach (var blk in cex.Trace) + foreach (var blk in returnCounterexample.Trace) { if (reconstructedTrace == null) { @@ -2100,9 +2097,9 @@ public static Counterexample AssertCmdToCloneCounterexample(VCGenOptions options } } - cc = new ReturnCounterexample(options, reconstructedTrace ?? cex.Trace, cex.AugmentedTrace, aa, - returnCmd ?? oldCex.FailingReturn, - cex.Model, cex.MvInfo, cex.Context, cex.ProofRun, aa.Checksum); + cc = new ReturnCounterexample(options, reconstructedTrace ?? returnCounterexample.Trace, returnCounterexample.AugmentedTrace, assertEnsuresCmd, + returnCmd ?? returnCounterexample.FailingReturn, + returnCounterexample.Model, returnCounterexample.MvInfo, returnCounterexample.Context, returnCounterexample.ProofRun, assertEnsuresCmd.Checksum); } else { diff --git a/Source/VCGeneration/VerificationResultCollector.cs b/Source/VCGeneration/VerificationResultCollector.cs index 6957914ad..3d92b8ee0 100644 --- a/Source/VCGeneration/VerificationResultCollector.cs +++ b/Source/VCGeneration/VerificationResultCollector.cs @@ -16,18 +16,18 @@ public VerificationResultCollector(VCGenOptions options) : base(options.PrintPro [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(cce.NonNullElements(examples)); - Contract.Invariant(cce.NonNullElements(vcResults)); + Contract.Invariant(cce.NonNullElements(Examples)); + Contract.Invariant(cce.NonNullElements(VcResults)); } - public readonly ConcurrentQueue examples = new(); - public readonly ConcurrentQueue vcResults = new(); + public readonly ConcurrentQueue Examples = new(); + public readonly ConcurrentQueue VcResults = new(); public override void OnCounterexample(Counterexample ce, string /*?*/ reason) { //Contract.Requires(ce != null); ce.InitializeModelStates(); - examples.Enqueue(ce); + Examples.Enqueue(ce); } public override void OnUnreachableCode(ImplementationRun run) @@ -40,6 +40,6 @@ public override void OnUnreachableCode(ImplementationRun run) public override void OnVCResult(VerificationRunResult result) { - vcResults.Enqueue(result); + VcResults.Enqueue(result); } } \ No newline at end of file From 0465e6eeba30cf4488e43cafee3b718025475df0 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 15:41:26 +0200 Subject: [PATCH 03/70] Finish test and some more refactoring --- Source/ExecutionEngine/ExecutionEngine.cs | 2 +- .../Counterexample/AssertCounterexample.cs | 9 +-------- .../Counterexample/CallCounterexample.cs | 12 +++++------- .../Counterexample/Counterexample.cs | 6 +++--- .../Counterexample/ReturnCounterexample.cs | 17 ++++++----------- Source/VCGeneration/VerificationRunResult.cs | 14 +++----------- Test/pruning/isolatePaths.bpl | 4 +++- Test/pruning/isolatePaths.bpl.expect | 3 +++ Test/pruning/isolatePaths.bpl.split0.expect | 10 ++++++++++ Test/pruning/isolatePaths.bpl.split1.expect | 10 ++++++++++ 10 files changed, 45 insertions(+), 42 deletions(-) create mode 100644 Test/pruning/isolatePaths.bpl.split0.expect create mode 100644 Test/pruning/isolatePaths.bpl.split1.expect diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index cde400b3e..be6dd71a4 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1310,7 +1310,7 @@ public void ProcessErrors(OutputPrinter printer, return; } - errors = errors.DistinctBy(e => e.AssertCmd).ToList(); + errors = errors.DistinctBy(e => e.FailingAssert).ToList(); errors.Sort(new CounterexampleComparer()); foreach (Counterexample error in errors) { diff --git a/Source/VCGeneration/Counterexample/AssertCounterexample.cs b/Source/VCGeneration/Counterexample/AssertCounterexample.cs index 3959b75da..522b55cd0 100644 --- a/Source/VCGeneration/Counterexample/AssertCounterexample.cs +++ b/Source/VCGeneration/Counterexample/AssertCounterexample.cs @@ -6,12 +6,9 @@ namespace Microsoft.Boogie; public class AssertCounterexample : Counterexample { - [Peer] public readonly AssertCmd FailingAssert; - [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(FailingAssert != null); } @@ -22,7 +19,6 @@ public AssertCounterexample(VCGenOptions options, List trace, List FailingAssert; @@ -32,10 +28,7 @@ public override int GetLocation() return FailingAssert.tok.line * 1000 + FailingAssert.tok.col; } - public override byte[] Checksum - { - get { return FailingAssert.Checksum; } - } + public override byte[] Checksum => FailingAssert.Checksum; public override Counterexample Clone() { diff --git a/Source/VCGeneration/Counterexample/CallCounterexample.cs b/Source/VCGeneration/Counterexample/CallCounterexample.cs index c404cac65..199df0c15 100644 --- a/Source/VCGeneration/Counterexample/CallCounterexample.cs +++ b/Source/VCGeneration/Counterexample/CallCounterexample.cs @@ -8,7 +8,6 @@ public class CallCounterexample : Counterexample { public readonly CallCmd FailingCall; public readonly Requires FailingRequires; - public readonly AssertRequiresCmd FailingAssert; [ContractInvariantMethod] void ObjectInvariant() @@ -18,12 +17,12 @@ void ObjectInvariant() } - public CallCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertRequiresCmd assertRequiresCmd, Model model, + public CallCounterexample(VCGenOptions options, List trace, List augmentedTrace, AssertRequiresCmd failingAssertRequires, Model model, VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, byte[] checksum = null) - : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun, assertRequiresCmd) + : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun, failingAssertRequires) { - var failingRequires = assertRequiresCmd.Requires; - var failingCall = assertRequiresCmd.Call; + var failingRequires = failingAssertRequires.Requires; + var failingCall = failingAssertRequires.Call; Contract.Requires(!failingRequires.Free); Contract.Requires(trace != null); Contract.Requires(context != null); @@ -31,7 +30,6 @@ public CallCounterexample(VCGenOptions options, List trace, List Contract.Requires(failingRequires != null); this.FailingCall = failingCall; this.FailingRequires = failingRequires; - this.FailingAssert = assertRequiresCmd; this.checksum = checksum; this.SugaredCmdChecksum = failingCall.Checksum; } @@ -52,7 +50,7 @@ public override byte[] Checksum public override Counterexample Clone() { - var ret = new CallCounterexample(Options, Trace, AugmentedTrace, FailingAssert, Model, MvInfo, Context, ProofRun, Checksum); + var ret = new CallCounterexample(Options, Trace, AugmentedTrace, (AssertRequiresCmd)FailingAssert, Model, MvInfo, Context, ProofRun, Checksum); ret.CalleeCounterexamples = CalleeCounterexamples; return ret; } diff --git a/Source/VCGeneration/Counterexample/Counterexample.cs b/Source/VCGeneration/Counterexample/Counterexample.cs index f22def9ad..f49abf15c 100644 --- a/Source/VCGeneration/Counterexample/Counterexample.cs +++ b/Source/VCGeneration/Counterexample/Counterexample.cs @@ -25,7 +25,7 @@ void ObjectInvariant() protected readonly VCGenOptions Options; [Peer] public List Trace; public readonly List AugmentedTrace; - public AssertCmd AssertCmd { get; } + public AssertCmd FailingAssert { get; } public Model Model { get; } public readonly ModelViewInfo MvInfo; public readonly ProverContext Context; @@ -36,7 +36,7 @@ void ObjectInvariant() public Dictionary CalleeCounterexamples; internal Counterexample(VCGenOptions options, List trace, List augmentedTrace, Model model, - VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, AssertCmd assertCmd) + VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, AssertCmd failingAssert) { Contract.Requires(trace != null); Contract.Requires(context != null); @@ -46,7 +46,7 @@ internal Counterexample(VCGenOptions options, List trace, List au this.MvInfo = mvInfo; this.Context = context; this.ProofRun = proofRun; - this.AssertCmd = assertCmd; + this.FailingAssert = failingAssert; this.CalleeCounterexamples = new Dictionary(); // the call to instance method GetModelValue in the following code requires the fields Model and Context to be initialized if (augmentedTrace != null) diff --git a/Source/VCGeneration/Counterexample/ReturnCounterexample.cs b/Source/VCGeneration/Counterexample/ReturnCounterexample.cs index 30057c4bf..98092c946 100644 --- a/Source/VCGeneration/Counterexample/ReturnCounterexample.cs +++ b/Source/VCGeneration/Counterexample/ReturnCounterexample.cs @@ -8,7 +8,6 @@ public class ReturnCounterexample : Counterexample { public TransferCmd FailingReturn; public readonly Ensures FailingEnsures; - public readonly AssertEnsuresCmd FailingAssert; [ContractInvariantMethod] void ObjectInvariant() @@ -19,11 +18,11 @@ void ObjectInvariant() public ReturnCounterexample(VCGenOptions options, List trace, List augmentedTrace, - AssertEnsuresCmd assertEnsuresCmd, TransferCmd failingReturn, Model model, + AssertEnsuresCmd failingAssertEnsures, TransferCmd failingReturn, Model model, VC.ModelViewInfo mvInfo, ProverContext context, ProofRun proofRun, byte[] checksum) - : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun, assertEnsuresCmd) + : base(options, trace, augmentedTrace, model, mvInfo, context, proofRun, failingAssertEnsures) { - var failingEnsures = assertEnsuresCmd.Ensures; + var failingEnsures = failingAssertEnsures.Ensures; Contract.Requires(trace != null); Contract.Requires(context != null); Contract.Requires(failingReturn != null); @@ -31,7 +30,6 @@ public ReturnCounterexample(VCGenOptions options, List trace, List /// Returns the checksum of the corresponding assertion. /// - public override byte[] Checksum - { - get { return checksum; } - } + public override byte[] Checksum => checksum; public override Counterexample Clone() { - var ret = new ReturnCounterexample(Options, Trace, AugmentedTrace, FailingAssert, FailingReturn, Model, MvInfo, Context, ProofRun, checksum); + var ret = new ReturnCounterexample(Options, Trace, AugmentedTrace, (AssertEnsuresCmd)FailingAssert, FailingReturn, Model, MvInfo, Context, ProofRun, checksum); ret.CalleeCounterexamples = CalleeCounterexamples; return ret; } diff --git a/Source/VCGeneration/VerificationRunResult.cs b/Source/VCGeneration/VerificationRunResult.cs index b2566f93e..0ddcd0baa 100644 --- a/Source/VCGeneration/VerificationRunResult.cs +++ b/Source/VCGeneration/VerificationRunResult.cs @@ -27,17 +27,9 @@ public void ComputePerAssertOutcomes(out Dictionary pe if (Outcome == SolverOutcome.Valid) { perAssertOutcome = Asserts.ToDictionary(cmd => cmd, _ => SolverOutcome.Valid); } else { - foreach (var counterExample in CounterExamples) { - AssertCmd underlyingAssert; - if (counterExample is AssertCounterexample assertCounterexample) { - underlyingAssert = assertCounterexample.FailingAssert; - } else if (counterExample is CallCounterexample callCounterexample) { - underlyingAssert = callCounterexample.FailingAssert; - } else if (counterExample is ReturnCounterexample returnCounterexample) { - underlyingAssert = returnCounterexample.FailingAssert; - } else { - continue; - } + foreach (var counterExample in CounterExamples) + { + var underlyingAssert = counterExample.FailingAssert; // We ensure that the underlyingAssert is among the original asserts if (!Asserts.Contains(underlyingAssert)) { diff --git a/Test/pruning/isolatePaths.bpl b/Test/pruning/isolatePaths.bpl index 6a168f523..476dc300d 100644 --- a/Test/pruning/isolatePaths.bpl +++ b/Test/pruning/isolatePaths.bpl @@ -1,5 +1,7 @@ -// RUN: %parallel-boogie "%s" > "%t" +// RUN: %parallel-boogie "%s" /printSplit:%t /errorTrace:0 > "%t" // RUN: %diff "%s.expect" "%t" +// RUN: %diff %S/isolatePaths.bpl.split0.expect %t-NoDuplicateErrors-0.spl +// RUN: %diff %S/isolatePaths.bpl.split1.expect %t-NoDuplicateErrors-1.spl procedure {:isolate_paths} NoDuplicateErrors() { diff --git a/Test/pruning/isolatePaths.bpl.expect b/Test/pruning/isolatePaths.bpl.expect index e69de29bb..db5930dce 100644 --- a/Test/pruning/isolatePaths.bpl.expect +++ b/Test/pruning/isolatePaths.bpl.expect @@ -0,0 +1,3 @@ +isolatePaths.bpl(15,3): Error: this assertion could not be proved + +Boogie program verifier finished with 0 verified, 2 errors diff --git a/Test/pruning/isolatePaths.bpl.split0.expect b/Test/pruning/isolatePaths.bpl.split0.expect new file mode 100644 index 000000000..2bb28d5b0 --- /dev/null +++ b/Test/pruning/isolatePaths.bpl.split0.expect @@ -0,0 +1,10 @@ +implementation {:isolate_paths} NoDuplicateErrors() +{ + + anon0: + assume x#AT#0 == 2; + assert x#AT#0 == 1; + return; +} + + diff --git a/Test/pruning/isolatePaths.bpl.split1.expect b/Test/pruning/isolatePaths.bpl.split1.expect new file mode 100644 index 000000000..56342ebf8 --- /dev/null +++ b/Test/pruning/isolatePaths.bpl.split1.expect @@ -0,0 +1,10 @@ +implementation {:isolate_paths} NoDuplicateErrors() +{ + + anon0: + assume x#AT#0 == 3; + assert x#AT#0 == 1; + return; +} + + From b451f9f00696fa0a0862a989ae5418a1b22fdd79 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 15:42:39 +0200 Subject: [PATCH 04/70] Refactoring --- Source/ExecutionEngine/ExecutionEngine.cs | 2 +- Source/VCGeneration/FocusAttribute.cs | 2 +- Source/VCGeneration/ManualSplitFinder.cs | 26 ++++----------------- Source/VCGeneration/SplitAndVerifyWorker.cs | 2 +- 4 files changed, 7 insertions(+), 25 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index be6dd71a4..a0c5dafb0 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -745,7 +745,7 @@ public async Task> GetVerificationTasks(Program out var modelViewInfo); ConditionGeneration.ResetPredecessors(run.Implementation.Blocks); - var splits = ManualSplitFinder.FocusAndSplit(program, Options, run, gotoCmdOrigins, vcGenerator).ToList(); + var splits = ManualSplitFinder.SplitOnPathsAndAssertions(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/VCGeneration/FocusAttribute.cs b/Source/VCGeneration/FocusAttribute.cs index da5bac811..d61583fc6 100644 --- a/Source/VCGeneration/FocusAttribute.cs +++ b/Source/VCGeneration/FocusAttribute.cs @@ -16,7 +16,7 @@ namespace VCGeneration; public static class FocusAttribute { - public static List FocusImpl(VCGenOptions options, ImplementationRun run, Dictionary gotoCmdOrigins, VerificationConditionGenerator par) + public static List SplitOnFocus(VCGenOptions options, ImplementationRun run, Dictionary gotoCmdOrigins, VerificationConditionGenerator par) { var impl = run.Implementation; var dag = Program.GraphFromImpl(impl); diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index fbb65fe6f..a9fa52a1b 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -9,12 +9,12 @@ namespace VCGeneration; public static class ManualSplitFinder { - public static IEnumerable FocusAndSplit(Program program, VCGenOptions options, ImplementationRun run, + public static IEnumerable SplitOnPathsAndAssertions(Program program, VCGenOptions options, ImplementationRun run, Dictionary gotoCmdOrigins, VerificationConditionGenerator par) { var paths = options.IsolatePaths || QKeyValue.FindBoolAttribute(run.Implementation.Attributes, "isolate_paths") ? PathSplits(options, run, gotoCmdOrigins, par) - : FocusAttribute.FocusImpl(options, run, gotoCmdOrigins, par); - return paths.SelectMany(s => FindManualSplits(program, s)); + : FocusAttribute.SplitOnFocus(options, run, gotoCmdOrigins, par); + return paths.SelectMany(s => SplitOnAssertions(program, s)); } private static List PathSplits(VCGenOptions options, ImplementationRun run, @@ -52,7 +52,7 @@ private static List PathSplits(VCGenOptions options, Implementation return result; } - private static List FindManualSplits(Program program, ManualSplit initialSplit) { + private static List SplitOnAssertions(Program program, ManualSplit initialSplit) { var splitOnEveryAssert = initialSplit.Options.VcsSplitOnEveryAssert; initialSplit.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); @@ -138,24 +138,6 @@ private static Dictionary PickBlocksToVerify(List blocks, D } return blockAssignments; } - - 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(oldBlock.tok) { - Label = oldBlock.Label, - 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, diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index a42723458..40818c3ef 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -66,7 +66,7 @@ public SplitAndVerifyWorker( ResetPredecessors(Implementation.Blocks); - ManualSplits = ManualSplitFinder.FocusAndSplit(program, options, run, gotoCmdOrigins, verificationConditionGenerator).ToList(); + ManualSplits = ManualSplitFinder.SplitOnPathsAndAssertions(program, options, run, gotoCmdOrigins, verificationConditionGenerator).ToList(); if (ManualSplits.Count == 1 && maxSplits > 1) { From b016f40f2ddd503cbba62421ba55e828cce20e24 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 15:49:02 +0200 Subject: [PATCH 05/70] Refactoring --- Source/ExecutionEngine/ExecutionEngine.cs | 4 +-- Source/VCGeneration/FocusAttribute.cs | 21 ++++++-------- Source/VCGeneration/ManualSplitFinder.cs | 32 ++++++++++----------- Source/VCGeneration/SplitAndVerifyWorker.cs | 3 +- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index a0c5dafb0..327304aff 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -736,7 +736,6 @@ public async Task> GetVerificationTasks(Program { var writer = TextWriter.Null; var vcGenerator = new VerificationConditionGenerator(processedProgram.Program, CheckerPool); - var run = new ImplementationRun(implementation, writer); var collector = new VerificationResultCollector(Options); @@ -745,7 +744,8 @@ public async Task> GetVerificationTasks(Program out var modelViewInfo); ConditionGeneration.ResetPredecessors(run.Implementation.Blocks); - var splits = ManualSplitFinder.SplitOnPathsAndAssertions(program, Options, run, gotoCmdOrigins, vcGenerator).ToList(); + var splits = ManualSplitFinder.SplitOnPathsAndAssertions(Options, run, + (token, blocks) => new ManualSplit(Options, () => blocks, gotoCmdOrigins, vcGenerator, run, token)).ToList(); for (var index = 0; index < splits.Count; index++) { var split = splits[index]; split.SplitIndex = index; diff --git a/Source/VCGeneration/FocusAttribute.cs b/Source/VCGeneration/FocusAttribute.cs index d61583fc6..246b6192e 100644 --- a/Source/VCGeneration/FocusAttribute.cs +++ b/Source/VCGeneration/FocusAttribute.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; @@ -7,8 +8,6 @@ using Cmd = Microsoft.Boogie.Cmd; using PredicateCmd = Microsoft.Boogie.PredicateCmd; using QKeyValue = Microsoft.Boogie.QKeyValue; -using ReturnCmd = Microsoft.Boogie.ReturnCmd; -using TransferCmd = Microsoft.Boogie.TransferCmd; using VCGenOptions = Microsoft.Boogie.VCGenOptions; namespace VCGeneration; @@ -16,7 +15,8 @@ namespace VCGeneration; public static class FocusAttribute { - public static List SplitOnFocus(VCGenOptions options, ImplementationRun run, Dictionary gotoCmdOrigins, VerificationConditionGenerator par) + public static List SplitOnFocus(VCGenOptions options, ImplementationRun run, + Func, ManualSplit> createSplit) { var impl = run.Implementation; var dag = Program.GraphFromImpl(impl); @@ -24,17 +24,17 @@ public static List SplitOnFocus(VCGenOptions options, Implementatio // By default, we process the foci in a top-down fashion, i.e., in the topological order. // If the user sets the RelaxFocus flag, we use the reverse (topological) order. var focusBlocks = GetFocusBlocks(topologicallySortedBlocks); - if (par.CheckerPool.Options.RelaxFocus) { + if (options.RelaxFocus) { focusBlocks.Reverse(); } if (!focusBlocks.Any()) { - return new List { new(options, () => impl.Blocks, gotoCmdOrigins, par, run, run.Implementation.tok) }; + return new List { createSplit(run.Implementation.tok, impl.Blocks) }; } var ancestorsPerBlock = new Dictionary>(); var descendantsPerBlock = new Dictionary>(); focusBlocks.ForEach(fb => ancestorsPerBlock[fb.Block] = dag.ComputeReachability(fb.Block, false).ToHashSet()); - focusBlocks.ForEach(fb => descendantsPerBlock[fb.Block] = dag.ComputeReachability(fb.Block, true).ToHashSet()); + focusBlocks.ForEach(fb => descendantsPerBlock[fb.Block] = dag.ComputeReachability(fb.Block).ToHashSet()); var result = new List(); var duplicator = new Duplicator(); @@ -71,11 +71,8 @@ void FocusRec(IToken focusToken, int focusIndex, IReadOnlyList blocksToIn } newBlocks.Reverse(); Contract.Assert(newBlocks[0] == oldToNewBlockMap[impl.Blocks[0]]); - result.Add(new ManualSplit(options, () => - { - BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); - return newBlocks; - }, gotoCmdOrigins, par, run, focusToken)); + BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); + result.Add(createSplit(focusToken, newBlocks)); } else if (!blocksToInclude.Contains(focusBlocks[focusIndex].Block) || freeAssumeBlocks.Contains(focusBlocks[focusIndex].Block)) { @@ -100,7 +97,7 @@ void FocusRec(IToken focusToken, int focusIndex, IReadOnlyList blocksToIn // finds all the blocks dominated by focusBlock in the subgraph // which only contains vertices of subgraph. - private static HashSet DominatedBlocks(List topologicallySortedBlocks, Block focusBlock, IEnumerable subgraph) + private static HashSet DominatedBlocks(List topologicallySortedBlocks, Block focusBlock, IReadOnlyList subgraph) { var dominators = new Dictionary>(); foreach (var b in topologicallySortedBlocks.Where(subgraph.Contains)) diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index a9fa52a1b..02c553afa 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.Contracts; @@ -9,16 +10,16 @@ namespace VCGeneration; public static class ManualSplitFinder { - public static IEnumerable SplitOnPathsAndAssertions(Program program, VCGenOptions options, ImplementationRun run, - Dictionary gotoCmdOrigins, VerificationConditionGenerator par) { + public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, + Func, ManualSplit> createSplit) { var paths = options.IsolatePaths || QKeyValue.FindBoolAttribute(run.Implementation.Attributes, "isolate_paths") - ? PathSplits(options, run, gotoCmdOrigins, par) - : FocusAttribute.SplitOnFocus(options, run, gotoCmdOrigins, par); - return paths.SelectMany(s => SplitOnAssertions(program, s)); + ? PathSplits(run, createSplit) + : FocusAttribute.SplitOnFocus(options, run, createSplit); + return paths.SelectMany(s => SplitOnAssertions(s)); } - private static List PathSplits(VCGenOptions options, ImplementationRun run, - Dictionary gotoCmdOrigins, VerificationConditionGenerator par) + private static List PathSplits(ImplementationRun run, + Func, ManualSplit> createSplit) { var result = new List(); var firstBlock = run.Implementation.Blocks[0]; @@ -30,16 +31,13 @@ private static List PathSplits(VCGenOptions options, Implementation var last = current.Peek(); if (last.TransferCmd is not GotoCmd gotoCmd) { - result.Add(new ManualSplit(options, () => + var masterBlock = new Block(firstBlock.tok) { - var masterBlock = new Block(firstBlock.tok) - { - Label = firstBlock.Label, - Cmds = current.Reverse().SelectMany(block => block.Cmds).ToList(), - TransferCmd = current.Peek().TransferCmd - }; - return new List { masterBlock }; - }, gotoCmdOrigins, par, run, run.Implementation.tok)); + Label = firstBlock.Label, + Cmds = current.Reverse().SelectMany(block => block.Cmds).ToList(), + TransferCmd = current.Peek().TransferCmd + }; + result.Add(createSplit(run.Implementation.tok, new List { masterBlock })); continue; } @@ -52,7 +50,7 @@ private static List PathSplits(VCGenOptions options, Implementation return result; } - private static List SplitOnAssertions(Program program, ManualSplit initialSplit) { + private static List SplitOnAssertions(ManualSplit initialSplit) { var splitOnEveryAssert = initialSplit.Options.VcsSplitOnEveryAssert; initialSplit.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/SplitAndVerifyWorker.cs index 40818c3ef..f02225df4 100644 --- a/Source/VCGeneration/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/SplitAndVerifyWorker.cs @@ -66,7 +66,8 @@ public SplitAndVerifyWorker( ResetPredecessors(Implementation.Blocks); - ManualSplits = ManualSplitFinder.SplitOnPathsAndAssertions(program, options, run, gotoCmdOrigins, verificationConditionGenerator).ToList(); + ManualSplits = ManualSplitFinder.SplitOnPathsAndAssertions(options, run, + (token, blocks) => new ManualSplit(options, () => blocks, gotoCmdOrigins, verificationConditionGenerator, run, token)).ToList(); if (ManualSplits.Count == 1 && maxSplits > 1) { From 0fa18170c76931cbabac7d7c7fd2db1cf3914f20 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 15:49:13 +0200 Subject: [PATCH 06/70] Update version number --- Source/Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Directory.Build.props b/Source/Directory.Build.props index d81beeac1..d68939fad 100644 --- a/Source/Directory.Build.props +++ b/Source/Directory.Build.props @@ -2,7 +2,7 @@ - 3.2.5 + 3.3.5 net6.0 false Boogie From 4755839c30896907f7a6f18cfbcd0c38a406123d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 16:36:30 +0200 Subject: [PATCH 07/70] Tweaks --- Source/ExecutionEngine/CommandLineOptions.cs | 7 ++++++- Source/VCGeneration/ManualSplitFinder.cs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index c97396c65..63c3adc98 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -975,7 +975,7 @@ protected override bool ParseOption(string name, CommandLineParseState ps) int cb = 0; if (ps.GetIntArgument(x => cb = x, 2)) { - CoalesceBlocks = cb == 1; + IsolatePaths = cb == 1; } return true; @@ -1926,6 +1926,11 @@ verify each input program separately /removeEmptyBlocks: 0 - do not remove empty blocks during VC generation 1 - remove empty blocks (default) + /isolatePaths: + 0 = do not isolate paths + 1 = generate a separate group of VCs for each control flow path through an implementation. + Each group has only a single VC by default, but usage of options and attribute that split VCs based on assertions + can introduce more. /coalesceBlocks: 0 = do not coalesce blocks 1 = coalesce blocks (default) diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index 02c553afa..50f220cab 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -15,7 +15,7 @@ public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions op var paths = options.IsolatePaths || QKeyValue.FindBoolAttribute(run.Implementation.Attributes, "isolate_paths") ? PathSplits(run, createSplit) : FocusAttribute.SplitOnFocus(options, run, createSplit); - return paths.SelectMany(s => SplitOnAssertions(s)); + return paths.SelectMany(SplitOnAssertions); } private static List PathSplits(ImplementationRun run, From 23f44eddd9f7863ae390e7cea623c53a2b4a5a1d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 24 Sep 2024 17:31:22 +0200 Subject: [PATCH 08/70] Improve implementation and add tests --- Source/VCGeneration/ManualSplitFinder.cs | 21 ++++++--- Test/pruning/isolatePaths.bpl | 44 ++++++++++++++++++- ...isolatePaths.bpl.EarlyAssertions--1.expect | 10 +++++ ...ePaths.bpl.EarlyAssertionsVariant-0.expect | 10 +++++ ...ePaths.bpl.EarlyAssertionsVariant-1.expect | 10 +++++ .../isolatePaths.bpl.EmptyPath--1.expect | 9 ++++ ...olatePaths.bpl.NoDuplicateErrors-0.expect} | 0 ...olatePaths.bpl.NoDuplicateErrors-1.expect} | 0 Test/pruning/isolatePaths.bpl.expect | 4 +- 9 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 Test/pruning/isolatePaths.bpl.EarlyAssertions--1.expect create mode 100644 Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-0.expect create mode 100644 Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-1.expect create mode 100644 Test/pruning/isolatePaths.bpl.EmptyPath--1.expect rename Test/pruning/{isolatePaths.bpl.split0.expect => isolatePaths.bpl.NoDuplicateErrors-0.expect} (100%) rename Test/pruning/{isolatePaths.bpl.split1.expect => isolatePaths.bpl.NoDuplicateErrors-1.expect} (100%) diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index 50f220cab..a84ef5927 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -13,12 +13,12 @@ public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { var paths = options.IsolatePaths || QKeyValue.FindBoolAttribute(run.Implementation.Attributes, "isolate_paths") - ? PathSplits(run, createSplit) + ? PathSplits(options, run, createSplit) : FocusAttribute.SplitOnFocus(options, run, createSplit); return paths.SelectMany(SplitOnAssertions); } - private static List PathSplits(ImplementationRun run, + private static List PathSplits(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { var result = new List(); @@ -29,7 +29,7 @@ private static List PathSplits(ImplementationRun run, { var current = paths.Pop(); var last = current.Peek(); - if (last.TransferCmd is not GotoCmd gotoCmd) + if (last.TransferCmd is not GotoCmd gotoCmd || !gotoCmd.labelTargets.Any()) { var masterBlock = new Block(firstBlock.tok) { @@ -41,9 +41,20 @@ private static List PathSplits(ImplementationRun run, continue; } - foreach (var target in gotoCmd.labelTargets) + var firstTarget = gotoCmd.labelTargets.First(); + paths.Push(current.Push(firstTarget)); + + if (gotoCmd.labelTargets.Count <= 1) { + continue; + } + + var assumedBlock = new Block(run.Implementation.tok) { + Cmds = current.Reverse().SelectMany(block => + block.Cmds.Select(command => CommandTransformations.AssertIntoAssume(options, command))).ToList() + }; + foreach (var target in gotoCmd.labelTargets.Skip(1)) { - paths.Push(current.Push(target)); + paths.Push(ImmutableStack.Empty.Push(assumedBlock).Push(target)); } } diff --git a/Test/pruning/isolatePaths.bpl b/Test/pruning/isolatePaths.bpl index 476dc300d..cd194925c 100644 --- a/Test/pruning/isolatePaths.bpl +++ b/Test/pruning/isolatePaths.bpl @@ -1,7 +1,11 @@ // RUN: %parallel-boogie "%s" /printSplit:%t /errorTrace:0 > "%t" // RUN: %diff "%s.expect" "%t" -// RUN: %diff %S/isolatePaths.bpl.split0.expect %t-NoDuplicateErrors-0.spl -// RUN: %diff %S/isolatePaths.bpl.split1.expect %t-NoDuplicateErrors-1.spl +// RUN: %diff %S/isolatePaths.bpl.NoDuplicateErrors-0.expect %t-NoDuplicateErrors-0.spl +// RUN: %diff %S/isolatePaths.bpl.NoDuplicateErrors-1.expect %t-NoDuplicateErrors-1.spl +// RUN: %diff %S/isolatePaths.bpl.EarlyAssertions--1.expect %t-EarlyAssertions--1.spl +// RUN: %diff %S/isolatePaths.bpl.EarlyAssertionsVariant-0.expect %t-EarlyAssertionsVariant-0.spl +// RUN: %diff %S/isolatePaths.bpl.EarlyAssertionsVariant-1.expect %t-EarlyAssertionsVariant-1.spl +// RUN: %diff %S/isolatePaths.bpl.EmptyPath--1.expect %t-EmptyPath--1.spl procedure {:isolate_paths} NoDuplicateErrors() { @@ -13,4 +17,40 @@ procedure {:isolate_paths} NoDuplicateErrors() x := 2; } assert x == 1; +} + +// We expect only a single VC +procedure {:isolate_paths} EmptyPath() +{ + var x: int; + x := 10; + if (*) { + assert x > 5; + } else { + } +} + +// We expect a single VC that contains both assertions +procedure {:isolate_paths} EarlyAssertions() +{ + var x: int; + x := 10; + assert x > 5; // Should only be asserted once + if (*) { + assert x > 6; + } else { + } +} + +// We expect two VCs, one with two assertions +procedure {:isolate_paths} EarlyAssertionsVariant() +{ + var x: int; + x := 10; + assert x > 5; // Should only be asserted once + if (*) { + assert x > 4; + } else { + assert x > 6; + } } \ No newline at end of file diff --git a/Test/pruning/isolatePaths.bpl.EarlyAssertions--1.expect b/Test/pruning/isolatePaths.bpl.EarlyAssertions--1.expect new file mode 100644 index 000000000..3739eb6f3 --- /dev/null +++ b/Test/pruning/isolatePaths.bpl.EarlyAssertions--1.expect @@ -0,0 +1,10 @@ +implementation {:isolate_paths} EarlyAssertions() +{ + + PreconditionGeneratedEntry: + assert 10 > 5; + assert 10 > 6; + goto ; +} + + diff --git a/Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-0.expect b/Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-0.expect new file mode 100644 index 000000000..acb78aef4 --- /dev/null +++ b/Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-0.expect @@ -0,0 +1,10 @@ +implementation {:isolate_paths} EarlyAssertionsVariant() +{ + + PreconditionGeneratedEntry: + assume 10 > 5; + assert 10 > 6; + goto ; +} + + diff --git a/Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-1.expect b/Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-1.expect new file mode 100644 index 000000000..e493e0687 --- /dev/null +++ b/Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-1.expect @@ -0,0 +1,10 @@ +implementation {:isolate_paths} EarlyAssertionsVariant() +{ + + PreconditionGeneratedEntry: + assert 10 > 5; + assert 10 > 4; + goto ; +} + + diff --git a/Test/pruning/isolatePaths.bpl.EmptyPath--1.expect b/Test/pruning/isolatePaths.bpl.EmptyPath--1.expect new file mode 100644 index 000000000..40fdbde10 --- /dev/null +++ b/Test/pruning/isolatePaths.bpl.EmptyPath--1.expect @@ -0,0 +1,9 @@ +implementation {:isolate_paths} EmptyPath() +{ + + anon0: + assert 10 > 5; + goto ; +} + + diff --git a/Test/pruning/isolatePaths.bpl.split0.expect b/Test/pruning/isolatePaths.bpl.NoDuplicateErrors-0.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.split0.expect rename to Test/pruning/isolatePaths.bpl.NoDuplicateErrors-0.expect diff --git a/Test/pruning/isolatePaths.bpl.split1.expect b/Test/pruning/isolatePaths.bpl.NoDuplicateErrors-1.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.split1.expect rename to Test/pruning/isolatePaths.bpl.NoDuplicateErrors-1.expect diff --git a/Test/pruning/isolatePaths.bpl.expect b/Test/pruning/isolatePaths.bpl.expect index db5930dce..4ecca4d93 100644 --- a/Test/pruning/isolatePaths.bpl.expect +++ b/Test/pruning/isolatePaths.bpl.expect @@ -1,3 +1,3 @@ -isolatePaths.bpl(15,3): Error: this assertion could not be proved +isolatePaths.bpl(19,3): Error: this assertion could not be proved -Boogie program verifier finished with 0 verified, 2 errors +Boogie program verifier finished with 3 verified, 2 errors From 64b101064c98b0f792b9556a4af991b1d274b132 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 25 Sep 2024 10:58:12 +0200 Subject: [PATCH 09/70] More efficient assumed block generation --- Source/VCGeneration/ManualSplitFinder.cs | 41 +++++++++++++++--------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index a84ef5927..b9d538070 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -13,48 +13,59 @@ public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { var paths = options.IsolatePaths || QKeyValue.FindBoolAttribute(run.Implementation.Attributes, "isolate_paths") - ? PathSplits(options, run, createSplit) + ? IsolatedPathSplits(options, run, createSplit) : FocusAttribute.SplitOnFocus(options, run, createSplit); return paths.SelectMany(SplitOnAssertions); } - private static List PathSplits(VCGenOptions options, ImplementationRun run, + record PathBlocks(ImmutableStack Assumed, ImmutableStack UnAssumed); + + private static List IsolatedPathSplits(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { var result = new List(); var firstBlock = run.Implementation.Blocks[0]; - var paths = new Stack>(); - paths.Push(ImmutableStack.Empty.Push(firstBlock)); + var paths = new Stack(); + paths.Push(new PathBlocks(ImmutableStack.Empty, ImmutableStack.Create(firstBlock))); while (paths.Any()) { var current = paths.Pop(); - var last = current.Peek(); + var last = current.UnAssumed.Peek(); if (last.TransferCmd is not GotoCmd gotoCmd || !gotoCmd.labelTargets.Any()) { var masterBlock = new Block(firstBlock.tok) { Label = firstBlock.Label, - Cmds = current.Reverse().SelectMany(block => block.Cmds).ToList(), - TransferCmd = current.Peek().TransferCmd + Cmds = current.Assumed.Concat(current.UnAssumed).Reverse().SelectMany(block => block.Cmds).ToList(), + TransferCmd = current.UnAssumed.Peek().TransferCmd }; result.Add(createSplit(run.Implementation.tok, new List { masterBlock })); continue; } var firstTarget = gotoCmd.labelTargets.First(); - paths.Push(current.Push(firstTarget)); - + paths.Push(current with { UnAssumed = current.UnAssumed.Push(firstTarget) }); + if (gotoCmd.labelTargets.Count <= 1) { continue; } - - var assumedBlock = new Block(run.Implementation.tok) { - Cmds = current.Reverse().SelectMany(block => - block.Cmds.Select(command => CommandTransformations.AssertIntoAssume(options, command))).ToList() - }; foreach (var target in gotoCmd.labelTargets.Skip(1)) { - paths.Push(ImmutableStack.Empty.Push(assumedBlock).Push(target)); + paths.Push(new PathBlocks(Assumed: current.UnAssumed, UnAssumed: ImmutableStack.Create(target))); + } + + + var assumed = current.Assumed; + var remainingUnassumed = current.UnAssumed; + while (!remainingUnassumed.IsEmpty) { + assumed = assumed.Push(new Block(remainingUnassumed.Peek().tok) { + Cmds = remainingUnassumed.Peek().Cmds.Select(command => CommandTransformations.AssertIntoAssume(options, command)).ToList() + }); + remainingUnassumed = remainingUnassumed.Pop(); + } + foreach (var target in gotoCmd.labelTargets.Skip(1)) + { + paths.Push(new PathBlocks(assumed, ImmutableStack.Create(target))); } } From bccf0b10fdcdf365cd892d409d022077a127dc41 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 25 Sep 2024 14:50:00 +0200 Subject: [PATCH 10/70] Improvements to splitting, but still need check command to make the test work --- Source/Core/AST/AbsyCmd.cs | 18 ++--- Source/VCGeneration/ManualSplitFinder.cs | 87 +++++++++++++++++------- Test/pruning/isolatePaths.bpl | 20 +++++- 3 files changed, 88 insertions(+), 37 deletions(-) diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 485a88877..5ca6edf09 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -1225,8 +1225,7 @@ public static void ComputeChecksums(CoreOptions options, Cmd cmd, Implementation return; } - var assumeCmd = cmd as AssumeCmd; - if (assumeCmd != null + if (cmd is AssumeCmd assumeCmd && QKeyValue.FindBoolAttribute(assumeCmd.Attributes, "assumption_variable_initialization")) { // Ignore assumption variable initializations. @@ -1238,8 +1237,7 @@ public static void ComputeChecksums(CoreOptions options, Cmd cmd, Implementation using (var tokTxtWr = new TokenTextWriter("", strWr, false, false, options)) { tokTxtWr.UseForComputingChecksums = true; - var havocCmd = cmd as HavocCmd; - if (havocCmd != null) + if (cmd is HavocCmd havocCmd) { tokTxtWr.Write("havoc "); var relevantVars = havocCmd.Vars @@ -1265,11 +1263,9 @@ public static void ComputeChecksums(CoreOptions options, Cmd cmd, Implementation cmd.Checksum = currentChecksum; } - var assertCmd = cmd as AssertCmd; - if (assertCmd != null && assertCmd.Checksum != null) + if (cmd is AssertCmd { Checksum: not null } assertCmd) { - var assertRequiresCmd = assertCmd as AssertRequiresCmd; - if (assertRequiresCmd != null) + if (assertCmd is AssertRequiresCmd assertRequiresCmd) { impl.AddAssertionChecksum(assertRequiresCmd.Checksum); impl.AddAssertionChecksum(assertRequiresCmd.Call.Checksum); @@ -1281,12 +1277,10 @@ public static void ComputeChecksums(CoreOptions options, Cmd cmd, Implementation } } - var sugaredCmd = cmd as SugaredCmd; - if (sugaredCmd != null) + if (cmd is SugaredCmd sugaredCmd) { // The checksum of a sugared command should not depend on the desugaring itself. - var stateCmd = sugaredCmd.GetDesugaring(options) as StateCmd; - if (stateCmd != null) + if (sugaredCmd.GetDesugaring(options) is StateCmd stateCmd) { foreach (var c in stateCmd.Cmds) { diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index b9d538070..bcf6328d4 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -5,10 +5,26 @@ using System.Diagnostics.Contracts; using System.Linq; using Microsoft.Boogie; +using Microsoft.CSharp.RuntimeBinder; using VC; namespace VCGeneration; +static class HashCodeExtensions { + internal static int GetHashCodeByItems(this IEnumerable lst) + { + unchecked + { + int hash = 19; + foreach (T item in lst) + { + hash = hash * 31 + (item != null ? item.GetHashCode() : 1); + } + return hash; + } + } +} + public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { @@ -18,60 +34,83 @@ public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions op return paths.SelectMany(SplitOnAssertions); } - record PathBlocks(ImmutableStack Assumed, ImmutableStack UnAssumed); + class PathBlocksComparer : IEqualityComparer { + + public PathBlocksComparer() { + } + + public bool Equals(PathBlocks? x, PathBlocks? y) { + if (x == null || y == null) { + return x == y; + } + + return x.TransferCmd == y.TransferCmd && x.Commands.SequenceEqual(y.Commands); + } + + public int GetHashCode(PathBlocks obj) { + return HashCode.Combine(obj.TransferCmd.GetHashCode(), obj.Commands.GetHashCodeByItems()); + } + } + + record PathBlocks(ImmutableStack Assumed, ImmutableStack UnAssumed, TransferCmd TransferCmd) { + public IEnumerable Commands => UnAssumed.Concat(Assumed); + } private static List IsolatedPathSplits(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createSplit) - { + Func, ManualSplit> createSplit) { + var comparer = new PathBlocksComparer(); + var visitedPathBlocks = new HashSet(comparer); + var result = new List(); var firstBlock = run.Implementation.Blocks[0]; var paths = new Stack(); - paths.Push(new PathBlocks(ImmutableStack.Empty, ImmutableStack.Create(firstBlock))); + paths.Push(new PathBlocks(ImmutableStack.Empty, ImmutableStack.CreateRange(firstBlock.Cmds), firstBlock.TransferCmd)); while (paths.Any()) { var current = paths.Pop(); - var last = current.UnAssumed.Peek(); - if (last.TransferCmd is not GotoCmd gotoCmd || !gotoCmd.labelTargets.Any()) + // if (!visitedPathBlocks.Add(current)) { + // continue; + // } + + if (current.TransferCmd is not GotoCmd gotoCmd || !gotoCmd.labelTargets.Any()) { var masterBlock = new Block(firstBlock.tok) { Label = firstBlock.Label, - Cmds = current.Assumed.Concat(current.UnAssumed).Reverse().SelectMany(block => block.Cmds).ToList(), - TransferCmd = current.UnAssumed.Peek().TransferCmd + Cmds = current.UnAssumed.Concat(current.Assumed).Reverse().ToList(), + TransferCmd = current.TransferCmd }; result.Add(createSplit(run.Implementation.tok, new List { masterBlock })); continue; } var firstTarget = gotoCmd.labelTargets.First(); - paths.Push(current with { UnAssumed = current.UnAssumed.Push(firstTarget) }); + + paths.Push(current with { UnAssumed = PushRange(firstTarget.Cmds, current.UnAssumed), TransferCmd = firstTarget.TransferCmd }); if (gotoCmd.labelTargets.Count <= 1) { continue; } + + var assumed = PushRange(current.UnAssumed.ToList(), current.Assumed); foreach (var target in gotoCmd.labelTargets.Skip(1)) { - paths.Push(new PathBlocks(Assumed: current.UnAssumed, UnAssumed: ImmutableStack.Create(target))); - } - - - var assumed = current.Assumed; - var remainingUnassumed = current.UnAssumed; - while (!remainingUnassumed.IsEmpty) { - assumed = assumed.Push(new Block(remainingUnassumed.Peek().tok) { - Cmds = remainingUnassumed.Peek().Cmds.Select(command => CommandTransformations.AssertIntoAssume(options, command)).ToList() - }); - remainingUnassumed = remainingUnassumed.Pop(); - } - foreach (var target in gotoCmd.labelTargets.Skip(1)) - { - paths.Push(new PathBlocks(assumed, ImmutableStack.Create(target))); + paths.Push(new PathBlocks(assumed, ImmutableStack.CreateRange(target.Cmds), target.TransferCmd)); } + } return result; } + private static ImmutableStack PushRange(IReadOnlyList newElements, ImmutableStack stack) { + for (var i = newElements.Count - 1; i >= 0; i--) { + stack = stack.Push(newElements[i]); + } + + return stack; + } + private static List SplitOnAssertions(ManualSplit initialSplit) { var splitOnEveryAssert = initialSplit.Options.VcsSplitOnEveryAssert; diff --git a/Test/pruning/isolatePaths.bpl b/Test/pruning/isolatePaths.bpl index cd194925c..f3d7bdb2f 100644 --- a/Test/pruning/isolatePaths.bpl +++ b/Test/pruning/isolatePaths.bpl @@ -7,6 +7,7 @@ // RUN: %diff %S/isolatePaths.bpl.EarlyAssertionsVariant-1.expect %t-EarlyAssertionsVariant-1.spl // RUN: %diff %S/isolatePaths.bpl.EmptyPath--1.expect %t-EmptyPath--1.spl +// Two VCs, each with one assertion procedure {:isolate_paths} NoDuplicateErrors() { var x: int; @@ -53,4 +54,21 @@ procedure {:isolate_paths} EarlyAssertionsVariant() } else { assert x > 6; } -} \ No newline at end of file +} + +procedure {:isolate_paths} EquivalentPrePaths() { + var x: int; + x := 10; + if (*) { + check true; + } + assert x > 4; // Only asserted once + + if (*) { + // Previous assertion stays one in the first follow-up path + check true; + } else { + // Previous assertion became an assume in this path + } + assert x > 5; // Only asserted once +} From 7d5b58f6fa53abde9237187b1bd9b5a388eba7db Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 11:02:54 +0200 Subject: [PATCH 11/70] Some changes --- Source/Core/AST/AbsyQuant.cs | 11 ++++ Source/Graph/Graph.cs | 2 +- Source/VCGeneration/FocusAttribute.cs | 66 ++++++++++++++---------- Source/VCGeneration/ManualSplitFinder.cs | 48 ++++++++++++++--- Test/pruning/isolatePaths.bpl | 20 ++++++- 5 files changed, 108 insertions(+), 39 deletions(-) diff --git a/Source/Core/AST/AbsyQuant.cs b/Source/Core/AST/AbsyQuant.cs index 301a3bf70..5d112862c 100644 --- a/Source/Core/AST/AbsyQuant.cs +++ b/Source/Core/AST/AbsyQuant.cs @@ -552,6 +552,17 @@ public static bool FindBoolAttribute(QKeyValue kv, string name) ((LiteralExpr) qkv.Params[0]).IsTrue))); return kv != null; } + + public static bool? FindNullableBoolAttribute(QKeyValue kv, string name) + { + Contract.Requires(name != null); + kv = FindAttribute(kv, qkv => qkv.Key == name); + if (kv == null) { + return null; + } + return kv.Params.Count == 0 || (kv.Params.Count == 1 && kv.Params[0] is LiteralExpr && + ((LiteralExpr)kv.Params[0]).IsTrue); + } public static int FindIntAttribute(QKeyValue kv, string name, int defl) { diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 0324e09de..c83cd9ece 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -1151,7 +1151,7 @@ public ICollection ComputeReachability(Node start, bool forward = true) continue; } - var targets = forward ? this.Successors(current) : this.Predecessors(current); + var targets = forward ? Successors(current) : Predecessors(current); foreach (var target in targets) { if (!visited.Contains(target)) { todo.Push(target); diff --git a/Source/VCGeneration/FocusAttribute.cs b/Source/VCGeneration/FocusAttribute.cs index 246b6192e..2c92f4c72 100644 --- a/Source/VCGeneration/FocusAttribute.cs +++ b/Source/VCGeneration/FocusAttribute.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; +using System.Text.Json.Nodes; using Microsoft.Boogie; using VC; using Block = Microsoft.Boogie.Block; @@ -15,6 +16,10 @@ namespace VCGeneration; public static class FocusAttribute { + /// + /// Each focus block creates two options. + /// We recurse twice for each focus, leading to potentially 2^N splits + /// public static List SplitOnFocus(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { @@ -33,7 +38,11 @@ public static List SplitOnFocus(VCGenOptions options, Implementatio var ancestorsPerBlock = new Dictionary>(); var descendantsPerBlock = new Dictionary>(); - focusBlocks.ForEach(fb => ancestorsPerBlock[fb.Block] = dag.ComputeReachability(fb.Block, false).ToHashSet()); + focusBlocks.ForEach(fb => { + var reachables = dag.ComputeReachability(fb.Block, false).ToHashSet(); + reachables.Remove(fb.Block); + ancestorsPerBlock[fb.Block] = reachables; + }); focusBlocks.ForEach(fb => descendantsPerBlock[fb.Block] = dag.ComputeReachability(fb.Block).ToHashSet()); var result = new List(); var duplicator = new Duplicator(); @@ -46,11 +55,11 @@ void FocusRec(IToken focusToken, int focusIndex, IReadOnlyList blocksToIn if (focusIndex == focusBlocks.Count) { // it is important for l to be consistent with reverse topological order. - var sortedBlocks = dag.TopologicalSort().Where(blocksToInclude.Contains).Reverse(); + var reverseSortedBlocks = topologicallySortedBlocks.Where(blocksToInclude.Contains).Reverse(); // assert that the root block, impl.Blocks[0], is in l var newBlocks = new List(); var oldToNewBlockMap = new Dictionary(blocksToInclude.Count()); - foreach (var block in sortedBlocks) + foreach (var block in reverseSortedBlocks) { var newBlock = (Block)duplicator.Visit(block); newBlocks.Add(newBlock); @@ -73,24 +82,30 @@ void FocusRec(IToken focusToken, int focusIndex, IReadOnlyList blocksToIn Contract.Assert(newBlocks[0] == oldToNewBlockMap[impl.Blocks[0]]); BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); result.Add(createSplit(focusToken, newBlocks)); - } - else if (!blocksToInclude.Contains(focusBlocks[focusIndex].Block) || freeAssumeBlocks.Contains(focusBlocks[focusIndex].Block)) - { - FocusRec(focusBlocks[focusIndex].Token, focusIndex + 1, blocksToInclude, freeAssumeBlocks); - } - else - { - var (block, nextToken) = focusBlocks[focusIndex]; // assert b in blocks - var dominatedBlocks = DominatedBlocks(topologicallySortedBlocks, block, blocksToInclude); - // the first part takes all blocks except the ones dominated by the focus block - FocusRec(nextToken, focusIndex + 1, blocksToInclude.Where(blk => !dominatedBlocks.Contains(blk)).ToList(), freeAssumeBlocks); - var ancestors = ancestorsPerBlock[block]; - ancestors.Remove(block); - var descendants = descendantsPerBlock[block]; - // the other part takes all the ancestors, the focus block, and the descendants. - FocusRec(nextToken, focusIndex + 1, - ancestors.Union(descendants).Intersect(blocksToInclude).ToList(), - ancestors.Union(freeAssumeBlocks).ToList()); + } else { + var (focusBlock, nextToken) = focusBlocks[focusIndex]; // assert b in blocks + if (!blocksToInclude.Contains(focusBlocks[focusIndex].Block) || freeAssumeBlocks.Contains(focusBlocks[focusIndex].Block)) + { + // This focus block can not be reached in our current path, so we ignore it by continuing + FocusRec(focusToken, focusIndex + 1, blocksToInclude, freeAssumeBlocks); + } + else + { + var dominatedBlocks = DominatedBlocks(topologicallySortedBlocks, focusBlock, blocksToInclude); + // Recursive call that does NOT focus the block + // Contains all blocks except the ones dominated by the focus block + FocusRec(focusToken, focusIndex + 1, + blocksToInclude.Where(blk => !dominatedBlocks.Contains(blk)).ToList(), freeAssumeBlocks); + var ancestors = ancestorsPerBlock[focusBlock]; + var descendants = descendantsPerBlock[focusBlock]; + + // TODO: nextToken should be a combination of focusToken and nextToken + // Recursive call that does focus the block + // Contains all the ancestors, the focus block, and the descendants. + FocusRec(nextToken, focusIndex + 1, + ancestors.Union(descendants).Intersect(blocksToInclude).ToList(), + ancestors.Union(freeAssumeBlocks).ToList()); + } } } } @@ -122,13 +137,8 @@ private static Cmd DisableSplits(Cmd command) return command; } - for (var kv = pc.Attributes; kv != null; kv = kv.Next) - { - if (kv.Key == "split") - { - kv.AddParam(new LiteralExpr(Token.NoToken, false)); - } - } + pc.Attributes = new QKeyValue(Token.NoToken, "split", + new List { new LiteralExpr(Token.NoToken, false) }, pc.Attributes); return command; } diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index bcf6328d4..d8034095d 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.Contracts; @@ -92,10 +93,14 @@ private static List IsolatedPathSplits(VCGenOptions options, Implem continue; } - var assumed = PushRange(current.UnAssumed.ToList(), current.Assumed); - foreach (var target in gotoCmd.labelTargets.Skip(1)) - { - paths.Push(new PathBlocks(assumed, ImmutableStack.CreateRange(target.Cmds), target.TransferCmd)); + var assumed = current.Assumed; + var unassumedList = current.UnAssumed.ToList(); + for (int i = unassumedList.Count - 1; i >= 0; i--) { + assumed = assumed.Push(CommandTransformations.AssertIntoAssume(options, unassumedList[i])); + } + foreach (var target in gotoCmd.labelTargets.Skip(1)) { + var immutableStack = ImmutableStack.CreateRange(target.Cmds); + paths.Push(new PathBlocks(assumed, immutableStack, target.TransferCmd)); } } @@ -104,8 +109,8 @@ private static List IsolatedPathSplits(VCGenOptions options, Implem } private static ImmutableStack PushRange(IReadOnlyList newElements, ImmutableStack stack) { - for (var i = newElements.Count - 1; i >= 0; i--) { - stack = stack.Push(newElements[i]); + foreach (var element in newElements) { + stack = stack.Push(element); } return stack; @@ -165,8 +170,12 @@ private static List SplitOnAssertions(ManualSplit initialSplit) { } private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { - return c is PredicateCmd p && QKeyValue.FindBoolAttribute(p.Attributes, "split_here") - || c is AssertCmd && splitOnEveryAssert; + if (c is not PredicateCmd predicateCmd) { + return false; + } + + var findBoolAttribute = QKeyValue.FindNullableBoolAttribute(predicateCmd.Attributes, "split_here"); + return findBoolAttribute ?? (c is AssertCmd && splitOnEveryAssert); } // Verify b with the last split in blockAssignments[b] @@ -277,4 +286,27 @@ private static void AddBlockJumps(List oldBlocks, Dictionary : IReadOnlyList { + private readonly Func f; + + public SelectReadOnlyLists(IReadOnlyList inner, Func f) { + this.f = f; + Inner = inner; + } + + public IReadOnlyList Inner { get; } + public IEnumerator GetEnumerator() { + return Inner.Select(inner => f(inner)).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + public int Count => Inner.Count; + + public U this[int index] => f(Inner[index]); } \ No newline at end of file diff --git a/Test/pruning/isolatePaths.bpl b/Test/pruning/isolatePaths.bpl index f3d7bdb2f..ccc352261 100644 --- a/Test/pruning/isolatePaths.bpl +++ b/Test/pruning/isolatePaths.bpl @@ -56,17 +56,33 @@ procedure {:isolate_paths} EarlyAssertionsVariant() } } +// Only three VCs, not four +procedure {:isolate_paths} AssumeFalse() { + var x: int; + x := 10; + if (*) { + assume false; + } + assert x > 4; // Only asserted once + + if (*) { + assume false; + } else { + } + assert x > 5; // Only asserted once +} + procedure {:isolate_paths} EquivalentPrePaths() { var x: int; x := 10; if (*) { - check true; + //check true; } assert x > 4; // Only asserted once if (*) { // Previous assertion stays one in the first follow-up path - check true; + // check true; } else { // Previous assertion became an assume in this path } From a2f1f7604a76fb72cd79917a0f331ef151b66649 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 11:05:07 +0200 Subject: [PATCH 12/70] Remove isolatePaths code --- Source/ExecutionEngine/CommandLineOptions.cs | 1 - Source/VCGeneration/ManualSplitFinder.cs | 100 +------------------ Source/VCGeneration/VCGenOptions.cs | 1 - Test/pruning/MultiFocus.bpl | 63 ++++++++++++ Test/pruning/MultiFocus.bpl.expect | 5 + Test/pruning/isolatePaths.bpl | 2 +- 6 files changed, 70 insertions(+), 102 deletions(-) create mode 100644 Test/pruning/MultiFocus.bpl create mode 100644 Test/pruning/MultiFocus.bpl.expect diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 63c3adc98..45f92cf05 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -450,7 +450,6 @@ public bool TrustSequentialization { } public bool RemoveEmptyBlocks { get; set; } = true; - public bool IsolatePaths { get; set; } public bool CoalesceBlocks { get; set; } = true; public bool PruneInfeasibleEdges { get; set; } = true; diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index d8034095d..bf5803737 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -6,115 +6,17 @@ using System.Diagnostics.Contracts; using System.Linq; using Microsoft.Boogie; -using Microsoft.CSharp.RuntimeBinder; using VC; namespace VCGeneration; -static class HashCodeExtensions { - internal static int GetHashCodeByItems(this IEnumerable lst) - { - unchecked - { - int hash = 19; - foreach (T item in lst) - { - hash = hash * 31 + (item != null ? item.GetHashCode() : 1); - } - return hash; - } - } -} public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { - var paths = options.IsolatePaths || QKeyValue.FindBoolAttribute(run.Implementation.Attributes, "isolate_paths") - ? IsolatedPathSplits(options, run, createSplit) - : FocusAttribute.SplitOnFocus(options, run, createSplit); + var paths = FocusAttribute.SplitOnFocus(options, run, createSplit); return paths.SelectMany(SplitOnAssertions); } - - class PathBlocksComparer : IEqualityComparer { - - public PathBlocksComparer() { - } - - public bool Equals(PathBlocks? x, PathBlocks? y) { - if (x == null || y == null) { - return x == y; - } - - return x.TransferCmd == y.TransferCmd && x.Commands.SequenceEqual(y.Commands); - } - - public int GetHashCode(PathBlocks obj) { - return HashCode.Combine(obj.TransferCmd.GetHashCode(), obj.Commands.GetHashCodeByItems()); - } - } - - record PathBlocks(ImmutableStack Assumed, ImmutableStack UnAssumed, TransferCmd TransferCmd) { - public IEnumerable Commands => UnAssumed.Concat(Assumed); - } - - private static List IsolatedPathSplits(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createSplit) { - var comparer = new PathBlocksComparer(); - var visitedPathBlocks = new HashSet(comparer); - - var result = new List(); - var firstBlock = run.Implementation.Blocks[0]; - var paths = new Stack(); - paths.Push(new PathBlocks(ImmutableStack.Empty, ImmutableStack.CreateRange(firstBlock.Cmds), firstBlock.TransferCmd)); - while (paths.Any()) - { - var current = paths.Pop(); - // if (!visitedPathBlocks.Add(current)) { - // continue; - // } - - if (current.TransferCmd is not GotoCmd gotoCmd || !gotoCmd.labelTargets.Any()) - { - var masterBlock = new Block(firstBlock.tok) - { - Label = firstBlock.Label, - Cmds = current.UnAssumed.Concat(current.Assumed).Reverse().ToList(), - TransferCmd = current.TransferCmd - }; - result.Add(createSplit(run.Implementation.tok, new List { masterBlock })); - continue; - } - - var firstTarget = gotoCmd.labelTargets.First(); - - paths.Push(current with { UnAssumed = PushRange(firstTarget.Cmds, current.UnAssumed), TransferCmd = firstTarget.TransferCmd }); - - if (gotoCmd.labelTargets.Count <= 1) { - continue; - } - - var assumed = current.Assumed; - var unassumedList = current.UnAssumed.ToList(); - for (int i = unassumedList.Count - 1; i >= 0; i--) { - assumed = assumed.Push(CommandTransformations.AssertIntoAssume(options, unassumedList[i])); - } - foreach (var target in gotoCmd.labelTargets.Skip(1)) { - var immutableStack = ImmutableStack.CreateRange(target.Cmds); - paths.Push(new PathBlocks(assumed, immutableStack, target.TransferCmd)); - } - - } - - return result; - } - - private static ImmutableStack PushRange(IReadOnlyList newElements, ImmutableStack stack) { - foreach (var element in newElements) { - stack = stack.Push(element); - } - - return stack; - } private static List SplitOnAssertions(ManualSplit initialSplit) { diff --git a/Source/VCGeneration/VCGenOptions.cs b/Source/VCGeneration/VCGenOptions.cs index d7129f617..6fc1ca5f5 100644 --- a/Source/VCGeneration/VCGenOptions.cs +++ b/Source/VCGeneration/VCGenOptions.cs @@ -6,5 +6,4 @@ public interface VCGenOptions : SMTLibOptions bool AlwaysAssumeFreeLoopInvariants { get; } int LiveVariableAnalysis { get; } bool RemoveEmptyBlocks { get; } - bool IsolatePaths { get; } } \ No newline at end of file diff --git a/Test/pruning/MultiFocus.bpl b/Test/pruning/MultiFocus.bpl new file mode 100644 index 000000000..6d64c1440 --- /dev/null +++ b/Test/pruning/MultiFocus.bpl @@ -0,0 +1,63 @@ +// RUN: %parallel-boogie /printSplit:%t /vcsSplitOnEveryAssert /errorTrace:0 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure GuardWithoutFocus(x: int) returns (r: int) + ensures r > 0; +{ + if (x > 0) { + assert x > 2; + r := 3; + return; + } + + r := 2; +} + +procedure GuardWithFocus(x: int) returns (r: int) + ensures r > 0; +{ + if (x > 0) { + assert {:focus} x > 2; + r := 3; + return; + } + + r := 2; + return; +} + +procedure SubSequentAndNestedFocus(x: int) +{ + if (*) { + if (*) { + assume {:focus} true; + assert x > 1; + } else { + assert x > 2; + } + } else if (*) { + assume {:focus} true; + assert x > 3; + } else { + assert x > 4; + } + assert x > 5; + if (*) { + assume {:focus} true; + assert x > 6; + } else { + assert x > 7; + } +} + +procedure {:vcs_split_on_every_assert} EarlyReturn(x: int) { + if (*) { + assume {:focus} true; + assert x > 1; + return; + } + + assert x > 2; + assert x > 3; + assert x > 4; +} \ No newline at end of file diff --git a/Test/pruning/MultiFocus.bpl.expect b/Test/pruning/MultiFocus.bpl.expect new file mode 100644 index 000000000..4250cfcbc --- /dev/null +++ b/Test/pruning/MultiFocus.bpl.expect @@ -0,0 +1,5 @@ +MultiFocus.bpl(22,3): Error: this assertion could not be proved +MultiFocus.bpl(22,3): Error: this assertion could not be proved +MultiFocus.bpl(22,3): Error: this assertion could not be proved + +Boogie program verifier finished with 0 verified, 3 errors diff --git a/Test/pruning/isolatePaths.bpl b/Test/pruning/isolatePaths.bpl index ccc352261..1a14bb2b6 100644 --- a/Test/pruning/isolatePaths.bpl +++ b/Test/pruning/isolatePaths.bpl @@ -1,4 +1,4 @@ -// RUN: %parallel-boogie "%s" /printSplit:%t /errorTrace:0 > "%t" +// RUN: %parallel-boogie "%s" /printSplit:%t /trace /errorTrace:0 > "%t" // RUN: %diff "%s.expect" "%t" // RUN: %diff %S/isolatePaths.bpl.NoDuplicateErrors-0.expect %t-NoDuplicateErrors-0.spl // RUN: %diff %S/isolatePaths.bpl.NoDuplicateErrors-1.expect %t-NoDuplicateErrors-1.spl From 9620a3f92729568e847eabdaa94b23f4f6f60338 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 11:19:03 +0200 Subject: [PATCH 13/70] Add path tokens --- .../{FocusAttribute.cs => Splits/Focus.cs} | 22 ++++----- .../{ => Splits}/ManualSplitFinder.cs | 25 +--------- Source/VCGeneration/Splits/PathToken.cs | 15 ++++++ Source/VCGeneration/{ => Splits}/Split.cs | 1 + .../{ => Splits}/SplitAndVerifyWorker.cs | 0 Source/VCGeneration/Splits/TokenWrapper.cs | 47 +++++++++++++++++++ 6 files changed, 75 insertions(+), 35 deletions(-) rename Source/VCGeneration/{FocusAttribute.cs => Splits/Focus.cs} (89%) rename Source/VCGeneration/{ => Splits}/ManualSplitFinder.cs (92%) create mode 100644 Source/VCGeneration/Splits/PathToken.cs rename Source/VCGeneration/{ => Splits}/Split.cs (99%) rename Source/VCGeneration/{ => Splits}/SplitAndVerifyWorker.cs (100%) create mode 100644 Source/VCGeneration/Splits/TokenWrapper.cs diff --git a/Source/VCGeneration/FocusAttribute.cs b/Source/VCGeneration/Splits/Focus.cs similarity index 89% rename from Source/VCGeneration/FocusAttribute.cs rename to Source/VCGeneration/Splits/Focus.cs index 2c92f4c72..f487d0bcd 100644 --- a/Source/VCGeneration/FocusAttribute.cs +++ b/Source/VCGeneration/Splits/Focus.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.Contracts; using System.Linq; using System.Text.Json.Nodes; @@ -13,12 +14,12 @@ namespace VCGeneration; -public static class FocusAttribute +public static class Focus { /// /// Each focus block creates two options. - /// We recurse twice for each focus, leading to potentially 2^N splits + /// We recurse twice for each focus, leading to potentially 2^N splits /// public static List SplitOnFocus(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) @@ -47,12 +48,12 @@ public static List SplitOnFocus(VCGenOptions options, Implementatio var result = new List(); var duplicator = new Duplicator(); - FocusRec(run.Implementation.tok, 0, impl.Blocks, new List()); + AddSplitsFromIndex(ImmutableStack.Empty, 0, impl.Blocks, new List()); return result; - void FocusRec(IToken focusToken, int focusIndex, IReadOnlyList blocksToInclude, IReadOnlyList freeAssumeBlocks) - { - if (focusIndex == focusBlocks.Count) + void AddSplitsFromIndex(ImmutableStack path, int focusIndex, IReadOnlyList blocksToInclude, IReadOnlyList freeAssumeBlocks) { + var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; + if (allFocusBlocksHaveBeenProcessed) { // it is important for l to be consistent with reverse topological order. var reverseSortedBlocks = topologicallySortedBlocks.Where(blocksToInclude.Contains).Reverse(); @@ -81,28 +82,27 @@ void FocusRec(IToken focusToken, int focusIndex, IReadOnlyList blocksToIn newBlocks.Reverse(); Contract.Assert(newBlocks[0] == oldToNewBlockMap[impl.Blocks[0]]); BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); - result.Add(createSplit(focusToken, newBlocks)); + result.Add(createSplit(new PathToken(run.Implementation.tok, path), newBlocks)); } else { var (focusBlock, nextToken) = focusBlocks[focusIndex]; // assert b in blocks if (!blocksToInclude.Contains(focusBlocks[focusIndex].Block) || freeAssumeBlocks.Contains(focusBlocks[focusIndex].Block)) { // This focus block can not be reached in our current path, so we ignore it by continuing - FocusRec(focusToken, focusIndex + 1, blocksToInclude, freeAssumeBlocks); + AddSplitsFromIndex(path, focusIndex + 1, blocksToInclude, freeAssumeBlocks); } else { var dominatedBlocks = DominatedBlocks(topologicallySortedBlocks, focusBlock, blocksToInclude); // Recursive call that does NOT focus the block // Contains all blocks except the ones dominated by the focus block - FocusRec(focusToken, focusIndex + 1, + AddSplitsFromIndex(path, focusIndex + 1, blocksToInclude.Where(blk => !dominatedBlocks.Contains(blk)).ToList(), freeAssumeBlocks); var ancestors = ancestorsPerBlock[focusBlock]; var descendants = descendantsPerBlock[focusBlock]; - // TODO: nextToken should be a combination of focusToken and nextToken // Recursive call that does focus the block // Contains all the ancestors, the focus block, and the descendants. - FocusRec(nextToken, focusIndex + 1, + AddSplitsFromIndex(path.Push(nextToken), focusIndex + 1, ancestors.Union(descendants).Intersect(blocksToInclude).ToList(), ancestors.Union(freeAssumeBlocks).ToList()); } diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs similarity index 92% rename from Source/VCGeneration/ManualSplitFinder.cs rename to Source/VCGeneration/Splits/ManualSplitFinder.cs index bf5803737..30dd0a163 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -14,7 +14,7 @@ namespace VCGeneration; public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { - var paths = FocusAttribute.SplitOnFocus(options, run, createSplit); + var paths = Focus.SplitOnFocus(options, run, createSplit); return paths.SelectMany(SplitOnAssertions); } @@ -188,27 +188,4 @@ private static void AddBlockJumps(List oldBlocks, Dictionary : IReadOnlyList { - private readonly Func f; - - public SelectReadOnlyLists(IReadOnlyList inner, Func f) { - this.f = f; - Inner = inner; - } - - public IReadOnlyList Inner { get; } - public IEnumerator GetEnumerator() { - return Inner.Select(inner => f(inner)).GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); - } - - public int Count => Inner.Count; - - public U this[int index] => f(Inner[index]); } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/PathToken.cs b/Source/VCGeneration/Splits/PathToken.cs new file mode 100644 index 000000000..016dc96be --- /dev/null +++ b/Source/VCGeneration/Splits/PathToken.cs @@ -0,0 +1,15 @@ +#nullable enable +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.Boogie; + +namespace VCGeneration; + +class PathToken : TokenWrapper { + + public PathToken(IToken inner, ImmutableStack branches) : base(inner) { + Branches = branches; + } + + public ImmutableStack Branches { get; } +} \ No newline at end of file diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Splits/Split.cs similarity index 99% rename from Source/VCGeneration/Split.cs rename to Source/VCGeneration/Splits/Split.cs index 7cf747118..c7fd79cc8 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Splits/Split.cs @@ -13,6 +13,7 @@ namespace VC { + // TODO rename since it's not only used to split a proof into two. For example focus doesn't really split public class Split : ProofRun { public VCGenOptions Options { get; } diff --git a/Source/VCGeneration/SplitAndVerifyWorker.cs b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs similarity index 100% rename from Source/VCGeneration/SplitAndVerifyWorker.cs rename to Source/VCGeneration/Splits/SplitAndVerifyWorker.cs diff --git a/Source/VCGeneration/Splits/TokenWrapper.cs b/Source/VCGeneration/Splits/TokenWrapper.cs new file mode 100644 index 000000000..0c1329b8e --- /dev/null +++ b/Source/VCGeneration/Splits/TokenWrapper.cs @@ -0,0 +1,47 @@ +using Microsoft.Boogie; + +namespace VCGeneration; + +class TokenWrapper : IToken { + private readonly IToken inner; + + public TokenWrapper(IToken inner) { + this.inner = inner; + } + + public int CompareTo(IToken? other) { + return inner.CompareTo(other); + } + + public int kind { + get => inner.kind; + set => inner.kind = value; + } + + public string filename { + get => inner.filename; + set => inner.filename = value; + } + + public int pos { + get => inner.pos; + set => inner.pos = value; + } + + public int col { + get => inner.col; + set => inner.col = value; + } + + public int line { + get => inner.line; + set => inner.line = value; + } + + public string val { + get => inner.val; + set => inner.val = value; + } + + public bool IsValid => inner.IsValid; +} \ No newline at end of file From c5f13f06b34ab09e1d0c77d85b9b3b63431b3daa Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 11:44:13 +0200 Subject: [PATCH 14/70] Refactoring --- Source/ExecutionEngine/CommandLineOptions.cs | 10 --- Source/VCGeneration/Splits/Focus.cs | 84 ++++++++++--------- .../VCGeneration/Splits/ManualSplitFinder.cs | 2 +- 3 files changed, 47 insertions(+), 49 deletions(-) diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 45f92cf05..8c6b1ff3a 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -969,16 +969,6 @@ protected override bool ParseOption(string name, CommandLineParseState ps) return true; } - case "isolatePaths": - { - int cb = 0; - if (ps.GetIntArgument(x => cb = x, 2)) - { - IsolatePaths = cb == 1; - } - - return true; - } case "coalesceBlocks": { int cb = 0; diff --git a/Source/VCGeneration/Splits/Focus.cs b/Source/VCGeneration/Splits/Focus.cs index f487d0bcd..268e85418 100644 --- a/Source/VCGeneration/Splits/Focus.cs +++ b/Source/VCGeneration/Splits/Focus.cs @@ -27,6 +27,7 @@ public static List SplitOnFocus(VCGenOptions options, Implementatio var impl = run.Implementation; var dag = Program.GraphFromImpl(impl); var topologicallySortedBlocks = dag.TopologicalSort().ToList(); + var blocksReversed = Enumerable.Reverse(topologicallySortedBlocks).ToList(); // By default, we process the foci in a top-down fashion, i.e., in the topological order. // If the user sets the RelaxFocus flag, we use the reverse (topological) order. var focusBlocks = GetFocusBlocks(topologicallySortedBlocks); @@ -46,46 +47,18 @@ public static List SplitOnFocus(VCGenOptions options, Implementatio }); focusBlocks.ForEach(fb => descendantsPerBlock[fb.Block] = dag.ComputeReachability(fb.Block).ToHashSet()); var result = new List(); - var duplicator = new Duplicator(); - AddSplitsFromIndex(ImmutableStack.Empty, 0, impl.Blocks, new List()); + AddSplitsFromIndex(ImmutableStack.Empty, 0, impl.Blocks.ToHashSet(), ImmutableHashSet.Empty); return result; - void AddSplitsFromIndex(ImmutableStack path, int focusIndex, IReadOnlyList blocksToInclude, IReadOnlyList freeAssumeBlocks) { + void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet blocksToInclude, ISet freeAssumeBlocks) { var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; - if (allFocusBlocksHaveBeenProcessed) - { - // it is important for l to be consistent with reverse topological order. - var reverseSortedBlocks = topologicallySortedBlocks.Where(blocksToInclude.Contains).Reverse(); - // assert that the root block, impl.Blocks[0], is in l - var newBlocks = new List(); - var oldToNewBlockMap = new Dictionary(blocksToInclude.Count()); - foreach (var block in reverseSortedBlocks) - { - var newBlock = (Block)duplicator.Visit(block); - newBlocks.Add(newBlock); - oldToNewBlockMap[block] = newBlock; - // freeBlocks consist of the predecessors of the relevant foci. - // Their assertions turn into assumes and any splits inside them are disabled. - if(freeAssumeBlocks.Contains(block)) - { - newBlock.Cmds = block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(options, c)).Select(DisableSplits).ToList(); - } - if (block.TransferCmd is GotoCmd gtc) - { - var targets = gtc.labelTargets.Where(blocksToInclude.Contains).ToList(); - newBlock.TransferCmd = new GotoCmd(gtc.tok, - targets.Select(blk => oldToNewBlockMap[blk].Label).ToList(), - targets.Select(blk => oldToNewBlockMap[blk]).ToList()); - } - } - newBlocks.Reverse(); - Contract.Assert(newBlocks[0] == oldToNewBlockMap[impl.Blocks[0]]); - BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); + if (allFocusBlocksHaveBeenProcessed) { + var newBlocks = ComputeNewBlocks(options, blocksToInclude, blocksReversed, freeAssumeBlocks); result.Add(createSplit(new PathToken(run.Implementation.tok, path), newBlocks)); } else { var (focusBlock, nextToken) = focusBlocks[focusIndex]; // assert b in blocks - if (!blocksToInclude.Contains(focusBlocks[focusIndex].Block) || freeAssumeBlocks.Contains(focusBlocks[focusIndex].Block)) + if (!blocksToInclude.Contains(focusBlock) || freeAssumeBlocks.Contains(focusBlock)) { // This focus block can not be reached in our current path, so we ignore it by continuing AddSplitsFromIndex(path, focusIndex + 1, blocksToInclude, freeAssumeBlocks); @@ -96,23 +69,58 @@ void AddSplitsFromIndex(ImmutableStack path, int focusIndex, IReadOnlyLi // Recursive call that does NOT focus the block // Contains all blocks except the ones dominated by the focus block AddSplitsFromIndex(path, focusIndex + 1, - blocksToInclude.Where(blk => !dominatedBlocks.Contains(blk)).ToList(), freeAssumeBlocks); + blocksToInclude.Where(blk => !dominatedBlocks.Contains(blk)).ToHashSet(), freeAssumeBlocks); var ancestors = ancestorsPerBlock[focusBlock]; var descendants = descendantsPerBlock[focusBlock]; // Recursive call that does focus the block // Contains all the ancestors, the focus block, and the descendants. AddSplitsFromIndex(path.Push(nextToken), focusIndex + 1, - ancestors.Union(descendants).Intersect(blocksToInclude).ToList(), - ancestors.Union(freeAssumeBlocks).ToList()); + ancestors.Union(descendants).Intersect(blocksToInclude).ToHashSet(), + ancestors.Union(freeAssumeBlocks).ToHashSet()); } } } } - + + private static List ComputeNewBlocks(VCGenOptions options, ISet blocksToInclude, List blocksReversed, + ISet freeAssumeBlocks) + { + var duplicator = new Duplicator(); + var newBlocks = new List(); + var oldToNewBlockMap = new Dictionary(blocksToInclude.Count); + + // Traverse backwards to allow settings the jumps to the new blocks + foreach (var block in blocksReversed) + { + if (!blocksToInclude.Contains(block)) { + continue; + } + var newBlock = (Block)duplicator.Visit(block); + newBlocks.Add(newBlock); + oldToNewBlockMap[block] = newBlock; + // freeBlocks consist of the predecessors of the relevant foci. + // Their assertions turn into assumes and any splits inside them are disabled. + if(freeAssumeBlocks.Contains(block)) + { + newBlock.Cmds = block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(options, c)).Select(DisableSplits).ToList(); + } + if (block.TransferCmd is GotoCmd gtc) + { + var targets = gtc.labelTargets.Where(blocksToInclude.Contains).ToList(); + newBlock.TransferCmd = new GotoCmd(gtc.tok, + targets.Select(blk => oldToNewBlockMap[blk].Label).ToList(), + targets.Select(blk => oldToNewBlockMap[blk]).ToList()); + } + } + newBlocks.Reverse(); + BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); + return newBlocks; + } + // finds all the blocks dominated by focusBlock in the subgraph // which only contains vertices of subgraph. - private static HashSet DominatedBlocks(List topologicallySortedBlocks, Block focusBlock, IReadOnlyList subgraph) + private static HashSet DominatedBlocks(List topologicallySortedBlocks, Block focusBlock, ISet subgraph) { var dominators = new Dictionary>(); foreach (var b in topologicallySortedBlocks.Where(subgraph.Contains)) diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 30dd0a163..1db3cd918 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -35,7 +35,7 @@ private static List SplitOnAssertions(ManualSplit initialSplit) { if (!splitPoints.Any()) { splits.Add(initialSplit); } else { - Block entryPoint = initialSplit.Blocks[0]; + var entryPoint = initialSplit.Blocks[0]; var blockAssignments = PickBlocksToVerify(initialSplit.Blocks, splitPoints); var entryBlockHasSplit = splitPoints.ContainsKey(entryPoint); var firstSplitBlocks = DoPreAssignedManualSplit(initialSplit.Options, initialSplit.Blocks, blockAssignments, From 409cdb80f37cc3f55597128eb49840acd179fc20 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 11:47:00 +0200 Subject: [PATCH 15/70] Rename --- Source/VCGeneration/Splits/ManualSplitFinder.cs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 1db3cd918..589381bc3 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -38,7 +38,7 @@ private static List SplitOnAssertions(ManualSplit initialSplit) { var entryPoint = initialSplit.Blocks[0]; var blockAssignments = PickBlocksToVerify(initialSplit.Blocks, splitPoints); var entryBlockHasSplit = splitPoints.ContainsKey(entryPoint); - var firstSplitBlocks = DoPreAssignedManualSplit(initialSplit.Options, initialSplit.Blocks, blockAssignments, + var firstSplitBlocks = GetPostSplitVcBlocks(initialSplit.Options, initialSplit.Blocks, blockAssignments, -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert); if (firstSplitBlocks != null) { @@ -56,7 +56,7 @@ private static List SplitOnAssertions(ManualSplit initialSplit) { for (int i = 0; i < tokens.Count; i++) { var token = tokens[i]; bool lastSplitInBlock = i == tokens.Count - 1; - var newBlocks = DoPreAssignedManualSplit(initialSplit.Options, initialSplit.Blocks, blockAssignments, i, block, lastSplitInBlock, splitOnEveryAssert); + var newBlocks = GetPostSplitVcBlocks(initialSplit.Options, initialSplit.Blocks, blockAssignments, i, block, lastSplitInBlock, splitOnEveryAssert); if (newBlocks != null) { splits.Add(new ManualSplit(initialSplit.Options, @@ -109,11 +109,10 @@ private static Dictionary PickBlocksToVerify(List blocks, D return blockAssignments; } - private static List? DoPreAssignedManualSplit(VCGenOptions options, List blocks, + private static List? GetPostSplitVcBlocks(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(); + Block blockWithSplit, bool lastSplitInBlock, bool splitOnEveryAssert) { + var newBlocks = new List(blocks.Count); var assertionCount = 0; var oldToNewBlockMap = new Dictionary(blocks.Count); // Maps original blocks to their new copies in newBlocks foreach (var currentBlock in blocks) { @@ -124,7 +123,7 @@ private static Dictionary PickBlocksToVerify(List blocks, D oldToNewBlockMap[currentBlock] = newBlock; newBlocks.Add(newBlock); - if (currentBlock == containingBlock) { + if (currentBlock == blockWithSplit) { var newCmds = new List(); var splitCount = -1; var verify = splitCount == splitNumberWithinBlock; @@ -141,7 +140,7 @@ private static Dictionary PickBlocksToVerify(List blocks, D newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(options, command)); } newBlock.Cmds = newCmds; - } else if (lastSplitInBlock && blockAssignments[currentBlock] == containingBlock) { + } else if (lastSplitInBlock && blockAssignments[currentBlock] == blockWithSplit) { var verify = true; var newCmds = new List(); foreach (var command in currentBlock.Cmds) { From d4d5db6de6ef174a9dd9da4496d2cffb98c8e4b2 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 14:00:09 +0200 Subject: [PATCH 16/70] Refactoring --- .../AbstractInterpretation/NativeLattice.cs | 2 +- Source/AbstractInterpretation/Traverse.cs | 12 +- Source/Concurrency/CivlCoreTypes.cs | 2 +- Source/Concurrency/CivlTypeChecker.cs | 2 +- Source/Concurrency/CivlUtil.cs | 2 +- Source/Concurrency/LinearTypeChecker.cs | 2 +- .../TransitionRelationComputation.cs | 2 +- .../YieldSufficiencyTypeChecker.cs | 2 +- .../YieldingProcInstrumentation.cs | 2 +- Source/Core/AST/Absy.cs | 8 +- Source/Core/AST/AbsyCmd.cs | 58 ++--- Source/Core/AST/Block.cs | 2 +- Source/Core/AST/Implementation.cs | 10 +- Source/Core/AST/Program.cs | 4 +- Source/Core/Analysis/BlockCoalescer.cs | 18 +- .../ImplementationControlFlowGraph.cs | 4 +- .../LiveVariableAnalysis.cs | 4 +- Source/Core/Duplicator.cs | 12 +- Source/Core/Inline.cs | 6 +- .../Core/InterProceduralReachabilityGraph.cs | 22 +- Source/Core/LoopUnroll.cs | 4 +- Source/Core/Monomorphization.cs | 4 +- Source/Core/VariableDependenceAnalyser.cs | 4 +- Source/Graph/Graph.cs | 30 ++- Source/Provers/LeanAuto/LeanAutoGenerator.cs | 4 +- Source/UnitTests/CoreTests/Duplicator.cs | 22 +- Source/VCGeneration/BlockTransformations.cs | 18 +- Source/VCGeneration/ConditionGeneration.cs | 16 +- Source/VCGeneration/LoopExtractor.cs | 32 +-- Source/VCGeneration/SmokeTester.cs | 12 +- Source/VCGeneration/Splits/Focus.cs | 2 +- .../VCGeneration/Splits/ManualSplitFinder.cs | 210 ++++++++++-------- Source/VCGeneration/Splits/Split.cs | 6 +- Source/VCGeneration/StratifiedVC.cs | 2 +- .../VerificationConditionGenerator.cs | 100 ++++----- 35 files changed, 336 insertions(+), 306 deletions(-) diff --git a/Source/AbstractInterpretation/NativeLattice.cs b/Source/AbstractInterpretation/NativeLattice.cs index 1c5e4db9e..2efc1f0bf 100644 --- a/Source/AbstractInterpretation/NativeLattice.cs +++ b/Source/AbstractInterpretation/NativeLattice.cs @@ -258,7 +258,7 @@ public void Analyze(Implementation impl, NativeLattice lattice, NativeLattice.El if (g != null) { // if g==null, it's a pity we didn't pay attention to that earlier, because then we could have skipped analyzing the code in this block - foreach (Block succ in g.labelTargets) + foreach (Block succ in g.LabelTargets) { workItems.Enqueue(new Tuple(succ, e)); } diff --git a/Source/AbstractInterpretation/Traverse.cs b/Source/AbstractInterpretation/Traverse.cs index 483766d3d..1a1315c51 100644 --- a/Source/AbstractInterpretation/Traverse.cs +++ b/Source/AbstractInterpretation/Traverse.cs @@ -73,9 +73,9 @@ static void Visit(Block b) // labelTargets is made non-null by Resolve, which we assume // has already called in a prior pass. - Contract.Assume(g.labelTargets != null); - cce.BeginExpose(g.labelTargets); - foreach (Block succ in g.labelTargets) + Contract.Assume(g.LabelTargets != null); + cce.BeginExpose(g.LabelTargets); + foreach (Block succ in g.LabelTargets) // invariant b.currentlyTraversed; //PM: The following loop invariant will work once properties are axiomatized //&& (g.labelNames != null && g.labelTargets != null ==> g.labelNames.Length == g.labelTargets.Length); @@ -93,7 +93,7 @@ static void Visit(Block b) //PM: The folowing assumption is needed because we cannot prove that a simple field update //PM: leaves the value of a property unchanged. - Contract.Assume(g.labelNames == null || g.labelNames.Count == g.labelTargets.Count); + Contract.Assume(g.LabelNames == null || g.LabelNames.Count == g.LabelTargets.Count); cce.EndExpose(); } else @@ -169,9 +169,9 @@ private static void DoDFSVisit(Block block, List path, List blocks GotoCmd successors = (GotoCmd) block.TransferCmd; Contract.Assert(successors != null); - if (successors.labelTargets != null) + if (successors.LabelTargets != null) { - foreach (Block nextBlock in successors.labelTargets) + foreach (Block nextBlock in successors.LabelTargets) { Contract.Assert(nextBlock != null); if (path.Contains(nextBlock)) // If the current path has already seen the block, just skip it diff --git a/Source/Concurrency/CivlCoreTypes.cs b/Source/Concurrency/CivlCoreTypes.cs index 806d0247a..bf80c2fb9 100644 --- a/Source/Concurrency/CivlCoreTypes.cs +++ b/Source/Concurrency/CivlCoreTypes.cs @@ -392,7 +392,7 @@ private List HoistAsserts(Implementation impl, ConcurrencyOptions opt else if (block.TransferCmd is GotoCmd gotoCmd) { var wlp = - HoistAsserts(block.Cmds, gotoCmd.labelTargets.SelectMany(b => wlps[b]).ToList(), options); + HoistAsserts(block.Cmds, gotoCmd.LabelTargets.SelectMany(b => wlps[b]).ToList(), options); wlps.Add(block, wlp); } else diff --git a/Source/Concurrency/CivlTypeChecker.cs b/Source/Concurrency/CivlTypeChecker.cs index 56183e37b..95f79cc51 100644 --- a/Source/Concurrency/CivlTypeChecker.cs +++ b/Source/Concurrency/CivlTypeChecker.cs @@ -217,7 +217,7 @@ private void CheckAsyncToSyncSafety(ActionDecl actionDecl, HashSet refin graph.TopologicalSort().ForEach(block => { var modifiedGlobals = block.TransferCmd is GotoCmd gotoCmd && - gotoCmd.labelTargets.Any(x => blocksLeadingToModifiedGlobals.Contains(x)); + gotoCmd.LabelTargets.Any(x => blocksLeadingToModifiedGlobals.Contains(x)); for (int i = block.Cmds.Count - 1; 0 <= i; i--) { var cmd = block.Cmds[i]; diff --git a/Source/Concurrency/CivlUtil.cs b/Source/Concurrency/CivlUtil.cs index e2f330361..ac48c1164 100644 --- a/Source/Concurrency/CivlUtil.cs +++ b/Source/Concurrency/CivlUtil.cs @@ -32,7 +32,7 @@ public void Compute() else if (block.TransferCmd is GotoCmd gotoCmd) { liveVarsBefore[block] = - Propagate(block.Cmds, gotoCmd.labelTargets.SelectMany(x => liveVarsBefore[x]).ToHashSet()); + Propagate(block.Cmds, gotoCmd.LabelTargets.SelectMany(x => liveVarsBefore[x]).ToHashSet()); } else { diff --git a/Source/Concurrency/LinearTypeChecker.cs b/Source/Concurrency/LinearTypeChecker.cs index 69e48cd62..2e3fb6aa8 100644 --- a/Source/Concurrency/LinearTypeChecker.cs +++ b/Source/Concurrency/LinearTypeChecker.cs @@ -380,7 +380,7 @@ public override Implementation VisitImplementation(Implementation node) HashSet end = PropagateAvailableLinearVarsAcrossBlock(b); if (b.TransferCmd is GotoCmd gotoCmd) { - foreach (Block target in gotoCmd.labelTargets) + foreach (Block target in gotoCmd.LabelTargets) { if (!availableLinearVars.ContainsKey(target)) { diff --git a/Source/Concurrency/TransitionRelationComputation.cs b/Source/Concurrency/TransitionRelationComputation.cs index 6a6bfbc79..9e7381ab4 100644 --- a/Source/Concurrency/TransitionRelationComputation.cs +++ b/Source/Concurrency/TransitionRelationComputation.cs @@ -148,7 +148,7 @@ private void EnumeratePathsRec(Block b, bool inFirst) else { GotoCmd gotoCmd = b.TransferCmd as GotoCmd; - foreach (Block target in gotoCmd.labelTargets) + foreach (Block target in gotoCmd.LabelTargets) { EnumeratePathsRec(target, inFirst); } diff --git a/Source/Concurrency/YieldSufficiencyTypeChecker.cs b/Source/Concurrency/YieldSufficiencyTypeChecker.cs index d96473ede..721c0b0b5 100644 --- a/Source/Concurrency/YieldSufficiencyTypeChecker.cs +++ b/Source/Concurrency/YieldSufficiencyTypeChecker.cs @@ -267,7 +267,7 @@ private void ComputeGraph() // Block exit edges if (block.TransferCmd is GotoCmd gotoCmd) { - foreach (Block successor in gotoCmd.labelTargets) + foreach (Block successor in gotoCmd.LabelTargets) { var edge = new Tuple(block.TransferCmd, successor); atomicityLabels[edge] = P; diff --git a/Source/Concurrency/YieldingProcInstrumentation.cs b/Source/Concurrency/YieldingProcInstrumentation.cs index 53d03cdd6..71a3623f4 100644 --- a/Source/Concurrency/YieldingProcInstrumentation.cs +++ b/Source/Concurrency/YieldingProcInstrumentation.cs @@ -480,7 +480,7 @@ private void DesugarConcurrency(Implementation impl, List preconditions) { var targetBlocks = new List(); var addEdge = false; - foreach (var nextBlock in gotoCmd.labelTargets) + foreach (var nextBlock in gotoCmd.LabelTargets) { if (nextBlock.cmds.Count > 0) { diff --git a/Source/Core/AST/Absy.cs b/Source/Core/AST/Absy.cs index 307f898cf..3b0366d07 100644 --- a/Source/Core/AST/Absy.cs +++ b/Source/Core/AST/Absy.cs @@ -4038,15 +4038,15 @@ public static RE Transform(Block b) GotoCmd /*!*/ g = (GotoCmd) tc; Contract.Assert(g != null); - Contract.Assume(g.labelTargets != null); - if (g.labelTargets.Count == 1) + Contract.Assume(g.LabelTargets != null); + if (g.LabelTargets.Count == 1) { - return new Sequential(new AtomicRE(b), Transform(cce.NonNull(g.labelTargets[0]))); + return new Sequential(new AtomicRE(b), Transform(cce.NonNull(g.LabelTargets[0]))); } else { List rs = new List(); - foreach (Block /*!*/ target in g.labelTargets) + foreach (Block /*!*/ target in g.LabelTargets) { Contract.Assert(target != null); RE r = Transform(target); diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 5ca6edf09..6e7f34efa 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -539,7 +539,7 @@ void CheckLegalLabels(StmtList stmtList, StmtList parentContext, BigBlock parent if (b.tc is GotoCmd) { GotoCmd g = (GotoCmd) b.tc; - foreach (string /*!*/ lbl in cce.NonNull(g.labelNames)) + foreach (string /*!*/ lbl in cce.NonNull(g.LabelNames)) { Contract.Assert(lbl != null); /* @@ -3476,13 +3476,13 @@ public override Absy StdDispatch(StandardVisitor visitor) public class GotoCmd : TransferCmd { - [Rep] public List labelNames; - [Rep] public List labelTargets; + [Rep] public List LabelNames; + [Rep] public List LabelTargets; [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(labelNames == null || labelTargets == null || labelNames.Count == labelTargets.Count); + Contract.Invariant(LabelNames == null || LabelTargets == null || LabelNames.Count == LabelTargets.Count); } [NotDelayed] @@ -3491,7 +3491,7 @@ public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq) { Contract.Requires(tok != null); Contract.Requires(labelSeq != null); - this.labelNames = labelSeq; + this.LabelNames = labelSeq; } public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq, List /*!*/ blockSeq) @@ -3506,8 +3506,8 @@ public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq, List /*!*/ Debug.Assert(Equals(labelSeq[i], cce.NonNull(blockSeq[i]).Label)); } - this.labelNames = labelSeq; - this.labelTargets = blockSeq; + this.LabelNames = labelSeq; + this.LabelTargets = blockSeq; } public GotoCmd(IToken /*!*/ tok, List /*!*/ blockSeq) @@ -3522,31 +3522,31 @@ public GotoCmd(IToken /*!*/ tok, List /*!*/ blockSeq) labelSeq.Add(cce.NonNull(blockSeq[i]).Label); } - this.labelNames = labelSeq; - this.labelTargets = blockSeq; + this.LabelNames = labelSeq; + this.LabelTargets = blockSeq; } public void RemoveTarget(Block b) { - labelNames.Remove(b.Label); - labelTargets.Remove(b); + LabelNames.Remove(b.Label); + LabelTargets.Remove(b); } public void AddTarget(Block b) { Contract.Requires(b != null); Contract.Requires(b.Label != null); - Contract.Requires(this.labelTargets != null); - Contract.Requires(this.labelNames != null); - this.labelTargets.Add(b); - this.labelNames.Add(b.Label); + Contract.Requires(this.LabelTargets != null); + Contract.Requires(this.LabelNames != null); + this.LabelTargets.Add(b); + this.LabelNames.Add(b.Label); } public void AddTargets(IEnumerable blocks) { Contract.Requires(blocks != null); Contract.Requires(cce.NonNullElements(blocks)); - Contract.Requires(this.labelTargets != null); - Contract.Requires(this.labelNames != null); + Contract.Requires(this.LabelTargets != null); + Contract.Requires(this.LabelNames != null); foreach (var block in blocks) { AddTarget(block); @@ -3556,14 +3556,14 @@ public void AddTargets(IEnumerable blocks) public override void Emit(TokenTextWriter stream, int level) { //Contract.Requires(stream != null); - Contract.Assume(this.labelNames != null); + Contract.Assume(this.LabelNames != null); stream.Write(this, level, "goto "); if (stream.Options.PrintWithUniqueASTIds) { - if (labelTargets == null) + if (LabelTargets == null) { string sep = ""; - foreach (string name in labelNames) + foreach (string name in LabelNames) { stream.Write("{0}{1}^^{2}", sep, "NoDecl", name); sep = ", "; @@ -3572,7 +3572,7 @@ public override void Emit(TokenTextWriter stream, int level) else { string sep = ""; - foreach (Block /*!*/ b in labelTargets) + foreach (Block /*!*/ b in LabelTargets) { Contract.Assert(b != null); stream.Write("{0}h{1}^^{2}", sep, b.GetHashCode(), b.Label); @@ -3582,7 +3582,7 @@ public override void Emit(TokenTextWriter stream, int level) } else { - labelNames.Emit(stream); + LabelNames.Emit(stream); } stream.WriteLine(";"); @@ -3591,16 +3591,16 @@ public override void Emit(TokenTextWriter stream, int level) public override void Resolve(ResolutionContext rc) { //Contract.Requires(rc != null); - Contract.Ensures(labelTargets != null); - if (labelTargets != null) + Contract.Ensures(LabelTargets != null); + if (LabelTargets != null) { // already resolved return; } - Contract.Assume(this.labelNames != null); - labelTargets = new List(); - foreach (string /*!*/ lbl in labelNames) + Contract.Assume(this.LabelNames != null); + LabelTargets = new List(); + foreach (string /*!*/ lbl in LabelNames) { Contract.Assert(lbl != null); Block b = rc.LookUpBlock(lbl); @@ -3610,11 +3610,11 @@ public override void Resolve(ResolutionContext rc) } else { - labelTargets.Add(b); + LabelTargets.Add(b); } } - Debug.Assert(rc.ErrorCount > 0 || labelTargets.Count == labelNames.Count); + Debug.Assert(rc.ErrorCount > 0 || LabelTargets.Count == LabelNames.Count); } public override Absy StdDispatch(StandardVisitor visitor) diff --git a/Source/Core/AST/Block.cs b/Source/Core/AST/Block.cs index bcc077050..c6ac50740 100644 --- a/Source/Core/AST/Block.cs +++ b/Source/Core/AST/Block.cs @@ -43,7 +43,7 @@ public IEnumerable Exits() { if (TransferCmd is GotoCmd g) { - return cce.NonNull(g.labelTargets); + return cce.NonNull(g.LabelTargets); } return new List(); } diff --git a/Source/Core/AST/Implementation.cs b/Source/Core/AST/Implementation.cs index 9169f014b..6d6d2415c 100644 --- a/Source/Core/AST/Implementation.cs +++ b/Source/Core/AST/Implementation.cs @@ -922,9 +922,9 @@ override public void ResetAbstractInterpretationState() if (gotoCmd != null) { // If it is a gotoCmd - Contract.Assert(gotoCmd.labelTargets != null); + Contract.Assert(gotoCmd.LabelTargets != null); - return gotoCmd.labelTargets; + return gotoCmd.LabelTargets; } else { @@ -972,8 +972,8 @@ public static void ComputePredecessorsForBlocks(List blocks) continue; } - Contract.Assert(gtc.labelTargets != null); - foreach (var /*!*/ dest in gtc.labelTargets) + Contract.Assert(gtc.LabelTargets != null); + foreach (var /*!*/ dest in gtc.LabelTargets) { Contract.Assert(dest != null); dest.Predecessors.Add(block); @@ -1010,7 +1010,7 @@ public void PruneUnreachableBlocks(CoreOptions options) } // it seems that the goto statement at the end may be reached - foreach (var next in gotoCmd.labelTargets) { + foreach (var next in gotoCmd.LabelTargets) { Contract.Assume(next != null); toVisit.Push(next); } diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index a347a4500..45011b574 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -519,8 +519,8 @@ void AddEdge(Block a, Block b) { { if (block.TransferCmd is GotoCmd gtc) { - Contract.Assume(gtc.labelTargets != null); - gtc.labelTargets.ForEach(dest => AddEdge(block, dest)); + Contract.Assume(gtc.LabelTargets != null); + gtc.LabelTargets.ForEach(dest => AddEdge(block, dest)); } } return result; diff --git a/Source/Core/Analysis/BlockCoalescer.cs b/Source/Core/Analysis/BlockCoalescer.cs index f1065f65b..60100930f 100644 --- a/Source/Core/Analysis/BlockCoalescer.cs +++ b/Source/Core/Analysis/BlockCoalescer.cs @@ -43,12 +43,12 @@ private static HashSet ComputeMultiPredecessorBlocks(Block rootBlock) Contract.Assert(block.TransferCmd is GotoCmd); var gotoCmd = (GotoCmd) block.TransferCmd; - if (gotoCmd.labelTargets == null) + if (gotoCmd.LabelTargets == null) { continue; } - foreach (var /*!*/ succ in gotoCmd.labelTargets) + foreach (var /*!*/ succ in gotoCmd.LabelTargets) { Contract.Assert(succ != null); dfsStack.Push(succ); @@ -70,10 +70,10 @@ public override Implementation VisitImplementation(Implementation impl) } var gotoCmd = (GotoCmd)block.TransferCmd; - gotoCmd.labelNames = new List(); - foreach (var successor in gotoCmd.labelTargets) + gotoCmd.LabelNames = new List(); + foreach (var successor in gotoCmd.LabelTargets) { - gotoCmd.labelNames.Add(successor.Label); + gotoCmd.LabelNames.Add(successor.Label); } } return impl; @@ -112,14 +112,14 @@ public static List CoalesceFromRootBlock(List blocks) Contract.Assert(block.TransferCmd is GotoCmd); var gotoCmd = (GotoCmd) block.TransferCmd; - if (gotoCmd.labelTargets == null) + if (gotoCmd.LabelTargets == null) { continue; } - if (gotoCmd.labelTargets.Count != 1) + if (gotoCmd.LabelTargets.Count != 1) { - foreach (var aSuccessor in gotoCmd.labelTargets) + foreach (var aSuccessor in gotoCmd.LabelTargets) { Contract.Assert(aSuccessor != null); toVisit.Push(aSuccessor); @@ -127,7 +127,7 @@ public static List CoalesceFromRootBlock(List blocks) continue; } - var successor = cce.NonNull(gotoCmd.labelTargets[0]); + var successor = cce.NonNull(gotoCmd.LabelTargets[0]); if (multiPredecessorBlocks.Contains(successor)) { toVisit.Push(successor); diff --git a/Source/Core/Analysis/LiveVariableAnalysis/ImplementationControlFlowGraph.cs b/Source/Core/Analysis/LiveVariableAnalysis/ImplementationControlFlowGraph.cs index 9845609b0..5f220efea 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/ImplementationControlFlowGraph.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/ImplementationControlFlowGraph.cs @@ -116,8 +116,8 @@ private void Initialize(Implementation impl) { GotoCmd gc = b.TransferCmd as GotoCmd; Contract.Assert(gc != null); - Contract.Assert(gc.labelTargets != null); - foreach (Block /*!*/ t in gc.labelTargets) + Contract.Assert(gc.LabelTargets != null); + foreach (Block /*!*/ t in gc.LabelTargets) { Contract.Assert(t != null); addEdge(b, t); diff --git a/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs b/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs index 316cd11f2..bcdbbf517 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs @@ -54,9 +54,9 @@ public void ComputeLiveVariables(Implementation impl) if (block.TransferCmd is GotoCmd) { GotoCmd gotoCmd = (GotoCmd) block.TransferCmd; - if (gotoCmd.labelTargets != null) + if (gotoCmd.LabelTargets != null) { - foreach (Block /*!*/ succ in gotoCmd.labelTargets) + foreach (Block /*!*/ succ in gotoCmd.LabelTargets) { Contract.Assert(succ != null); Contract.Assert(succ.liveVarsBefore != null); diff --git a/Source/Core/Duplicator.cs b/Source/Core/Duplicator.cs index a4b058c39..75ca3f65c 100644 --- a/Source/Core/Duplicator.cs +++ b/Source/Core/Duplicator.cs @@ -122,13 +122,13 @@ public override Expr VisitCodeExpr(CodeExpr node) if (g != null) { List targets = new List(); - foreach (Block t in cce.NonNull(g.labelTargets)) + foreach (Block t in cce.NonNull(g.LabelTargets)) { Block nt = subst[t]; targets.Add(nt); } - g.labelTargets = targets; + g.LabelTargets = targets; } } @@ -378,15 +378,15 @@ public override Implementation VisitImplementation(Implementation node) { var newLabelTargets = new List(); var newLabelNames = new List(); - for (int index = 0; index < gotoCmd.labelTargets.Count; ++index) + for (int index = 0; index < gotoCmd.LabelTargets.Count; ++index) { - var newBlock = blockDuplicationMapping[gotoCmd.labelTargets[index]]; + var newBlock = blockDuplicationMapping[gotoCmd.LabelTargets[index]]; newLabelTargets.Add(newBlock); newLabelNames.Add(newBlock.Label); } - gotoCmd.labelTargets = newLabelTargets; - gotoCmd.labelNames = newLabelNames; + gotoCmd.LabelTargets = newLabelTargets; + gotoCmd.LabelNames = newLabelNames; } if (impl.Proc is YieldProcedureDecl yieldProcedureDecl) diff --git a/Source/Core/Inline.cs b/Source/Core/Inline.cs index f5fc11126..3162cddbe 100644 --- a/Source/Core/Inline.cs +++ b/Source/Core/Inline.cs @@ -742,7 +742,7 @@ protected TransferCmd CreateInlinedTransferCmd(TransferCmd transferCmd, string p GotoCmd gotoCmd = transferCmd as GotoCmd; if (gotoCmd != null) { - List gotoSeq = gotoCmd.labelNames; + List gotoSeq = gotoCmd.LabelNames; List newGotoSeq = new List(); foreach (string /*!*/ blockLabel in cce.NonNull(gotoSeq)) { @@ -838,9 +838,9 @@ public TransferCmd CopyTransferCmd(TransferCmd cmd) Contract.Ensures(Contract.Result() != null); if (cmd is GotoCmd gotoCmd) { - Contract.Assert(gotoCmd.labelNames != null); + Contract.Assert(gotoCmd.LabelNames != null); List labels = new List(); - labels.AddRange(gotoCmd.labelNames); + labels.AddRange(gotoCmd.LabelNames); return new GotoCmd(cmd.tok, labels); } else if (cmd is ReturnExprCmd returnExprCmd) diff --git a/Source/Core/InterProceduralReachabilityGraph.cs b/Source/Core/InterProceduralReachabilityGraph.cs index d748fd757..81eb226cf 100644 --- a/Source/Core/InterProceduralReachabilityGraph.cs +++ b/Source/Core/InterProceduralReachabilityGraph.cs @@ -54,7 +54,7 @@ public InterproceduralReachabilityGraph(Program prog, CoreOptions options) GotoCmd gotoCmd = n.TransferCmd as GotoCmd; if (gotoCmd != null) { - foreach (Block b in gotoCmd.labelTargets) + foreach (Block b in gotoCmd.LabelTargets) { reachabilityGraph.AddEdge(n, b); } @@ -90,14 +90,14 @@ private void AddCallAndReturnEdges() GotoCmd gotoCmd = n.TransferCmd as GotoCmd; Debug.Assert(gotoCmd != null); - for (int i = 0; i < gotoCmd.labelTargets.Count; i++) + for (int i = 0; i < gotoCmd.LabelTargets.Count; i++) { - (newProcedureExitNodes[proc].TransferCmd as GotoCmd).labelTargets.Add(gotoCmd.labelTargets[i]); - (newProcedureExitNodes[proc].TransferCmd as GotoCmd).labelNames.Add(gotoCmd.labelNames[i]); + (newProcedureExitNodes[proc].TransferCmd as GotoCmd).LabelTargets.Add(gotoCmd.LabelTargets[i]); + (newProcedureExitNodes[proc].TransferCmd as GotoCmd).LabelNames.Add(gotoCmd.LabelNames[i]); } - gotoCmd.labelTargets = new List {newProcedureEntryNodes[proc]}; - gotoCmd.labelNames = new List {newProcedureEntryNodes[proc].Label}; + gotoCmd.LabelTargets = new List {newProcedureEntryNodes[proc]}; + gotoCmd.LabelNames = new List {newProcedureEntryNodes[proc].Label}; } } @@ -114,7 +114,7 @@ private void PatchUpGotoTargets() if (gotoCmd != null) { List newTargets = new List(); - foreach (Block t in gotoCmd.labelTargets) + foreach (Block t in gotoCmd.LabelTargets) { if (originalToNew.ContainsKey(t)) { @@ -126,7 +126,7 @@ private void PatchUpGotoTargets() } } - gotoCmd.labelTargets = newTargets; + gotoCmd.LabelTargets = newTargets; } } @@ -195,7 +195,7 @@ private void ProcessImplementations() Debug.Assert(prev != null); if (b.TransferCmd is ReturnCmd || (b.TransferCmd is GotoCmd && - ((GotoCmd) b.TransferCmd).labelTargets.Count == 0)) + ((GotoCmd) b.TransferCmd).LabelTargets.Count == 0)) { prev.TransferCmd = new GotoCmd(Token.NoToken, new List {exitLabel}, new List {newExit}); } @@ -209,7 +209,7 @@ private void ProcessImplementations() { var gotoCmd = b.TransferCmd as GotoCmd; Debug.Assert(gotoCmd != null); - prev.TransferCmd = new GotoCmd(gotoCmd.tok, gotoCmd.labelNames, gotoCmd.labelTargets); + prev.TransferCmd = new GotoCmd(gotoCmd.tok, gotoCmd.LabelNames, gotoCmd.LabelTargets); } } } @@ -329,7 +329,7 @@ public void dump() { options.OutputWriter.WriteLine(n.Label + " -> {"); GotoCmd gotoCmd = n.TransferCmd as GotoCmd; - foreach (Block m in gotoCmd.labelTargets) + foreach (Block m in gotoCmd.LabelTargets) { options.OutputWriter.WriteLine(" " + m.Label); } diff --git a/Source/Core/LoopUnroll.cs b/Source/Core/LoopUnroll.cs index 1c9b95897..6135db8e9 100644 --- a/Source/Core/LoopUnroll.cs +++ b/Source/Core/LoopUnroll.cs @@ -184,8 +184,8 @@ public static GraphNode ComputeGraphInfo(GraphNode from, Block b, GotoCmd gcmd = b.TransferCmd as GotoCmd; if (gcmd != null) { - Contract.Assume(gcmd.labelTargets != null); - foreach (Block /*!*/ succ in gcmd.labelTargets) + Contract.Assume(gcmd.LabelTargets != null); + foreach (Block /*!*/ succ in gcmd.LabelTargets) { Contract.Assert(succ != null); ComputeGraphInfo(g, succ, gd, beingVisited); diff --git a/Source/Core/Monomorphization.cs b/Source/Core/Monomorphization.cs index 4c5b2c36d..deff3bfe3 100644 --- a/Source/Core/Monomorphization.cs +++ b/Source/Core/Monomorphization.cs @@ -1723,8 +1723,8 @@ public Implementation InstantiateImplementation(Implementation impl, List { if (block.TransferCmd is GotoCmd gotoCmd) { - gotoCmd.labelTargets = gotoCmd.labelTargets.Select(target => blockMapping[target]).ToList(); - gotoCmd.labelNames = new List(gotoCmd.labelNames); + gotoCmd.LabelTargets = gotoCmd.LabelTargets.Select(target => blockMapping[target]).ToList(); + gotoCmd.LabelNames = new List(gotoCmd.LabelNames); } }); var instantiatedImpl = new Implementation(impl.tok, MkInstanceName(impl.Name, actualTypeParams), diff --git a/Source/Core/VariableDependenceAnalyser.cs b/Source/Core/VariableDependenceAnalyser.cs index 502c50cec..105b7811a 100644 --- a/Source/Core/VariableDependenceAnalyser.cs +++ b/Source/Core/VariableDependenceAnalyser.cs @@ -418,9 +418,9 @@ private HashSet GetControlDependencyVariables(string proc, B HashSet result = new HashSet(); var gotoCmd = b.TransferCmd as GotoCmd; - if (gotoCmd != null && gotoCmd.labelTargets.Count >= 2) + if (gotoCmd != null && gotoCmd.LabelTargets.Count >= 2) { - foreach (Block succ in gotoCmd.labelTargets) + foreach (Block succ in gotoCmd.LabelTargets) { foreach (Cmd c in succ.Cmds) { diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index c83cd9ece..70db7553a 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Linq; using System.Collections.Generic; @@ -421,7 +422,8 @@ public Node LeastCommonAncestor(Node n1, Node n2) } } - public class Graph + public class Graph + where Node : class { private HashSet> edges; private HashSet nodes; @@ -652,21 +654,27 @@ public Dictionary> DominatorsFast() return dominators; } - // Use this method only for DAGs because it uses DominatorsFast() for computing dominators - public Dictionary ImmediateDominator() + /// + /// Use this method only for DAGs because it uses DominatorsFast() for computing dominators + /// + public Dictionary ImmediateDominator() { - List topoSorted = this.TopologicalSort().ToList(); - Dictionary> dominators = DominatorsFast(); - var immediateDominator = new Dictionary(); - foreach (var u in this.Nodes) + var topoSorted = TopologicalSort().ToList(); + var indexPerNode = new Dictionary(); + for (int index = 0; index < topoSorted.Count; index++) { + indexPerNode[topoSorted[index]] = index; + } + var dominators = DominatorsFast(); + var immediateDominator = new Dictionary(); + foreach (var node in Nodes) { - if (dominators[u].Count() > 1) + if (dominators[node].Count > 1) { - dominators[u].Remove(u); + dominators[node].Remove(node); } - immediateDominator[u] = topoSorted.ElementAt(dominators[u].Max(e => topoSorted.IndexOf(e))); + immediateDominator[node] = topoSorted.ElementAt(dominators[node].Max(e => indexPerNode[e])); } - immediateDominator[this.source] = this.source; + immediateDominator[source] = null; return immediateDominator; } diff --git a/Source/Provers/LeanAuto/LeanAutoGenerator.cs b/Source/Provers/LeanAuto/LeanAutoGenerator.cs index 7bd3c533f..dde6fa6d1 100644 --- a/Source/Provers/LeanAuto/LeanAutoGenerator.cs +++ b/Source/Provers/LeanAuto/LeanAutoGenerator.cs @@ -197,9 +197,9 @@ public override Cmd VisitAssumeCmd(AssumeCmd node) public override GotoCmd VisitGotoCmd(GotoCmd node) { - string cmd = node.labelTargets.Any() + string cmd = node.LabelTargets.Any() ? node - .labelTargets + .LabelTargets .Select(l => $"goto {BlockName(l)}") .Aggregate((a, b) => $"{a} {AndString} {b}") : "ret"; diff --git a/Source/UnitTests/CoreTests/Duplicator.cs b/Source/UnitTests/CoreTests/Duplicator.cs index 38136e6cd..6e088773a 100644 --- a/Source/UnitTests/CoreTests/Duplicator.cs +++ b/Source/UnitTests/CoreTests/Duplicator.cs @@ -143,13 +143,13 @@ procedure main() var main = p.TopLevelDeclarations.OfType().Where(x => x.Name == "main").First(); // Access blocks via their labels of gotocmds - var oldEntryBlock = (main.Blocks[1].TransferCmd as GotoCmd).labelTargets[0]; + var oldEntryBlock = (main.Blocks[1].TransferCmd as GotoCmd).LabelTargets[0]; Assert.AreEqual("entry", oldEntryBlock.Label); - var oldThing1Block = (main.Blocks[1].TransferCmd as GotoCmd).labelTargets[1]; + var oldThing1Block = (main.Blocks[1].TransferCmd as GotoCmd).LabelTargets[1]; Assert.AreEqual("thing1", oldThing1Block.Label); - var oldThing2Block = (main.Blocks[0].TransferCmd as GotoCmd).labelTargets[1]; + var oldThing2Block = (main.Blocks[0].TransferCmd as GotoCmd).LabelTargets[1]; Assert.AreEqual("thing2", oldThing2Block.Label); // Now duplicate @@ -173,15 +173,15 @@ procedure main() var newEntryGotoCmd = newEntryBlock.TransferCmd as GotoCmd; var newthing1GotoCmd = newThing1Block.TransferCmd as GotoCmd; - Assert.AreNotSame(newEntryGotoCmd.labelTargets[0], oldThing1Block); - Assert.AreSame(newEntryGotoCmd.labelTargets[0], newThing1Block); - Assert.AreNotSame(newEntryGotoCmd.labelTargets[1], oldThing2Block); - Assert.AreSame(newEntryGotoCmd.labelTargets[1], newThing2Block); + Assert.AreNotSame(newEntryGotoCmd.LabelTargets[0], oldThing1Block); + Assert.AreSame(newEntryGotoCmd.LabelTargets[0], newThing1Block); + Assert.AreNotSame(newEntryGotoCmd.LabelTargets[1], oldThing2Block); + Assert.AreSame(newEntryGotoCmd.LabelTargets[1], newThing2Block); - Assert.AreNotSame(newthing1GotoCmd.labelTargets[0], oldEntryBlock); - Assert.AreSame(newthing1GotoCmd.labelTargets[0], newEntryBlock); - Assert.AreNotSame(newthing1GotoCmd.labelTargets[1], oldThing1Block); - Assert.AreSame(newthing1GotoCmd.labelTargets[1], newThing1Block); + Assert.AreNotSame(newthing1GotoCmd.LabelTargets[0], oldEntryBlock); + Assert.AreSame(newthing1GotoCmd.LabelTargets[0], newEntryBlock); + Assert.AreNotSame(newthing1GotoCmd.LabelTargets[1], oldThing1Block); + Assert.AreSame(newthing1GotoCmd.LabelTargets[1], newThing1Block); } [Test()] diff --git a/Source/VCGeneration/BlockTransformations.cs b/Source/VCGeneration/BlockTransformations.cs index b6472e319..26bebf84e 100644 --- a/Source/VCGeneration/BlockTransformations.cs +++ b/Source/VCGeneration/BlockTransformations.cs @@ -42,7 +42,7 @@ private static void StopControlFlowAtAssumeFalse(Block block) } block.TransferCmd = new ReturnCmd(block.tok); - foreach (var target in gotoCmd.labelTargets) { + foreach (var target in gotoCmd.LabelTargets) { target.Predecessors.Remove(block); } } @@ -68,13 +68,13 @@ public static void DeleteBlocksNotLeadingToAssertions(List blocks) if (currentBlock.TransferCmd is GotoCmd exit) { if (pop) { - var gtc = new GotoCmd(exit.tok, exit.labelTargets.Where(l => interestingBlocks.Contains(l)).ToList()); + var gtc = new GotoCmd(exit.tok, exit.LabelTargets.Where(l => interestingBlocks.Contains(l)).ToList()); currentBlock.TransferCmd = gtc; - interesting = gtc.labelTargets.Count != 0; + interesting = gtc.LabelTargets.Count != 0; } else { - exit.labelTargets.ForEach(b => todo.Push(b)); + exit.LabelTargets.ForEach(b => todo.Push(b)); } } if (pop) @@ -115,7 +115,7 @@ private static void DeleteUselessBlocks(List blocks) { continue; } - var isBranchingBlock = block.TransferCmd is GotoCmd gotoCmd1 && gotoCmd1.labelTargets.Count > 1 && + var isBranchingBlock = block.TransferCmd is GotoCmd gotoCmd1 && gotoCmd1.LabelTargets.Count > 1 && block.Predecessors.Count != 1; if (isBranchingBlock) { continue; @@ -125,7 +125,7 @@ private static void DeleteUselessBlocks(List blocks) { blocks.Remove(block); var noPredecessors = !block.Predecessors.Any(); - var noSuccessors = block.TransferCmd is not GotoCmd outGoto2 || !outGoto2.labelTargets.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); @@ -138,7 +138,7 @@ private static void DeleteUselessBlocks(List blocks) { continue; } - foreach (var outBlock in outGoto.labelTargets) { + foreach (var outBlock in outGoto.LabelTargets) { outBlock.Predecessors.Remove(block); if (noPredecessors) { toVisit.Add(outBlock); @@ -147,8 +147,8 @@ private static void DeleteUselessBlocks(List blocks) { foreach (var predecessor in block.Predecessors) { var intoCmd = (GotoCmd)predecessor.TransferCmd; - foreach (var outBlock in outGoto.labelTargets) { - if (!intoCmd.labelTargets.Contains(outBlock)) { + foreach (var outBlock in outGoto.LabelTargets) { + if (!intoCmd.LabelTargets.Contains(outBlock)) { intoCmd.AddTarget(outBlock); outBlock.Predecessors.Add(predecessor); } diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index fb149d34e..6846a7707 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -921,7 +921,7 @@ protected Dictionary ConvertBlocks2PassiveCmd(TextWriter traceWr else { // incarnationMap needs to be added only if there is some successor of b - b.succCount = gotoCmd.labelNames.Count; + b.succCount = gotoCmd.LabelNames.Count; block2Incarnation.Add(b, incarnationMap); } @@ -1539,14 +1539,14 @@ protected Block CreateBlockBetween(int predIndex, Block succ) #region Change the edge "pred->succ" to "pred->newBlock" GotoCmd gtc = (GotoCmd) cce.NonNull(pred.TransferCmd); - Contract.Assume(gtc.labelTargets != null); - Contract.Assume(gtc.labelNames != null); - for (int i = 0, n = gtc.labelTargets.Count; i < n; i++) + Contract.Assume(gtc.LabelTargets != null); + Contract.Assume(gtc.LabelNames != null); + for (int i = 0, n = gtc.LabelTargets.Count; i < n; i++) { - if (gtc.labelTargets[i] == succ) + if (gtc.LabelTargets[i] == succ) { - gtc.labelTargets[i] = newBlock; - gtc.labelNames[i] = newBlockLabel; + gtc.LabelTargets[i] = newBlock; + gtc.LabelNames[i] = newBlockLabel; break; } } @@ -1575,7 +1575,7 @@ protected void AddBlocksBetween(List blocks) for (int i = 0; i < nPreds; i++) { GotoCmd gotocmd = (GotoCmd) (cce.NonNull(b.Predecessors[i]).TransferCmd); - if (gotocmd.labelNames != null && gotocmd.labelNames.Count > 1) + if (gotocmd.LabelNames != null && gotocmd.LabelNames.Count > 1) { tweens.Add(CreateBlockBetween(i, b)); } diff --git a/Source/VCGeneration/LoopExtractor.cs b/Source/VCGeneration/LoopExtractor.cs index 5966f7498..0aa72bf9d 100644 --- a/Source/VCGeneration/LoopExtractor.cs +++ b/Source/VCGeneration/LoopExtractor.cs @@ -347,11 +347,11 @@ static void CreateProceduresForLoops(CoreOptions options, Implementation impl, G if (detLoopExtract) { GotoCmd auxGotoCmd = block.TransferCmd as GotoCmd; - Contract.Assert(auxGotoCmd != null && auxGotoCmd.labelNames != null && - auxGotoCmd.labelTargets != null && auxGotoCmd.labelTargets.Count >= 1); + Contract.Assert(auxGotoCmd != null && auxGotoCmd.LabelNames != null && + auxGotoCmd.LabelTargets != null && auxGotoCmd.LabelTargets.Count >= 1); //BUGFIX on 10/26/15: this contains nodes present in NaturalLoops for a different backedgenode var loopNodes = GetBlocksInAllNaturalLoops(options, header, g); //var loopNodes = g.NaturalLoops(header, source); - foreach (var bl in auxGotoCmd.labelTargets) + foreach (var bl in auxGotoCmd.LabelTargets) { if (g.Nodes.Contains(bl) && //newly created blocks are not present in NaturalLoop(header, xx, g) !loopNodes.Contains(bl)) @@ -418,27 +418,27 @@ static void CreateProceduresForLoops(CoreOptions options, Implementation impl, G dummyBlocks.Add(block1.Label); GotoCmd gotoCmd = source.TransferCmd as GotoCmd; - Contract.Assert(gotoCmd != null && gotoCmd.labelNames != null && gotoCmd.labelTargets != null && - gotoCmd.labelTargets.Count >= 1); + Contract.Assert(gotoCmd != null && gotoCmd.LabelNames != null && gotoCmd.LabelTargets != null && + gotoCmd.LabelTargets.Count >= 1); List /*!*/ newLabels = new List(); List /*!*/ newTargets = new List(); - for (int i = 0; i < gotoCmd.labelTargets.Count; i++) + for (int i = 0; i < gotoCmd.LabelTargets.Count; i++) { - if (gotoCmd.labelTargets[i] == header) + if (gotoCmd.LabelTargets[i] == header) { continue; } - newTargets.Add(gotoCmd.labelTargets[i]); - newLabels.Add(gotoCmd.labelNames[i]); + newTargets.Add(gotoCmd.LabelTargets[i]); + newLabels.Add(gotoCmd.LabelNames[i]); } newTargets.Add(block1); newLabels.Add(block1.Label); - gotoCmd.labelNames = newLabels; - gotoCmd.labelTargets = newTargets; + gotoCmd.LabelNames = newLabels; + gotoCmd.LabelTargets = newTargets; blockMap[block1] = block2; } @@ -479,15 +479,15 @@ static void CreateProceduresForLoops(CoreOptions options, Implementation impl, G } else { - Contract.Assume(gotoCmd.labelNames != null && gotoCmd.labelTargets != null); + Contract.Assume(gotoCmd.LabelNames != null && gotoCmd.LabelTargets != null); List newLabels = new List(); List newTargets = new List(); - for (int i = 0; i < gotoCmd.labelTargets.Count; i++) + for (int i = 0; i < gotoCmd.LabelTargets.Count; i++) { - Block target = gotoCmd.labelTargets[i]; + Block target = gotoCmd.LabelTargets[i]; if (blockMap.ContainsKey(target)) { - newLabels.Add(gotoCmd.labelNames[i]); + newLabels.Add(gotoCmd.LabelNames[i]); newTargets.Add(blockMap[target]); } } @@ -613,7 +613,7 @@ private static HashSet GetBreakBlocksOfLoop(CoreOptions options, Block he continue; } - foreach (var bl in auxCmd.labelTargets) + foreach (var bl in auxCmd.LabelTargets) { if (loopBlocks.Contains(bl)) { diff --git a/Source/VCGeneration/SmokeTester.cs b/Source/VCGeneration/SmokeTester.cs index 340dfff58..3f49e5207 100644 --- a/Source/VCGeneration/SmokeTester.cs +++ b/Source/VCGeneration/SmokeTester.cs @@ -90,7 +90,7 @@ Block CloneBlock(Block b) copies[b] = res; if (b.TransferCmd is GotoCmd) { - foreach (Block ch in cce.NonNull((GotoCmd) b.TransferCmd).labelTargets) + foreach (Block ch in cce.NonNull((GotoCmd) b.TransferCmd).LabelTargets) { Contract.Assert(ch != null); CloneBlock(ch); @@ -167,7 +167,7 @@ List GetCopiedBlocks() { GotoCmd copy = new GotoCmd(go.tok, new List(), new List()); kv.Value.TransferCmd = copy; - foreach (Block b in cce.NonNull(go.labelTargets)) + foreach (Block b in cce.NonNull(go.LabelTargets)) { Contract.Assert(b != null); if (copies.TryGetValue(b, out var c)) @@ -426,9 +426,9 @@ async Task DepthFirstSearch(TextWriter traceWriter, Block cur) GotoCmd go = cur.TransferCmd as GotoCmd; ReturnCmd ret = cur.TransferCmd as ReturnCmd; - Contract.Assume(!(go != null && go.labelTargets == null && go.labelNames != null && go.labelNames.Count > 0)); + Contract.Assume(!(go != null && go.LabelTargets == null && go.LabelNames != null && go.LabelNames.Count > 0)); - if (ret != null || (go != null && cce.NonNull(go.labelTargets).Count == 0)) + if (ret != null || (go != null && cce.NonNull(go.LabelTargets).Count == 0)) { // we end in return, so there will be no more places to check await CheckUnreachable(traceWriter, cur, seq); @@ -438,7 +438,7 @@ async Task DepthFirstSearch(TextWriter traceWriter, Block cur) bool needToCheck = true; // if all of our children have more than one parent, then // we're in the right place to check - foreach (Block target in cce.NonNull(go.labelTargets)) + foreach (Block target in cce.NonNull(go.LabelTargets)) { Contract.Assert(target != null); if (target.Predecessors.Count == 1) @@ -452,7 +452,7 @@ async Task DepthFirstSearch(TextWriter traceWriter, Block cur) await CheckUnreachable(traceWriter, cur, seq); } - foreach (Block target in go.labelTargets) + foreach (Block target in go.LabelTargets) { Contract.Assert(target != null); await DepthFirstSearch(traceWriter, target); diff --git a/Source/VCGeneration/Splits/Focus.cs b/Source/VCGeneration/Splits/Focus.cs index 268e85418..785a5b067 100644 --- a/Source/VCGeneration/Splits/Focus.cs +++ b/Source/VCGeneration/Splits/Focus.cs @@ -107,7 +107,7 @@ private static List ComputeNewBlocks(VCGenOptions options, ISet bl } if (block.TransferCmd is GotoCmd gtc) { - var targets = gtc.labelTargets.Where(blocksToInclude.Contains).ToList(); + var targets = gtc.LabelTargets.Where(blocksToInclude.Contains).ToList(); newBlock.TransferCmd = new GotoCmd(gtc.tok, targets.Select(blk => oldToNewBlockMap[blk].Label).ToList(), targets.Select(blk => oldToNewBlockMap[blk]).ToList()); diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 589381bc3..734b51423 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -15,60 +15,67 @@ public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { var paths = Focus.SplitOnFocus(options, run, createSplit); - return paths.SelectMany(SplitOnAssertions); + return paths.SelectMany(GetVcsForSplits); } - private static List SplitOnAssertions(ManualSplit initialSplit) { + private static List GetVcsForSplits(ManualSplit partToSplit) { - var splitOnEveryAssert = initialSplit.Options.VcsSplitOnEveryAssert; - initialSplit.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); + var splitOnEveryAssert = partToSplit.Options.VcsSplitOnEveryAssert; + partToSplit.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); - var splitPoints = new Dictionary>(); - foreach (var block in initialSplit.Blocks) { - foreach (Cmd command in block.Cmds) { - if (ShouldSplitHere(command, splitOnEveryAssert)) { - splitPoints.GetOrCreate(block, () => new List()).Add(command.tok); + var splitsPerBlock = new Dictionary>(); + var splits = new HashSet(); + foreach (var block in partToSplit.Blocks) { + var splitsForThisBlock = new List(); + splitsPerBlock[block] = splitsForThisBlock; + foreach (var command in block.Cmds) { + if (!ShouldSplitHere(command, splitOnEveryAssert)) { + continue; } + + splits.Add(command); + splitsForThisBlock.Add(command); } } - var splits = new List(); - if (!splitPoints.Any()) { - splits.Add(initialSplit); - } else { - var entryPoint = initialSplit.Blocks[0]; - var blockAssignments = PickBlocksToVerify(initialSplit.Blocks, splitPoints); - var entryBlockHasSplit = splitPoints.ContainsKey(entryPoint); - var firstSplitBlocks = GetPostSplitVcBlocks(initialSplit.Options, initialSplit.Blocks, blockAssignments, - -1, entryPoint, !entryBlockHasSplit, splitOnEveryAssert); - if (firstSplitBlocks != null) - { - splits.Add(new ManualSplit(initialSplit.Options, () => { - BlockTransformations.Optimize(firstSplitBlocks); - return firstSplitBlocks; - }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, initialSplit.Token)); + + if (!splits.Any()) { + return new List { partToSplit }; + } + + var vcs = new List(); + var entryPoint = partToSplit.Blocks[0]; + var blockStartToSplit = GetMapFromBlockStartToSplit(partToSplit.Blocks, splitsPerBlock); + + var beforeSplitsVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, blockStartToSplit, + entryPoint, splits, null); + if (beforeSplitsVc != null) + { + vcs.Add(beforeSplitsVc); + } + foreach (var block in partToSplit.Blocks) { + var splitsForBlock = splitsPerBlock.GetValueOrDefault(block); + if (splitsForBlock == null) { + continue; } - foreach (var block in initialSplit.Blocks) { - var tokens = splitPoints.GetValueOrDefault(block); - if (tokens == null) { - continue; - } - for (int i = 0; i < tokens.Count; i++) { - var token = tokens[i]; - bool lastSplitInBlock = i == tokens.Count - 1; - var newBlocks = GetPostSplitVcBlocks(initialSplit.Options, initialSplit.Blocks, blockAssignments, i, block, lastSplitInBlock, splitOnEveryAssert); - if (newBlocks != null) - { - splits.Add(new ManualSplit(initialSplit.Options, - () => { - BlockTransformations.Optimize(newBlocks); - return newBlocks; - }, initialSplit.GotoCmdOrigins, initialSplit.parent, initialSplit.Run, token)); - } + foreach (var split in splitsForBlock) + { + var splitVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, + blockStartToSplit, block, splits, split); + if (splitVc != null) + { + vcs.Add(splitVc); } } } - return splits; + return vcs; + + ManualSplit CreateVc(IToken token, List blocks) { + return new ManualSplit(partToSplit.Options, () => { + BlockTransformations.Optimize(blocks); + return blocks; + }, partToSplit.GotoCmdOrigins, partToSplit.parent, partToSplit.Run, token); + } } private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { @@ -80,10 +87,9 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { return findBoolAttribute ?? (c is AssertCmd && splitOnEveryAssert); } - // Verify b with the last split in blockAssignments[b] - private static Dictionary PickBlocksToVerify(List blocks, Dictionary> splitPoints) { + private static Dictionary GetMapFromBlockStartToSplit(List blocks, Dictionary> splitsPerBlock) { var todo = new Stack(); - var blockAssignments = new Dictionary(); + var blockAssignments = new Dictionary(); var immediateDominator = Program.GraphFromBlocks(blocks).ImmediateDominator(); todo.Push(blocks[0]); while (todo.Count > 0) { @@ -92,30 +98,35 @@ private static Dictionary PickBlocksToVerify(List blocks, D continue; } - if (immediateDominator[currentBlock] == currentBlock) // if the currentBlock doesn't have a predecessor. + var dominator = immediateDominator[currentBlock]; + if (dominator == null) { - blockAssignments[currentBlock] = currentBlock; - } else if (splitPoints.ContainsKey(immediateDominator[currentBlock])) // if the currentBlock's dominator has a split then it will be associated with that split + blockAssignments[currentBlock] = null; + } + else if (splitsPerBlock.TryGetValue(dominator, out var splitsForDominator)) // if the currentBlock's dominator has a split then it will be associated with that split { - blockAssignments[currentBlock] = immediateDominator[currentBlock]; - } else { - Contract.Assert(blockAssignments.Keys.Contains(immediateDominator[currentBlock])); - blockAssignments[currentBlock] = blockAssignments[immediateDominator[currentBlock]]; + blockAssignments[currentBlock] = splitsForDominator.Last(); + } + else { + Contract.Assert(blockAssignments.Keys.Contains(dominator)); + blockAssignments[currentBlock] = blockAssignments[dominator]; } - if (currentBlock.TransferCmd is GotoCmd exit) { - exit.labelTargets.ForEach(blk => todo.Push(blk)); + + if (currentBlock.TransferCmd is GotoCmd gotoCmd) { + gotoCmd.LabelTargets.ForEach(block => todo.Push(block)); } } return blockAssignments; } - private static List? GetPostSplitVcBlocks(VCGenOptions options, List blocks, - Dictionary blockAssignments, int splitNumberWithinBlock, - Block blockWithSplit, bool lastSplitInBlock, bool splitOnEveryAssert) { - var newBlocks = new List(blocks.Count); + private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, + ManualSplit partToSplit, + Dictionary blockStartToSplit, Block blockWithSplit, HashSet splits, Cmd? split) + { + var newBlocks = new List(partToSplit.Blocks.Count); var assertionCount = 0; - var oldToNewBlockMap = new Dictionary(blocks.Count); // Maps original blocks to their new copies in newBlocks - foreach (var currentBlock in blocks) { + var oldToNewBlockMap = new Dictionary(newBlocks.Count); // Maps original blocks to their new copies in newBlocks + foreach (var currentBlock in partToSplit.Blocks) { var newBlock = new Block(currentBlock.tok) { Label = currentBlock.Label @@ -124,36 +135,11 @@ private static Dictionary PickBlocksToVerify(List blocks, D oldToNewBlockMap[currentBlock] = newBlock; newBlocks.Add(newBlock); if (currentBlock == blockWithSplit) { - var newCmds = new List(); - var splitCount = -1; - var verify = splitCount == splitNumberWithinBlock; - foreach (Cmd command in currentBlock.Cmds) { - if (ShouldSplitHere(command, splitOnEveryAssert)) { - splitCount++; - verify = splitCount == splitNumberWithinBlock; - } - - if (verify && BlockTransformations.IsNonTrivialAssert(command)) - { - assertionCount++; - } - newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(options, command)); - } - newBlock.Cmds = newCmds; - } else if (lastSplitInBlock && blockAssignments[currentBlock] == blockWithSplit) { - var verify = true; - var newCmds = new List(); - foreach (var command in currentBlock.Cmds) { - verify = !ShouldSplitHere(command, splitOnEveryAssert) && verify; - if (verify && BlockTransformations.IsNonTrivialAssert(command)) - { - assertionCount++; - } - newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(options, command)); - } - newBlock.Cmds = newCmds; + newBlock.Cmds = GetCommandsForBlockWithSplit(currentBlock); + } else if (blockStartToSplit[currentBlock] == split) { + newBlock.Cmds = GetCommandsForBlockImmediatelyDominatedBySplit(currentBlock); } else { - newBlock.Cmds = currentBlock.Cmds.Select(x => CommandTransformations.AssertIntoAssume(options, x)).ToList(); + newBlock.Cmds = currentBlock.Cmds.Select(x => CommandTransformations.AssertIntoAssume(partToSplit.Options, x)).ToList(); } } @@ -163,8 +149,44 @@ private static Dictionary PickBlocksToVerify(List blocks, D } // Patch the edges between the new blocks - AddBlockJumps(blocks, oldToNewBlockMap); - return newBlocks; + AddBlockJumps(partToSplit.Blocks, oldToNewBlockMap); + var vcToken = split == null ? partToSplit.Token : split.tok; + return createVc(vcToken, newBlocks); + + List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) + { + var verify = true; + var newCmds = new List(); + foreach (var command in currentBlock.Cmds) { + verify &= !splits.Contains(command); + if (verify && BlockTransformations.IsNonTrivialAssert(command)) + { + assertionCount++; + } + newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(partToSplit.Options, command)); + } + + return newCmds; + } + + List GetCommandsForBlockWithSplit(Block currentBlock) + { + var newCmds = new List(); + var verify = false; + foreach (var command in currentBlock.Cmds) { + if (splits.Contains(command)) { + verify = command == split; + } + + if (verify && BlockTransformations.IsNonTrivialAssert(command)) + { + assertionCount++; + } + newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(partToSplit.Options, command)); + } + + return newCmds; + } } private static void AddBlockJumps(List oldBlocks, Dictionary oldToNewBlockMap) @@ -177,9 +199,9 @@ private static void AddBlockJumps(List oldBlocks, Dictionary(gotoCmd.labelTargets.Count()); - var newLabelNames = new List(gotoCmd.labelTargets.Count()); - foreach (var target in gotoCmd.labelTargets) { + var newLabelTargets = new List(gotoCmd.LabelTargets.Count()); + var newLabelNames = new List(gotoCmd.LabelTargets.Count()); + foreach (var target in gotoCmd.LabelTargets) { newLabelTargets.Add(oldToNewBlockMap[target]); newLabelNames.Add(oldToNewBlockMap[target].Label); } diff --git a/Source/VCGeneration/Splits/Split.cs b/Source/VCGeneration/Splits/Split.cs index c7fd79cc8..8eb2da555 100644 --- a/Source/VCGeneration/Splits/Split.cs +++ b/Source/VCGeneration/Splits/Split.cs @@ -354,7 +354,7 @@ void ComputeBestSplit() continue; } - List targ = cce.NonNull(gt.labelTargets); + List targ = cce.NonNull(gt.LabelTargets); if (targ.Count < 2) { continue; @@ -631,7 +631,7 @@ Block CloneBlock(Block b) GotoCmd newGoto = new GotoCmd(gt.tok, new List(), new List()); res.TransferCmd = newGoto; int pos = 0; - foreach (Block ch in cce.NonNull(gt.labelTargets)) + foreach (Block ch in cce.NonNull(gt.LabelTargets)) { Contract.Assert(ch != null); Contract.Assert(doingSlice || @@ -798,7 +798,7 @@ public Counterexample ToCounterexample(ProverContext context) if (g != null) { run.OutputWriter.Write(" exits: "); - foreach (Block b in cce.NonNull(g.labelTargets)) + foreach (Block b in cce.NonNull(g.LabelTargets)) { Contract.Assert(b != null); run.OutputWriter.Write("{0} ", b.Label); diff --git a/Source/VCGeneration/StratifiedVC.cs b/Source/VCGeneration/StratifiedVC.cs index 5419e56be..f23404af0 100644 --- a/Source/VCGeneration/StratifiedVC.cs +++ b/Source/VCGeneration/StratifiedVC.cs @@ -535,7 +535,7 @@ public void GenerateVCBoolControl() if (gc != null) { VCExpr succ = VCExpressionGenerator.False; - foreach (var sb in gc.labelTargets) + foreach (var sb in gc.LabelTargets) { succ = gen.OrSimp(succ, blockToControlVar[sb]); } diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index 06602cff0..dcb338473 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -844,9 +844,9 @@ private void ConvertCFG2DAGStandard(Implementation impl, Dictionary 1) + if (gotocmd.LabelNames.Count > 1) { Block newBlock = CreateBlockBetween(predIndex, header); impl.Blocks.Add(newBlock); @@ -882,18 +882,18 @@ private void ConvertCFG2DAGStandard(Implementation impl, Dictionary 1) + if (gtc != null && gtc.LabelTargets != null && gtc.LabelTargets.Count > 1) { // then remove the backedge by removing the target block from the list of gotos List remainingTargets = new List(); List remainingLabels = new List(); - Contract.Assume(gtc.labelNames != null); - for (int i = 0, n = gtc.labelTargets.Count; i < n; i++) + Contract.Assume(gtc.LabelNames != null); + for (int i = 0, n = gtc.LabelTargets.Count; i < n; i++) { - if (gtc.labelTargets[i] != header) + if (gtc.LabelTargets[i] != header) { - remainingTargets.Add(gtc.labelTargets[i]); - remainingLabels.Add(gtc.labelNames[i]); + remainingTargets.Add(gtc.LabelTargets[i]); + remainingLabels.Add(gtc.LabelNames[i]); } else { @@ -901,8 +901,8 @@ private void ConvertCFG2DAGStandard(Implementation impl, Dictionary g, List newTargets = new List(); List newLabels = new List(); - for (int i = 0; i < gc.labelTargets.Count(); ++i) + for (int i = 0; i < gc.LabelTargets.Count(); ++i) { - if (gc.labelTargets[i] == header) + if (gc.LabelTargets[i] == header) { if (nextHeader != null) { @@ -1220,7 +1220,7 @@ private Block DuplicateLoop(Implementation impl, Graph g, nextHeader.Predecessors.Add(copy); } } - else if (ori2CopiedBlocks.TryGetValue(gc.labelTargets[i], out var newTarget)) + else if (ori2CopiedBlocks.TryGetValue(gc.LabelTargets[i], out var newTarget)) { newTargets.Add(newTarget); newLabels.Add(newTarget.Label); @@ -1228,9 +1228,9 @@ private Block DuplicateLoop(Implementation impl, Graph g, } else if (!cutExits) { - newTargets.Add(gc.labelTargets[i]); - newLabels.Add(gc.labelNames[i]); - gc.labelTargets[i].Predecessors.Add(copy); + newTargets.Add(gc.LabelTargets[i]); + newLabels.Add(gc.LabelNames[i]); + gc.LabelTargets[i].Predecessors.Add(copy); } } @@ -1455,7 +1455,7 @@ static void InstrumentWithMayUnverifiedConditions(Implementation impl, Block uni } var gotoCmd = block.TransferCmd as GotoCmd; - if (gotoCmd != null && gotoCmd.labelTargets.Any(b => !conditionOnBlockEntry.ContainsKey(b))) + if (gotoCmd != null && gotoCmd.LabelTargets.Any(b => !conditionOnBlockEntry.ContainsKey(b))) { q.Enqueue(block); continue; @@ -1466,7 +1466,7 @@ static void InstrumentWithMayUnverifiedConditions(Implementation impl, Block uni { var mayInstrs = new List(); bool noInstr = true; - foreach (var succ in gotoCmd.labelTargets) + foreach (var succ in gotoCmd.LabelTargets) { var c = conditionOnBlockEntry[succ]; if (c != null) @@ -1673,7 +1673,7 @@ private void HandleSelectiveChecking(Implementation impl) var ex = x.TransferCmd as GotoCmd; if (ex != null) { - foreach (Block e in ex.labelTargets) + foreach (Block e in ex.LabelTargets) { todo.Push(e); } @@ -1944,7 +1944,7 @@ static Counterexample TraceCounterexample( } Block foundBlock = null; - foreach (Block bb in cce.NonNull(gotoCmd.labelTargets)) + foreach (Block bb in cce.NonNull(gotoCmd.LabelTargets)) { Contract.Assert(bb != null); if (traceNodes.Contains(bb)) @@ -2045,14 +2045,14 @@ public static Counterexample AssertCmdToCloneCounterexample(VCGenOptions options { var gto = (GotoCmd)prevBlock.TransferCmd; Block nb = null; - Contract.Assert(gto.labelNames.Count == - gto.labelTargets + Contract.Assert(gto.LabelNames.Count == + gto.LabelTargets .Count); // follows from GotoCmd invariant and the fact that resolution should have made both lists non-null - for (int i = 0; i < gto.labelNames.Count; i++) + for (int i = 0; i < gto.LabelNames.Count; i++) { - if (gto.labelNames[i] == blk.Label) + if (gto.LabelNames[i] == blk.Label) { - nb = gto.labelTargets[i]; + nb = gto.LabelTargets[i]; break; } } @@ -2176,9 +2176,9 @@ VCExpr LetVC(List blocks, } else { - Contract.Assert(gotocmd.labelTargets != null); - var succCorrectVars = new List(gotocmd.labelTargets.Count); - foreach (Block successor in gotocmd.labelTargets) + Contract.Assert(gotocmd.LabelTargets != null); + var succCorrectVars = new List(gotocmd.LabelTargets.Count); + foreach (Block successor in gotocmd.LabelTargets) { Contract.Assert(successor != null); VCExpr s = blockVariables[successor]; @@ -2239,7 +2239,7 @@ VCExpr DagVC(Block block, GotoCmd gotocmd = block.TransferCmd as GotoCmd; if (gotocmd != null) { - foreach (Block successor in cce.NonNull(gotocmd.labelTargets)) + foreach (Block successor in cce.NonNull(gotocmd.LabelTargets)) { Contract.Assert(successor != null); VCExpr c = DagVC(successor, controlFlowVariableExpr, absyIds, blockEquations, proverCtxt, out var ac); @@ -2300,7 +2300,7 @@ static void RemoveEmptyBlocks(List blocks) // generate renameInfoForStartBlock GotoCmd gtc = curr.TransferCmd as GotoCmd; renameInfo[curr] = null; - if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) + if (gtc == null || gtc.LabelTargets == null || gtc.LabelTargets.Count == 0) { if (curr.Cmds.Count == 0 && curr.tok.IsValid) { @@ -2318,7 +2318,7 @@ static void RemoveEmptyBlocks(List blocks) else { HashSet successorRenameInfo = new HashSet(); - foreach (Block s in gtc.labelTargets) + foreach (Block s in gtc.LabelTargets) { if (keep.Contains(s)) { @@ -2345,12 +2345,12 @@ static void RemoveEmptyBlocks(List blocks) grey.Add(curr); stack.Push(curr); GotoCmd gtc = curr.TransferCmd as GotoCmd; - if (gtc == null || gtc.labelTargets == null || gtc.labelTargets.Count == 0) + if (gtc == null || gtc.LabelTargets == null || gtc.LabelTargets.Count == 0) { continue; } - foreach (Block s in gtc.labelTargets) + foreach (Block s in gtc.LabelTargets) { if (!visited.Contains(s)) { @@ -2377,16 +2377,16 @@ static void RemoveEmptyBlocks(List blocks) { GotoCmd pGtc = p.TransferCmd as GotoCmd; Contract.Assert(pGtc != null); - pGtc.labelTargets.Remove(b); - pGtc.labelNames.Remove(b.Label); + pGtc.LabelTargets.Remove(b); + pGtc.LabelNames.Remove(b.Label); } - if (bGtc == null || bGtc.labelTargets == null || bGtc.labelTargets.Count == 0) + if (bGtc == null || bGtc.LabelTargets == null || bGtc.LabelTargets.Count == 0) { continue; } - List successors = bGtc.labelTargets; + List successors = bGtc.LabelTargets; // Try to push token information if possible if (b.tok.IsValid && successors.Count == 1 && b != renameInfo[startBlock]) @@ -2400,10 +2400,10 @@ static void RemoveEmptyBlocks(List blocks) { GotoCmd pGtc = p.TransferCmd as GotoCmd; Contract.Assert(pGtc != null); - pGtc.labelTargets.Remove(s); - pGtc.labelNames.Remove(s.Label); - pGtc.labelTargets.Add(s); - pGtc.labelNames.Add(b.Label); + pGtc.LabelTargets.Remove(s); + pGtc.LabelNames.Remove(s.Label); + pGtc.LabelTargets.Add(s); + pGtc.LabelNames.Add(b.Label); } } @@ -2418,10 +2418,10 @@ static void RemoveEmptyBlocks(List blocks) Contract.Assert(pGtc != null); foreach (Block s in successors) { - if (!pGtc.labelTargets.Contains(s)) + if (!pGtc.LabelTargets.Contains(s)) { - pGtc.labelTargets.Add(s); - pGtc.labelNames.Add(s.Label); + pGtc.LabelTargets.Add(s); + pGtc.LabelNames.Add(s.Label); } } } From 627b42c9e26c80df8d03cae89232944ee0520e68 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 14:04:34 +0200 Subject: [PATCH 17/70] Upgrade token --- Source/VCGeneration/Splits/ManualSplitFinder.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 734b51423..00693aaab 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -148,10 +148,9 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { return null; } - // Patch the edges between the new blocks - AddBlockJumps(partToSplit.Blocks, oldToNewBlockMap); - var vcToken = split == null ? partToSplit.Token : split.tok; - return createVc(vcToken, newBlocks); + AddJumpsToNewBlocks(partToSplit.Blocks, oldToNewBlockMap); + var partToken = split == null ? partToSplit.Token : new SplitToken(split.tok, partToSplit.Token); + return createVc(new SplitToken(partToken, partToSplit.Token), newBlocks); List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) { @@ -189,7 +188,7 @@ List GetCommandsForBlockWithSplit(Block currentBlock) } } - private static void AddBlockJumps(List oldBlocks, Dictionary oldToNewBlockMap) + private static void AddJumpsToNewBlocks(List oldBlocks, Dictionary oldToNewBlockMap) { foreach (var oldBlock in oldBlocks) { var newBlock = oldToNewBlockMap[oldBlock]; @@ -209,4 +208,12 @@ private static void AddBlockJumps(List oldBlocks, Dictionary Date: Fri, 27 Sep 2024 14:14:48 +0200 Subject: [PATCH 18/70] Fixes --- Source/Core/CoreOptions.cs | 12 +++++++++++ Source/Core/Token.cs | 6 ++++++ Source/ExecutionEngine/ConsolePrinter.cs | 13 ++---------- Source/ExecutionEngine/ExecutionEngine.cs | 20 ++++++------------- .../ExecutionEngine/ExecutionEngineOptions.cs | 1 - Source/Graph/Graph.cs | 6 ++---- .../VCGeneration/Splits/ManualSplitFinder.cs | 11 +++++----- Source/VCGeneration/Splits/PathToken.cs | 3 +++ Source/VCGeneration/Splits/TokenWrapper.cs | 3 +++ 9 files changed, 39 insertions(+), 36 deletions(-) diff --git a/Source/Core/CoreOptions.cs b/Source/Core/CoreOptions.cs index d0d472ff5..549965cc8 100644 --- a/Source/Core/CoreOptions.cs +++ b/Source/Core/CoreOptions.cs @@ -3,6 +3,16 @@ namespace Microsoft.Boogie { + public static class CoreOptionsExtensions { + public static string GetFileNameForConsole(this CoreOptions options, string filename) + { + return options.UseBaseNameForFileName && !string.IsNullOrEmpty(filename) && + filename != "" + ? Path.GetFileName(filename) + : filename; + } + } + /// /// Boogie command-line options (other tools can subclass this class in order to support a /// superset of Boogie's options). @@ -10,6 +20,8 @@ namespace Microsoft.Boogie public interface CoreOptions : PrintOptions { public TextWriter OutputWriter { get; } + bool UseBaseNameForFileName { get; } + public enum TypeEncoding { Predicates, diff --git a/Source/Core/Token.cs b/Source/Core/Token.cs index 5f7a1bb1f..fbcd5cca5 100644 --- a/Source/Core/Token.cs +++ b/Source/Core/Token.cs @@ -17,6 +17,8 @@ public interface IToken : IComparable string /*!*/ val { get; set; } // token value bool IsValid { get; } + + string Render(CoreOptions options); } [Immutable] @@ -90,6 +92,10 @@ public bool IsValid get { return this._filename != null; } } + public virtual string Render(CoreOptions options) { + return $"{options.GetFileNameForConsole(filename)}({line},{col})"; + } + public int CompareTo(IToken other) { if (line != other.line) { return line.CompareTo(other.line); diff --git a/Source/ExecutionEngine/ConsolePrinter.cs b/Source/ExecutionEngine/ConsolePrinter.cs index 16649a7ab..54ffea018 100644 --- a/Source/ExecutionEngine/ConsolePrinter.cs +++ b/Source/ExecutionEngine/ConsolePrinter.cs @@ -174,19 +174,10 @@ public virtual void ReportBplError(IToken tok, string message, bool error, TextW if (category != null) { - message = string.Format("{0}: {1}", category, message); + message = $"{category}: {message}"; } - string s; - if (tok != null) - { - s = string.Format("{0}({1},{2}): {3}", ExecutionEngine.GetFileNameForConsole(Options, tok.filename), tok.line, tok.col, - message); - } - else - { - s = message; - } + var s = tok != null ? $"{tok.Render(Options)}: {message}" : message; if (error) { diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 327304aff..6d3b98b8c 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -303,7 +303,7 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut if (Options.Trace) { - Options.OutputWriter.WriteLine("Parsing " + GetFileNameForConsole(Options, bplFileName)); + Options.OutputWriter.WriteLine("Parsing " + Options.GetFileNameForConsole(bplFileName)); } } @@ -314,7 +314,7 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut Options.UseBaseNameForFileName); if (programSnippet == null || errorCount != 0) { - Options.OutputWriter.WriteLine("{0} parse errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); + Options.OutputWriter.WriteLine("{0} parse errors detected in {1}", errorCount, Options.GetFileNameForConsole(bplFileName)); okay = false; } else @@ -325,7 +325,7 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut catch (IOException e) { Options.Printer.ErrorWriteLine(Options.OutputWriter, "Error opening file \"{0}\": {1}", - GetFileNameForConsole(Options, bplFileName), e.Message); + Options.GetFileNameForConsole(bplFileName), e.Message); okay = false; } } @@ -351,14 +351,6 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut } } - internal static string GetFileNameForConsole(ExecutionEngineOptions options, string filename) - { - return options.UseBaseNameForFileName && !string.IsNullOrEmpty(filename) && - filename != "" - ? Path.GetFileName(filename) - : filename; - } - /// /// Resolves and type checks the given Boogie program. Any errors are reported to the @@ -386,7 +378,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, var errorCount = program.Resolve(Options); if (errorCount != 0) { - Options.OutputWriter.WriteLine("{0} name resolution errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); + Options.OutputWriter.WriteLine("{0} name resolution errors detected in {1}", errorCount, Options.GetFileNameForConsole(bplFileName)); return PipelineOutcome.ResolutionError; } @@ -405,7 +397,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, errorCount = program.Typecheck(Options); if (errorCount != 0) { - Options.OutputWriter.WriteLine("{0} type checking errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); + Options.OutputWriter.WriteLine("{0} type checking errors detected in {1}", errorCount, Options.GetFileNameForConsole(bplFileName)); return PipelineOutcome.TypeCheckingError; } @@ -449,7 +441,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, if (civlTypeChecker.checkingContext.ErrorCount != 0) { Options.OutputWriter.WriteLine("{0} type checking errors detected in {1}", civlTypeChecker.checkingContext.ErrorCount, - GetFileNameForConsole(Options, bplFileName)); + Options.GetFileNameForConsole(bplFileName)); return PipelineOutcome.TypeCheckingError; } diff --git a/Source/ExecutionEngine/ExecutionEngineOptions.cs b/Source/ExecutionEngine/ExecutionEngineOptions.cs index f7e4806df..baf7c48a1 100644 --- a/Source/ExecutionEngine/ExecutionEngineOptions.cs +++ b/Source/ExecutionEngine/ExecutionEngineOptions.cs @@ -22,7 +22,6 @@ public interface ExecutionEngineOptions : HoudiniOptions, ConcurrencyOptions ShowEnvironment ShowEnv { get; } string Version { get; } string Environment { get; } - bool UseBaseNameForFileName { get; } HashSet Libraries { get; set; } bool NoResolve { get; } bool NoTypecheck { get; } diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 70db7553a..780f7aa09 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -422,8 +422,7 @@ public Node LeastCommonAncestor(Node n1, Node n2) } } - public class Graph - where Node : class + public class Graph { private HashSet> edges; private HashSet nodes; @@ -657,7 +656,7 @@ public Dictionary> DominatorsFast() /// /// Use this method only for DAGs because it uses DominatorsFast() for computing dominators /// - public Dictionary ImmediateDominator() + public Dictionary ImmediateDominator() { var topoSorted = TopologicalSort().ToList(); var indexPerNode = new Dictionary(); @@ -674,7 +673,6 @@ public Dictionary> DominatorsFast() } immediateDominator[node] = topoSorted.ElementAt(dominators[node].Max(e => indexPerNode[e])); } - immediateDominator[source] = null; return immediateDominator; } diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 00693aaab..46d9096c2 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -90,7 +90,7 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { private static Dictionary GetMapFromBlockStartToSplit(List blocks, Dictionary> splitsPerBlock) { var todo = new Stack(); var blockAssignments = new Dictionary(); - var immediateDominator = Program.GraphFromBlocks(blocks).ImmediateDominator(); + var immediateDominators = Program.GraphFromBlocks(blocks).ImmediateDominator(); todo.Push(blocks[0]); while (todo.Count > 0) { var currentBlock = todo.Pop(); @@ -98,18 +98,17 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { continue; } - var dominator = immediateDominator[currentBlock]; - if (dominator == null) + if (!immediateDominators.TryGetValue(currentBlock, out var immediateDominator)) { blockAssignments[currentBlock] = null; } - else if (splitsPerBlock.TryGetValue(dominator, out var splitsForDominator)) // if the currentBlock's dominator has a split then it will be associated with that split + else if (splitsPerBlock.TryGetValue(immediateDominator, out var splitsForDominator)) // if the currentBlock's dominator has a split then it will be associated with that split { blockAssignments[currentBlock] = splitsForDominator.Last(); } else { - Contract.Assert(blockAssignments.Keys.Contains(dominator)); - blockAssignments[currentBlock] = blockAssignments[dominator]; + Contract.Assert(blockAssignments.Keys.Contains(immediateDominator)); + blockAssignments[currentBlock] = blockAssignments[immediateDominator]; } if (currentBlock.TransferCmd is GotoCmd gotoCmd) { diff --git a/Source/VCGeneration/Splits/PathToken.cs b/Source/VCGeneration/Splits/PathToken.cs index 016dc96be..c7287a877 100644 --- a/Source/VCGeneration/Splits/PathToken.cs +++ b/Source/VCGeneration/Splits/PathToken.cs @@ -12,4 +12,7 @@ public PathToken(IToken inner, ImmutableStack branches) : base(inner) { } public ImmutableStack Branches { get; } + public override string Render(CoreOptions options) { + return base.Render(options); + } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/TokenWrapper.cs b/Source/VCGeneration/Splits/TokenWrapper.cs index 0c1329b8e..b3fe2a6b9 100644 --- a/Source/VCGeneration/Splits/TokenWrapper.cs +++ b/Source/VCGeneration/Splits/TokenWrapper.cs @@ -44,4 +44,7 @@ public string val { } public bool IsValid => inner.IsValid; + public virtual string Render(CoreOptions options) { + return inner.Render(options); + } } \ No newline at end of file From d08537eb33138539bb574895f21d180af2646c0b Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 15:35:53 +0200 Subject: [PATCH 19/70] Introduce ImplementationPartToken --- Source/Core/Token.cs | 6 ---- Source/ExecutionEngine/ConsolePrinter.cs | 5 ++- Source/ExecutionEngine/ExecutionEngine.cs | 20 +++++++---- Source/VCGeneration/ManualSplit.cs | 5 +-- Source/VCGeneration/Splits/Focus.cs | 6 ++-- .../VCGeneration/Splits/ManualSplitFinder.cs | 29 +++++++++------ Source/VCGeneration/Splits/PathToken.cs | 20 ++++++++--- Source/VCGeneration/Splits/TokenWrapper.cs | 35 +++++++++---------- 8 files changed, 73 insertions(+), 53 deletions(-) diff --git a/Source/Core/Token.cs b/Source/Core/Token.cs index fbcd5cca5..5f7a1bb1f 100644 --- a/Source/Core/Token.cs +++ b/Source/Core/Token.cs @@ -17,8 +17,6 @@ public interface IToken : IComparable string /*!*/ val { get; set; } // token value bool IsValid { get; } - - string Render(CoreOptions options); } [Immutable] @@ -92,10 +90,6 @@ public bool IsValid get { return this._filename != null; } } - public virtual string Render(CoreOptions options) { - return $"{options.GetFileNameForConsole(filename)}({line},{col})"; - } - public int CompareTo(IToken other) { if (line != other.line) { return line.CompareTo(other.line); diff --git a/Source/ExecutionEngine/ConsolePrinter.cs b/Source/ExecutionEngine/ConsolePrinter.cs index 54ffea018..183a7028c 100644 --- a/Source/ExecutionEngine/ConsolePrinter.cs +++ b/Source/ExecutionEngine/ConsolePrinter.cs @@ -177,7 +177,10 @@ public virtual void ReportBplError(IToken tok, string message, bool error, TextW message = $"{category}: {message}"; } - var s = tok != null ? $"{tok.Render(Options)}: {message}" : message; + var s = tok != null + ? string.Format("{0}({1},{2}): {3}", ExecutionEngine.GetFileNameForConsole(Options, tok.filename), tok.line, + tok.col, message) + : message; if (error) { diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 6d3b98b8c..327304aff 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -303,7 +303,7 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut if (Options.Trace) { - Options.OutputWriter.WriteLine("Parsing " + Options.GetFileNameForConsole(bplFileName)); + Options.OutputWriter.WriteLine("Parsing " + GetFileNameForConsole(Options, bplFileName)); } } @@ -314,7 +314,7 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut Options.UseBaseNameForFileName); if (programSnippet == null || errorCount != 0) { - Options.OutputWriter.WriteLine("{0} parse errors detected in {1}", errorCount, Options.GetFileNameForConsole(bplFileName)); + Options.OutputWriter.WriteLine("{0} parse errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); okay = false; } else @@ -325,7 +325,7 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut catch (IOException e) { Options.Printer.ErrorWriteLine(Options.OutputWriter, "Error opening file \"{0}\": {1}", - Options.GetFileNameForConsole(bplFileName), e.Message); + GetFileNameForConsole(Options, bplFileName), e.Message); okay = false; } } @@ -351,6 +351,14 @@ public Program ParseBoogieProgram(IList fileNames, bool suppressTraceOut } } + internal static string GetFileNameForConsole(ExecutionEngineOptions options, string filename) + { + return options.UseBaseNameForFileName && !string.IsNullOrEmpty(filename) && + filename != "" + ? Path.GetFileName(filename) + : filename; + } + /// /// Resolves and type checks the given Boogie program. Any errors are reported to the @@ -378,7 +386,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, var errorCount = program.Resolve(Options); if (errorCount != 0) { - Options.OutputWriter.WriteLine("{0} name resolution errors detected in {1}", errorCount, Options.GetFileNameForConsole(bplFileName)); + Options.OutputWriter.WriteLine("{0} name resolution errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); return PipelineOutcome.ResolutionError; } @@ -397,7 +405,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, errorCount = program.Typecheck(Options); if (errorCount != 0) { - Options.OutputWriter.WriteLine("{0} type checking errors detected in {1}", errorCount, Options.GetFileNameForConsole(bplFileName)); + Options.OutputWriter.WriteLine("{0} type checking errors detected in {1}", errorCount, GetFileNameForConsole(Options, bplFileName)); return PipelineOutcome.TypeCheckingError; } @@ -441,7 +449,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, if (civlTypeChecker.checkingContext.ErrorCount != 0) { Options.OutputWriter.WriteLine("{0} type checking errors detected in {1}", civlTypeChecker.checkingContext.ErrorCount, - Options.GetFileNameForConsole(bplFileName)); + GetFileNameForConsole(Options, bplFileName)); return PipelineOutcome.TypeCheckingError; } diff --git a/Source/VCGeneration/ManualSplit.cs b/Source/VCGeneration/ManualSplit.cs index 09927d3c1..ec643c883 100644 --- a/Source/VCGeneration/ManualSplit.cs +++ b/Source/VCGeneration/ManualSplit.cs @@ -1,20 +1,21 @@ using System; using System.Collections.Generic; using Microsoft.Boogie; +using VCGeneration; namespace VC; public class ManualSplit : Split { - public IToken Token { get; } + public ImplementationPartToken Token { get; } public ManualSplit(VCGenOptions options, Func> blocks, Dictionary gotoCmdOrigins, VerificationConditionGenerator par, ImplementationRun run, - IToken token, int? randomSeed = null) + ImplementationPartToken token, int? randomSeed = null) : base(options, blocks, gotoCmdOrigins, par, run, randomSeed) { Token = token; diff --git a/Source/VCGeneration/Splits/Focus.cs b/Source/VCGeneration/Splits/Focus.cs index 785a5b067..497891198 100644 --- a/Source/VCGeneration/Splits/Focus.cs +++ b/Source/VCGeneration/Splits/Focus.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics.Contracts; using System.Linq; -using System.Text.Json.Nodes; using Microsoft.Boogie; using VC; using Block = Microsoft.Boogie.Block; @@ -22,7 +20,7 @@ public static class Focus /// We recurse twice for each focus, leading to potentially 2^N splits /// public static List SplitOnFocus(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createSplit) + Func, ManualSplit> createSplit) { var impl = run.Implementation; var dag = Program.GraphFromImpl(impl); @@ -35,7 +33,7 @@ public static List SplitOnFocus(VCGenOptions options, Implementatio focusBlocks.Reverse(); } if (!focusBlocks.Any()) { - return new List { createSplit(run.Implementation.tok, impl.Blocks) }; + return new List { createSplit(new ImplementationRootToken(run.Implementation), impl.Blocks) }; } var ancestorsPerBlock = new Dictionary>(); diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 46d9096c2..c08b60c7f 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -1,8 +1,6 @@ #nullable enable using System; -using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; using System.Diagnostics.Contracts; using System.Linq; using Microsoft.Boogie; @@ -13,7 +11,7 @@ namespace VCGeneration; public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createSplit) { + Func, ManualSplit> createSplit) { var paths = Focus.SplitOnFocus(options, run, createSplit); return paths.SelectMany(GetVcsForSplits); } @@ -70,7 +68,7 @@ private static List GetVcsForSplits(ManualSplit partToSplit) { } return vcs; - ManualSplit CreateVc(IToken token, List blocks) { + ManualSplit CreateVc(ImplementationPartToken token, List blocks) { return new ManualSplit(partToSplit.Options, () => { BlockTransformations.Optimize(blocks); return blocks; @@ -118,7 +116,7 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { return blockAssignments; } - private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, + private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, ManualSplit partToSplit, Dictionary blockStartToSplit, Block blockWithSplit, HashSet splits, Cmd? split) { @@ -149,7 +147,7 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { AddJumpsToNewBlocks(partToSplit.Blocks, oldToNewBlockMap); var partToken = split == null ? partToSplit.Token : new SplitToken(split.tok, partToSplit.Token); - return createVc(new SplitToken(partToken, partToSplit.Token), newBlocks); + return createVc(partToken, newBlocks); List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) { @@ -207,12 +205,21 @@ private static void AddJumpsToNewBlocks(List oldBlocks, Dictionary branches) : base(inner) { Branches = branches; } public ImmutableStack Branches { get; } - public override string Render(CoreOptions options) { - return base.Render(options); + + public string Render(CoreOptions options) { + return $" after passing through: [{string.Join(", ", Branches.Select(b => $"({b.line},{b.col})"))}]"; + } +} + +class ImplementationRootToken : TokenWrapper, ImplementationPartToken { + public ImplementationRootToken(Implementation implementation) : base(implementation.tok) + { + } + + public string Render(CoreOptions options) { + return ""; } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/TokenWrapper.cs b/Source/VCGeneration/Splits/TokenWrapper.cs index b3fe2a6b9..4cfd20e54 100644 --- a/Source/VCGeneration/Splits/TokenWrapper.cs +++ b/Source/VCGeneration/Splits/TokenWrapper.cs @@ -3,48 +3,45 @@ namespace VCGeneration; class TokenWrapper : IToken { - private readonly IToken inner; + public IToken Inner { get; } public TokenWrapper(IToken inner) { - this.inner = inner; + this.Inner = inner; } public int CompareTo(IToken? other) { - return inner.CompareTo(other); + return Inner.CompareTo(other); } public int kind { - get => inner.kind; - set => inner.kind = value; + get => Inner.kind; + set => Inner.kind = value; } public string filename { - get => inner.filename; - set => inner.filename = value; + get => Inner.filename; + set => Inner.filename = value; } public int pos { - get => inner.pos; - set => inner.pos = value; + get => Inner.pos; + set => Inner.pos = value; } public int col { - get => inner.col; - set => inner.col = value; + get => Inner.col; + set => Inner.col = value; } public int line { - get => inner.line; - set => inner.line = value; + get => Inner.line; + set => Inner.line = value; } public string val { - get => inner.val; - set => inner.val = value; + get => Inner.val; + set => Inner.val = value; } - public bool IsValid => inner.IsValid; - public virtual string Render(CoreOptions options) { - return inner.Render(options); - } + public bool IsValid => Inner.IsValid; } \ No newline at end of file From b46af7324674fb32cd11e7678902c9aa88b0fa15 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 15:36:11 +0200 Subject: [PATCH 20/70] Rename --- Source/ExecutionEngine/VerificationTask.cs | 4 ++-- Source/VCGeneration/ManualSplit.cs | 6 +++--- Source/VCGeneration/Splits/Focus.cs | 6 +++--- Source/VCGeneration/Splits/ManualSplitFinder.cs | 16 ++++++++-------- Source/VCGeneration/Splits/PathToken.cs | 8 ++++---- .../VCGeneration/Splits/SplitAndVerifyWorker.cs | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Source/ExecutionEngine/VerificationTask.cs b/Source/ExecutionEngine/VerificationTask.cs index 43dda6925..6b0fb5f3e 100644 --- a/Source/ExecutionEngine/VerificationTask.cs +++ b/Source/ExecutionEngine/VerificationTask.cs @@ -24,7 +24,7 @@ public class VerificationTask : IVerificationTask { public IToken ScopeToken => Split.Implementation.tok; public string ScopeId => Split.Implementation.VerboseName; - public IToken Token => Split.Token; + public IToken Token => Split.Origin; public ManualSplit Split { get; } public VerificationTask(ExecutionEngine engine, ProcessedProgram processedProgram, ManualSplit split, @@ -45,7 +45,7 @@ public VerificationTask(ExecutionEngine engine, ProcessedProgram processedProgra public IVerificationTask FromSeed(int newSeed) { var split = new ManualSplit(Split.Options, () => Split.Blocks, Split.GotoCmdOrigins, - Split.parent, Split.Run, Split.Token, newSeed); + Split.parent, Split.Run, Split.Origin, newSeed); split.SplitIndex = Split.SplitIndex; return new VerificationTask(engine, ProcessedProgram, split, modelViewInfo); } diff --git a/Source/VCGeneration/ManualSplit.cs b/Source/VCGeneration/ManualSplit.cs index ec643c883..b2cf6482f 100644 --- a/Source/VCGeneration/ManualSplit.cs +++ b/Source/VCGeneration/ManualSplit.cs @@ -8,16 +8,16 @@ namespace VC; public class ManualSplit : Split { - public ImplementationPartToken Token { get; } + public ImplementationPartOrigin Origin { get; } public ManualSplit(VCGenOptions options, Func> blocks, Dictionary gotoCmdOrigins, VerificationConditionGenerator par, ImplementationRun run, - ImplementationPartToken token, int? randomSeed = null) + ImplementationPartOrigin origin, int? randomSeed = null) : base(options, blocks, gotoCmdOrigins, par, run, randomSeed) { - Token = token; + Origin = origin; } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/Focus.cs b/Source/VCGeneration/Splits/Focus.cs index 497891198..b84a35694 100644 --- a/Source/VCGeneration/Splits/Focus.cs +++ b/Source/VCGeneration/Splits/Focus.cs @@ -20,7 +20,7 @@ public static class Focus /// We recurse twice for each focus, leading to potentially 2^N splits /// public static List SplitOnFocus(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createSplit) + Func, ManualSplit> createSplit) { var impl = run.Implementation; var dag = Program.GraphFromImpl(impl); @@ -33,7 +33,7 @@ public static List SplitOnFocus(VCGenOptions options, Implementatio focusBlocks.Reverse(); } if (!focusBlocks.Any()) { - return new List { createSplit(new ImplementationRootToken(run.Implementation), impl.Blocks) }; + return new List { createSplit(new ImplementationRootOrigin(run.Implementation), impl.Blocks) }; } var ancestorsPerBlock = new Dictionary>(); @@ -53,7 +53,7 @@ void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; if (allFocusBlocksHaveBeenProcessed) { var newBlocks = ComputeNewBlocks(options, blocksToInclude, blocksReversed, freeAssumeBlocks); - result.Add(createSplit(new PathToken(run.Implementation.tok, path), newBlocks)); + result.Add(createSplit(new PathOrigin(run.Implementation.tok, path), newBlocks)); } else { var (focusBlock, nextToken) = focusBlocks[focusIndex]; // assert b in blocks if (!blocksToInclude.Contains(focusBlock) || freeAssumeBlocks.Contains(focusBlock)) diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index c08b60c7f..9cdd29da6 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -11,7 +11,7 @@ namespace VCGeneration; public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createSplit) { + Func, ManualSplit> createSplit) { var paths = Focus.SplitOnFocus(options, run, createSplit); return paths.SelectMany(GetVcsForSplits); } @@ -68,7 +68,7 @@ private static List GetVcsForSplits(ManualSplit partToSplit) { } return vcs; - ManualSplit CreateVc(ImplementationPartToken token, List blocks) { + ManualSplit CreateVc(ImplementationPartOrigin token, List blocks) { return new ManualSplit(partToSplit.Options, () => { BlockTransformations.Optimize(blocks); return blocks; @@ -116,7 +116,7 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { return blockAssignments; } - private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, + private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, ManualSplit partToSplit, Dictionary blockStartToSplit, Block blockWithSplit, HashSet splits, Cmd? split) { @@ -146,7 +146,7 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { } AddJumpsToNewBlocks(partToSplit.Blocks, oldToNewBlockMap); - var partToken = split == null ? partToSplit.Token : new SplitToken(split.tok, partToSplit.Token); + var partToken = split == null ? partToSplit.Origin : new SplitOrigin(split.tok, partToSplit.Origin); return createVc(partToken, newBlocks); List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) @@ -207,14 +207,14 @@ private static void AddJumpsToNewBlocks(List oldBlocks, Dictionary branches) : base(inner) { + public PathOrigin(IToken inner, ImmutableStack branches) : base(inner) { Branches = branches; } @@ -19,8 +19,8 @@ public string Render(CoreOptions options) { } } -class ImplementationRootToken : TokenWrapper, ImplementationPartToken { - public ImplementationRootToken(Implementation implementation) : base(implementation.tok) +class ImplementationRootOrigin : TokenWrapper, ImplementationPartOrigin { + public ImplementationRootOrigin(Implementation implementation) : base(implementation.tok) { } diff --git a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs index f02225df4..b029b561c 100644 --- a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs @@ -145,7 +145,7 @@ 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})" : "", + split is ManualSplit manualSplit ? $" (line {manualSplit.Origin.line})" : "", 100 * provenCost / (provenCost + remainingCost))); } From 67185647c9cc7e87653a741bb165546a0d6af51e Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 23:32:02 +0200 Subject: [PATCH 21/70] Refactoring --- Source/Graph/Graph.cs | 66 ++++++++++++------- Source/VCGeneration/BlockTransformations.cs | 2 +- .../Splits/{Focus.cs => FocusApplier.cs} | 33 +++++----- .../VCGeneration/Splits/ManualSplitFinder.cs | 28 ++++---- Source/VCGeneration/Splits/PathToken.cs | 4 +- Source/VCGeneration/Splits/TokenWrapper.cs | 2 +- 6 files changed, 78 insertions(+), 57 deletions(-) rename Source/VCGeneration/Splits/{Focus.cs => FocusApplier.cs} (83%) diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 780f7aa09..982f8da44 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Collections.Generic; +using System.Collections.Immutable; using System.Text; // for StringBuilder using System.Diagnostics.Contracts; @@ -434,8 +435,8 @@ public class Graph private HashSet splitCandidates; private DomRelation dominatorMap = null; - private Dictionary> predCache = new Dictionary>(); - private Dictionary> succCache = new Dictionary>(); + private Dictionary> predCache = new(); + private Dictionary> succCache = new(); private bool predComputed; [ContractInvariantMethod] @@ -587,7 +588,7 @@ private void ComputePredSuccCaches() } } - public IEnumerable Predecessors(Node n) + public IReadOnlyCollection Predecessors(Node n) { // original A# //Set result = Set{ x : x in Nodes, Edge(x,n) }; @@ -628,29 +629,42 @@ public List SuccessorsAsList(Node n) } } - // This method gives a simpler way to compute dominators but it assmumes the graph is a DAG. - // With acyclicty we can compute all dominators by traversing the graph (once) in topological order - // (using the property: A vertex's dominator set is unaffected by vertices that come later). - // The method does not check the graph for the DAG property. That risk is on the caller. - public Dictionary> DominatorsFast() + /// + /// This method gives a simpler way to compute dominators but it assmumes the graph is a DAG. + /// With acyclicty we can compute all dominators by traversing the graph (once) in topological order + /// (using the property: A vertex's dominator set is unaffected by vertices that come later). + /// The method does not check the graph for the DAG property. That risk is on the caller. + /// + public Dictionary> AcyclicDominators() { - List topoSorted = this.TopologicalSort().ToList(); - var dominators = new Dictionary>(); - topoSorted.ForEach(u => dominators[u] = topoSorted.ToHashSet()); - var todo = new Queue(); - foreach (var u in topoSorted) + var dominatorsPerNode = new Dictionary>(); + foreach (var node in TopologicalSort()) { - var s = new HashSet(); - var predecessors = this.Predecessors(u).ToList(); - if (predecessors.Count() != 0) - { - s.UnionWith(dominators[predecessors.First()]); - predecessors.ForEach(v => s.IntersectWith(dominators[v])); + var predecessors = Predecessors(node); + var dominatorsForNode = Intersection(predecessors.Select(p => dominatorsPerNode[p])); + dominatorsForNode.Add(node); + dominatorsPerNode[node] = dominatorsForNode; + } + return dominatorsPerNode; + } + + public static HashSet Intersection(IEnumerable> sets) { + var first = true; + HashSet? result = null; + foreach (var set in sets) { + if (first) { + result = set.ToHashSet(); + first = false; + } else { + result!.IntersectWith(set); } - s.Add(u); - dominators[u] = s; } - return dominators; + + if (result == null) { + return ImmutableHashSet.Empty; + } + + return result; } /// @@ -663,8 +677,8 @@ public Dictionary ImmediateDominator() for (int index = 0; index < topoSorted.Count; index++) { indexPerNode[topoSorted[index]] = index; } - var dominators = DominatorsFast(); - var immediateDominator = new Dictionary(); + var dominators = AcyclicDominators(); + var immediateDominator = new Dictionary(); foreach (var node in Nodes) { if (dominators[node].Count > 1) @@ -673,6 +687,8 @@ public Dictionary ImmediateDominator() } immediateDominator[node] = topoSorted.ElementAt(dominators[node].Max(e => indexPerNode[e])); } + + immediateDominator.Remove(source); return immediateDominator; } @@ -699,7 +715,7 @@ public List ImmediatelyDominatedBy(Node /*!*/ n) public IEnumerable TopologicalSort(bool reversed = false) { - this.TarjanTopSort(out var acyclic, out var sortedList, reversed); + TarjanTopSort(out var acyclic, out var sortedList, reversed); return acyclic ? sortedList : new List(); } diff --git a/Source/VCGeneration/BlockTransformations.cs b/Source/VCGeneration/BlockTransformations.cs index 26bebf84e..fe6507899 100644 --- a/Source/VCGeneration/BlockTransformations.cs +++ b/Source/VCGeneration/BlockTransformations.cs @@ -97,7 +97,7 @@ private static bool ContainsAssert(Block b) return b.Cmds.Exists(IsNonTrivialAssert); } - public static bool IsNonTrivialAssert (Cmd c) { return c is AssertCmd ac && !(ac.Expr is LiteralExpr { asBool: true }); } + public static bool IsNonTrivialAssert (Cmd c) { return c is AssertCmd { Expr: LiteralExpr { asBool: true } }; } private static void DeleteUselessBlocks(List blocks) { var toVisit = new HashSet(); diff --git a/Source/VCGeneration/Splits/Focus.cs b/Source/VCGeneration/Splits/FocusApplier.cs similarity index 83% rename from Source/VCGeneration/Splits/Focus.cs rename to Source/VCGeneration/Splits/FocusApplier.cs index b84a35694..84229df9e 100644 --- a/Source/VCGeneration/Splits/Focus.cs +++ b/Source/VCGeneration/Splits/FocusApplier.cs @@ -12,15 +12,15 @@ namespace VCGeneration; -public static class Focus +public static class FocusApplier { /// /// Each focus block creates two options. /// We recurse twice for each focus, leading to potentially 2^N splits /// - public static List SplitOnFocus(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createSplit) + public static List GetFocusParts(VCGenOptions options, ImplementationRun run, + Func, ManualSplit> createPart) { var impl = run.Implementation; var dag = Program.GraphFromImpl(impl); @@ -33,7 +33,7 @@ public static List SplitOnFocus(VCGenOptions options, Implementatio focusBlocks.Reverse(); } if (!focusBlocks.Any()) { - return new List { createSplit(new ImplementationRootOrigin(run.Implementation), impl.Blocks) }; + return new List { createPart(new ImplementationRootOrigin(run.Implementation), impl.Blocks) }; } var ancestorsPerBlock = new Dictionary>(); @@ -53,7 +53,10 @@ void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; if (allFocusBlocksHaveBeenProcessed) { var newBlocks = ComputeNewBlocks(options, blocksToInclude, blocksReversed, freeAssumeBlocks); - result.Add(createSplit(new PathOrigin(run.Implementation.tok, path), newBlocks)); + ImplementationPartOrigin token = path.Any() + ? new PathOrigin(run.Implementation.tok, path) + : new ImplementationRootOrigin(run.Implementation); + result.Add(createPart(token, newBlocks)); } else { var (focusBlock, nextToken) = focusBlocks[focusIndex]; // assert b in blocks if (!blocksToInclude.Contains(focusBlock) || freeAssumeBlocks.Contains(focusBlock)) @@ -120,20 +123,20 @@ private static List ComputeNewBlocks(VCGenOptions options, ISet bl // which only contains vertices of subgraph. private static HashSet DominatedBlocks(List topologicallySortedBlocks, Block focusBlock, ISet subgraph) { - var dominators = new Dictionary>(); - foreach (var b in topologicallySortedBlocks.Where(subgraph.Contains)) + var dominatorsPerBlock = new Dictionary>(); + foreach (var block in topologicallySortedBlocks.Where(subgraph.Contains)) { - var s = new HashSet(); - var pred = b.Predecessors.Where(subgraph.Contains).ToList(); - if (pred.Count != 0) + var dominatorsForBlock = new HashSet(); + var predecessors = block.Predecessors.Where(subgraph.Contains).ToList(); + if (predecessors.Count != 0) { - s.UnionWith(dominators[pred[0]]); - pred.ForEach(blk => s.IntersectWith(dominators[blk])); + dominatorsForBlock.UnionWith(dominatorsPerBlock[predecessors[0]]); + predecessors.ForEach(blk => dominatorsForBlock.IntersectWith(dominatorsPerBlock[blk])); } - s.Add(b); - dominators[b] = s; + dominatorsForBlock.Add(block); + dominatorsPerBlock[block] = dominatorsForBlock; } - return subgraph.Where(blk => dominators[blk].Contains(focusBlock)).ToHashSet(); + return subgraph.Where(blk => dominatorsPerBlock[blk].Contains(focusBlock)).ToHashSet(); } private static Cmd DisableSplits(Cmd command) diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 9cdd29da6..37f25a1e2 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -12,7 +12,7 @@ namespace VCGeneration; public static class ManualSplitFinder { public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { - var paths = Focus.SplitOnFocus(options, run, createSplit); + var paths = FocusApplier.GetFocusParts(options, run, createSplit); return paths.SelectMany(GetVcsForSplits); } @@ -25,7 +25,6 @@ private static List GetVcsForSplits(ManualSplit partToSplit) { var splits = new HashSet(); foreach (var block in partToSplit.Blocks) { var splitsForThisBlock = new List(); - splitsPerBlock[block] = splitsForThisBlock; foreach (var command in block.Cmds) { if (!ShouldSplitHere(command, splitOnEveryAssert)) { continue; @@ -34,6 +33,10 @@ private static List GetVcsForSplits(ManualSplit partToSplit) { splits.Add(command); splitsForThisBlock.Add(command); } + + if (splitsForThisBlock.Any()) { + splitsPerBlock[block] = splitsForThisBlock; + } } if (!splits.Any()) { @@ -45,7 +48,7 @@ private static List GetVcsForSplits(ManualSplit partToSplit) { var blockStartToSplit = GetMapFromBlockStartToSplit(partToSplit.Blocks, splitsPerBlock); var beforeSplitsVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, blockStartToSplit, - entryPoint, splits, null); + entryPoint, splits, null, splitOnEveryAssert); if (beforeSplitsVc != null) { vcs.Add(beforeSplitsVc); @@ -59,7 +62,7 @@ private static List GetVcsForSplits(ManualSplit partToSplit) { foreach (var split in splitsForBlock) { var splitVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, - blockStartToSplit, block, splits, split); + blockStartToSplit, block, splits, split, splitOnEveryAssert); if (splitVc != null) { vcs.Add(splitVc); @@ -118,7 +121,8 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, ManualSplit partToSplit, - Dictionary blockStartToSplit, Block blockWithSplit, HashSet splits, Cmd? split) + Dictionary blockStartToSplit, Block blockWithSplit, + HashSet splits, Cmd? split, bool implicitSplits) { var newBlocks = new List(partToSplit.Blocks.Count); var assertionCount = 0; @@ -146,7 +150,7 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { } AddJumpsToNewBlocks(partToSplit.Blocks, oldToNewBlockMap); - var partToken = split == null ? partToSplit.Origin : new SplitOrigin(split.tok, partToSplit.Origin); + var partToken = split == null ? partToSplit.Origin : new SplitOrigin(implicitSplits, split.tok, partToSplit.Origin); return createVc(partToken, newBlocks); List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) @@ -208,18 +212,16 @@ private static void AddJumpsToNewBlocks(List oldBlocks, Dictionary Implicit ? "assertion" : "split"; } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/PathToken.cs b/Source/VCGeneration/Splits/PathToken.cs index b87f8dd0d..c0122811f 100644 --- a/Source/VCGeneration/Splits/PathToken.cs +++ b/Source/VCGeneration/Splits/PathToken.cs @@ -6,7 +6,7 @@ namespace VCGeneration; -class PathOrigin : TokenWrapper, ImplementationPartOrigin { +public class PathOrigin : TokenWrapper, ImplementationPartOrigin { public PathOrigin(IToken inner, ImmutableStack branches) : base(inner) { Branches = branches; @@ -15,7 +15,7 @@ public PathOrigin(IToken inner, ImmutableStack branches) : base(inner) { public ImmutableStack Branches { get; } public string Render(CoreOptions options) { - return $" after passing through: [{string.Join(", ", Branches.Select(b => $"({b.line},{b.col})"))}]"; + return $" passing through: [{string.Join(", ", Branches.Select(b => $"({b.line},{b.col})"))}]"; } } diff --git a/Source/VCGeneration/Splits/TokenWrapper.cs b/Source/VCGeneration/Splits/TokenWrapper.cs index 4cfd20e54..5cbb85879 100644 --- a/Source/VCGeneration/Splits/TokenWrapper.cs +++ b/Source/VCGeneration/Splits/TokenWrapper.cs @@ -2,7 +2,7 @@ namespace VCGeneration; -class TokenWrapper : IToken { +public class TokenWrapper : IToken { public IToken Inner { get; } public TokenWrapper(IToken inner) { From 23a094237ea563ba10137cfcf241293b455c1678 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 23:54:23 +0200 Subject: [PATCH 22/70] Refactoring --- Source/Graph/Graph.cs | 118 +++++++++--------- Source/VCGeneration/Splits/FocusApplier.cs | 17 +-- .../VCGeneration/Splits/ManualSplitFinder.cs | 8 +- Source/VCGeneration/Splits/PathToken.cs | 15 +-- 4 files changed, 78 insertions(+), 80 deletions(-) diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 982f8da44..624426462 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -57,23 +57,26 @@ internal static class Util } } - public class DomRelation + public class DomRelation { // doms maps (unique) node numbers to the node numbers of the immediate dominator // to use it on Nodes, one needs the two way mapping between nodes and their numbers. - private int[] doms; // 0 is unused: means undefined + private int[] nodeNumberToImmediateDominator; // 0 is unused: means undefined // here are the two mappings - private Node[] postOrderNumberToNode; - private Dictionary nodeToPostOrderNumber; + private TNode[] postOrderNumberToNode; + private Dictionary nodeToPostOrderNumber; private int sourceNum; // (number for) root of the graph - private Node source; // root of the graph - private Graph graph; - private Dictionary> immediateDominatorMap; + private TNode source; // root of the graph + private Graph graph; + private Dictionary> immediateDominatorMap; [NotDelayed] - internal DomRelation(Graph g, Node source) + internal DomRelation(Graph g, TNode source) { + // TODO should we enable saying that the graph is a DAG, to enable an O(N) dominance algorithm? + // Or is the algorithm already O(N) for DAG graphs? + this.graph = g; // slot 0 not used: nodes are numbered from 1 to n so zero // can represent undefined. @@ -82,7 +85,7 @@ internal DomRelation(Graph g, Node source) this.NewComputeDominators(); } - public Dictionary> ImmediateDominatorMap + public Dictionary> ImmediateDominatorMap { get { @@ -91,10 +94,10 @@ public Dictionary> ImmediateDominatorMap } } - public bool DominatedBy(Node dominee, Node dominator, List path = null) + public bool DominatedBy(TNode dominee, TNode dominator, List path = null) { Contract.Assume(this.nodeToPostOrderNumber != null); - Contract.Assume(this.doms != null); + Contract.Assume(this.nodeNumberToImmediateDominator != null); int domineeNum = this.nodeToPostOrderNumber[dominee]; int dominatorNum = this.nodeToPostOrderNumber[dominator]; if (domineeNum == dominatorNum) @@ -102,49 +105,46 @@ public bool DominatedBy(Node dominee, Node dominator, List path = null) return true; } - int currentNodeNum = this.doms[domineeNum]; + int currentDominator = nodeNumberToImmediateDominator[domineeNum]; while (true) { - if (currentNodeNum == dominatorNum) + if (currentDominator == dominatorNum) { return true; } - if (currentNodeNum == this.sourceNum) + if (currentDominator == sourceNum) { return false; } - if (path != null) - { - path.Add(postOrderNumberToNode[currentNodeNum]); - } + path?.Add(postOrderNumberToNode[currentDominator]); - currentNodeNum = this.doms[currentNodeNum]; + currentDominator = nodeNumberToImmediateDominator[currentDominator]; } } - private Dictionary> domMap = null; + private Dictionary> domMap = null; [Pure] public override string ToString() { - Contract.Assume(this.doms != null); - int[] localDoms = this.doms; + Contract.Assume(this.nodeNumberToImmediateDominator != null); + int[] localDoms = this.nodeNumberToImmediateDominator; Contract.Assume(this.postOrderNumberToNode != null); if (domMap == null) { - domMap = new Dictionary>(); + domMap = new Dictionary>(); for (int i = 1; i < localDoms.Length; i++) { // 0 slot is not used int domineeNum = i; int currentNodeNum = domineeNum; - List dominators = new List(); + List dominators = new List(); while (currentNodeNum != this.sourceNum) { dominators.Add(this.postOrderNumberToNode[currentNodeNum]); - currentNodeNum = this.doms[currentNodeNum]; + currentNodeNum = this.nodeNumberToImmediateDominator[currentNodeNum]; } dominators.Add(this.postOrderNumberToNode[this.sourceNum]); @@ -155,14 +155,14 @@ public override string ToString() StringBuilder sb = new StringBuilder(); sb.Append("{"); bool first = true; - foreach (KeyValuePair> de in domMap) + foreach (KeyValuePair> de in domMap) { if (!first) { sb.Append(", "); } - Contract.Assert(!object.Equals(de.Key, default(Node))); + Contract.Assert(!object.Equals(de.Key, default(TNode))); sb.Append(de.Key.ToString()); sb.Append("~>"); sb.Append(ListToString(de.Value)); @@ -235,8 +235,8 @@ public void PrintList(IEnumerable xs) private void NewComputeDominators() { int n = this.graph.Nodes.Count; - this.postOrderNumberToNode = new Node[n + 1]; - this.nodeToPostOrderNumber = new Dictionary(); + this.postOrderNumberToNode = new TNode[n + 1]; + this.nodeToPostOrderNumber = new Dictionary(); //HashSet visited = new HashSet(); //int currentNumber = 1; Contract.Assume(this.source != null); @@ -244,9 +244,9 @@ private void NewComputeDominators() this.PostOrderVisitIterative(this.source); this.sourceNum = this.nodeToPostOrderNumber[source]; // for (int i = 1; i <= n; i++){ Console.WriteLine(postOrderNumberToNode[i]); } - this.doms = new int[n + 1]; // 0 is unused: means undefined - Node start_node = this.source; - this.doms[this.nodeToPostOrderNumber[start_node]] = this.nodeToPostOrderNumber[start_node]; + this.nodeNumberToImmediateDominator = new int[n + 1]; // 0 is unused: means undefined + TNode start_node = this.source; + this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[start_node]] = this.nodeToPostOrderNumber[start_node]; bool changed = true; // PrintIntArray(doms); while (changed) @@ -255,8 +255,8 @@ private void NewComputeDominators() // for all nodes, b, in reverse postorder (except start_node) for (int nodeNum = n - 1; 1 <= nodeNum; nodeNum--) { - Node b = this.postOrderNumberToNode[nodeNum]; - IEnumerable predecessors = this.graph.Predecessors(b); + TNode b = this.postOrderNumberToNode[nodeNum]; + IEnumerable predecessors = this.graph.Predecessors(b); // find a predecessor (i.e., a higher number) for which // the doms array has been set int new_idom = 0; @@ -264,9 +264,9 @@ private void NewComputeDominators() #region new_idom <- number of first (processed) predecessor of b (pick one) - foreach (Node p in predecessors) + foreach (TNode p in predecessors) { - if (this.doms[this.nodeToPostOrderNumber[p]] != 0) + if (this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[p]] != 0) { int x = this.nodeToPostOrderNumber[p]; new_idom = x; @@ -279,24 +279,24 @@ private void NewComputeDominators() #region for all other predecessors, p, of b - foreach (Node p in predecessors) + foreach (TNode p in predecessors) { if (this.nodeToPostOrderNumber[p] == first_processed_predecessor) { continue; } - if (this.doms[this.nodeToPostOrderNumber[p]] != 0) + if (this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[p]] != 0) { - new_idom = intersect(this.nodeToPostOrderNumber[p], new_idom, this.doms); + new_idom = Intersect(this.nodeToPostOrderNumber[p], new_idom, this.nodeNumberToImmediateDominator); } } #endregion - if (this.doms[this.nodeToPostOrderNumber[b]] != new_idom) + if (this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[b]] != new_idom) { - this.doms[this.nodeToPostOrderNumber[b]] = new_idom; + this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[b]] = new_idom; changed = true; } } @@ -305,12 +305,12 @@ private void NewComputeDominators() #region Populate the Immediate Dominator Map int sourceNum = this.nodeToPostOrderNumber[this.source]; - immediateDominatorMap = new Dictionary>(); + immediateDominatorMap = new Dictionary>(); for (int i = 1; i <= n; i++) { - Node node = this.postOrderNumberToNode[i]; - Node idomNode = this.postOrderNumberToNode[this.doms[i]]; - if (i == sourceNum && this.doms[i] == sourceNum) + TNode node = this.postOrderNumberToNode[i]; + TNode idomNode = this.postOrderNumberToNode[this.nodeNumberToImmediateDominator[i]]; + if (i == sourceNum && this.nodeNumberToImmediateDominator[i] == sourceNum) { continue; } @@ -321,7 +321,7 @@ private void NewComputeDominators() } else { - List l = new List(); + List l = new List(); l.Add(node); immediateDominatorMap.Add(idomNode, l); } @@ -330,7 +330,7 @@ private void NewComputeDominators() #endregion } - private int intersect(int b1, int b2, int[] doms) + private int Intersect(int b1, int b2, int[] doms) { int finger1 = b1; int finger2 = b2; @@ -350,7 +350,7 @@ private int intersect(int b1, int b2, int[] doms) return finger1; } - private void PostOrderVisit(Node /*!*/ n, HashSet visited, ref int currentNumber) + private void PostOrderVisit(TNode /*!*/ n, HashSet visited, ref int currentNumber) { Contract.Requires(n != null); if (visited.Contains(n)) @@ -359,7 +359,7 @@ private void PostOrderVisit(Node /*!*/ n, HashSet visited, ref int current } visited.Add(n); - foreach (Node /*!*/ child in this.graph.Successors(n)) + foreach (TNode /*!*/ child in this.graph.Successors(n)) { Contract.Assert(child != null); PostOrderVisit(child, visited, ref currentNumber); @@ -374,12 +374,12 @@ private void PostOrderVisit(Node /*!*/ n, HashSet visited, ref int current } // Iterative version: mimics the above recursive procedure - private void PostOrderVisitIterative(Node n) + private void PostOrderVisitIterative(TNode n) { Contract.Requires(n != null); - var visited = new HashSet(); - var grey = new HashSet(); - var stack = new Stack(); + var visited = new HashSet(); + var grey = new HashSet(); + var stack = new Stack(); int currentNumber = 1; @@ -402,7 +402,7 @@ private void PostOrderVisitIterative(Node n) { grey.Add(curr); stack.Push(curr); - foreach (Node /*!*/ child in this.graph.Successors(curr)) + foreach (TNode /*!*/ child in this.graph.Successors(curr)) { Contract.Assert(child != null); if (!visited.Contains(child)) @@ -415,10 +415,10 @@ private void PostOrderVisitIterative(Node n) } } - public Node LeastCommonAncestor(Node n1, Node n2) + public TNode LeastCommonAncestor(TNode n1, TNode n2) { int num1 = nodeToPostOrderNumber[n1], num2 = nodeToPostOrderNumber[n2]; - int lca = intersect(num1, num2, this.doms); + int lca = Intersect(num1, num2, this.nodeNumberToImmediateDominator); return postOrderNumberToNode[lca]; } } @@ -661,7 +661,7 @@ public static HashSet Intersection(IEnumerable> sets) { } if (result == null) { - return ImmutableHashSet.Empty; + return new HashSet(); } return result; @@ -710,10 +710,10 @@ public List ImmediatelyDominatedBy(Node /*!*/ n) { Contract.Requires(n != null); this.ImmediateDominatorMap.TryGetValue(n, out var dominees); - return dominees == null ? new List() : dominees; + return dominees ?? new List(); } - public IEnumerable TopologicalSort(bool reversed = false) + public List TopologicalSort(bool reversed = false) { TarjanTopSort(out var acyclic, out var sortedList, reversed); return acyclic ? sortedList : new List(); diff --git a/Source/VCGeneration/Splits/FocusApplier.cs b/Source/VCGeneration/Splits/FocusApplier.cs index 84229df9e..760f8e59d 100644 --- a/Source/VCGeneration/Splits/FocusApplier.cs +++ b/Source/VCGeneration/Splits/FocusApplier.cs @@ -22,9 +22,9 @@ public static class FocusApplier public static List GetFocusParts(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createPart) { - var impl = run.Implementation; - var dag = Program.GraphFromImpl(impl); - var topologicallySortedBlocks = dag.TopologicalSort().ToList(); + var implementation = run.Implementation; + var dag = Program.GraphFromImpl(implementation); + var topologicallySortedBlocks = dag.TopologicalSort(); var blocksReversed = Enumerable.Reverse(topologicallySortedBlocks).ToList(); // By default, we process the foci in a top-down fashion, i.e., in the topological order. // If the user sets the RelaxFocus flag, we use the reverse (topological) order. @@ -33,7 +33,7 @@ public static List GetFocusParts(VCGenOptions options, Implementati focusBlocks.Reverse(); } if (!focusBlocks.Any()) { - return new List { createPart(new ImplementationRootOrigin(run.Implementation), impl.Blocks) }; + return new List { createPart(new ImplementationRootOrigin(run.Implementation), implementation.Blocks) }; } var ancestorsPerBlock = new Dictionary>(); @@ -43,18 +43,19 @@ public static List GetFocusParts(VCGenOptions options, Implementati reachables.Remove(fb.Block); ancestorsPerBlock[fb.Block] = reachables; }); + var dominators = dag.DominatorMap; focusBlocks.ForEach(fb => descendantsPerBlock[fb.Block] = dag.ComputeReachability(fb.Block).ToHashSet()); var result = new List(); - AddSplitsFromIndex(ImmutableStack.Empty, 0, impl.Blocks.ToHashSet(), ImmutableHashSet.Empty); + AddSplitsFromIndex(ImmutableStack.Empty, 0, implementation.Blocks.ToHashSet(), ImmutableHashSet.Empty); return result; - void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet blocksToInclude, ISet freeAssumeBlocks) { + void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet blocksToInclude, ISet freeAssumeBlocks) { var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; if (allFocusBlocksHaveBeenProcessed) { var newBlocks = ComputeNewBlocks(options, blocksToInclude, blocksReversed, freeAssumeBlocks); ImplementationPartOrigin token = path.Any() - ? new PathOrigin(run.Implementation.tok, path) + ? new PathOrigin(run.Implementation.tok, path, dominators) : new ImplementationRootOrigin(run.Implementation); result.Add(createPart(token, newBlocks)); } else { @@ -76,7 +77,7 @@ void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet // Recursive call that does focus the block // Contains all the ancestors, the focus block, and the descendants. - AddSplitsFromIndex(path.Push(nextToken), focusIndex + 1, + AddSplitsFromIndex(path.Push(focusBlock), focusIndex + 1, ancestors.Union(descendants).Intersect(blocksToInclude).ToHashSet(), ancestors.Union(freeAssumeBlocks).ToHashSet()); } diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 37f25a1e2..74f3fcdae 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -150,7 +150,7 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { } AddJumpsToNewBlocks(partToSplit.Blocks, oldToNewBlockMap); - var partToken = split == null ? partToSplit.Origin : new SplitOrigin(implicitSplits, split.tok, partToSplit.Origin); + var partToken = split == null ? partToSplit.Origin : new SplitOrigin(implicitSplits, split.tok, blockWithSplit, partToSplit.Origin); return createVc(partToken, newBlocks); List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) @@ -213,13 +213,15 @@ private static void AddJumpsToNewBlocks(List oldBlocks, Dictionary branches) : base(inner) { + public PathOrigin(IToken inner, ImmutableStack branches, DomRelation dominators) : base(inner) { Branches = branches; + Dominators = dominators; } - public ImmutableStack Branches { get; } - - public string Render(CoreOptions options) { - return $" passing through: [{string.Join(", ", Branches.Select(b => $"({b.line},{b.col})"))}]"; - } + public ImmutableStack Branches { get; } + public DomRelation Dominators { get; } } class ImplementationRootOrigin : TokenWrapper, ImplementationPartOrigin { public ImplementationRootOrigin(Implementation implementation) : base(implementation.tok) { } - - public string Render(CoreOptions options) { - return ""; - } } \ No newline at end of file From 2404e954fc4ca56251fcf35f0f2f6a04d6f37e3c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 27 Sep 2024 23:59:14 +0200 Subject: [PATCH 23/70] Fix warnings --- Source/Graph/Graph.cs | 6 ++---- Source/VCGeneration/Splits/TokenWrapper.cs | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 624426462..67024c10d 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -1,9 +1,7 @@ -#nullable enable using System; using System.Linq; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text; // for StringBuilder +using System.Text; using System.Diagnostics.Contracts; namespace Microsoft.Boogie.GraphUtil @@ -650,7 +648,7 @@ public Dictionary> AcyclicDominators() public static HashSet Intersection(IEnumerable> sets) { var first = true; - HashSet? result = null; + HashSet result = null; foreach (var set in sets) { if (first) { result = set.ToHashSet(); diff --git a/Source/VCGeneration/Splits/TokenWrapper.cs b/Source/VCGeneration/Splits/TokenWrapper.cs index 5cbb85879..d7ea8427d 100644 --- a/Source/VCGeneration/Splits/TokenWrapper.cs +++ b/Source/VCGeneration/Splits/TokenWrapper.cs @@ -1,3 +1,4 @@ +#nullable enable using Microsoft.Boogie; namespace VCGeneration; From d84741297bb3f3fd1bba0b7aa6b63310209d08c3 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 30 Sep 2024 16:18:36 +0200 Subject: [PATCH 24/70] Draft for isolated assertion --- Source/VCGeneration/Splits/FocusApplier.cs | 3 + .../Splits/IsolateAttributeHandler.cs | 148 ++++++++++++++++++ .../VCGeneration/Splits/ManualSplitFinder.cs | 93 ++++++----- 3 files changed, 204 insertions(+), 40 deletions(-) create mode 100644 Source/VCGeneration/Splits/IsolateAttributeHandler.cs diff --git a/Source/VCGeneration/Splits/FocusApplier.cs b/Source/VCGeneration/Splits/FocusApplier.cs index 760f8e59d..17129896d 100644 --- a/Source/VCGeneration/Splits/FocusApplier.cs +++ b/Source/VCGeneration/Splits/FocusApplier.cs @@ -92,6 +92,7 @@ private static List ComputeNewBlocks(VCGenOptions options, ISet bl var newBlocks = new List(); var oldToNewBlockMap = new Dictionary(blocksToInclude.Count); + // TODO, use ManualSplitFinder.CreateSplit() // Traverse backwards to allow settings the jumps to the new blocks foreach (var block in blocksReversed) { @@ -116,6 +117,8 @@ private static List ComputeNewBlocks(VCGenOptions options, ISet bl } } newBlocks.Reverse(); + + // TODO remove? BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); return newBlocks; } diff --git a/Source/VCGeneration/Splits/IsolateAttributeHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeHandler.cs new file mode 100644 index 000000000..426caa9ea --- /dev/null +++ b/Source/VCGeneration/Splits/IsolateAttributeHandler.cs @@ -0,0 +1,148 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.Boogie; +using VC; + +namespace VCGeneration; + +static class IsolateAttributeHandler { + public static List GetPartsFromIsolatedAssertions(VCGenOptions options, ManualSplit partToDivide, + Func, ManualSplit> createSplit) { + + var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; + partToDivide.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); + + var isolatedAssertions = new HashSet(); + var results = new List(); + var dag = Program.GraphFromBlocks(partToDivide.Blocks); + + var assumedAssertions = new Dictionary(); + foreach (var block in partToDivide.Blocks) { + foreach (var assert in block.Cmds.OfType()) { + var isolateAttribute = QKeyValue.FindAttribute(assert.Attributes, p => p.Key == "isolate"); + if (isolateAttribute == null || (splitOnEveryAssert && isolateAttribute.Params.OfType().All(p => p != "none"))) { + continue; + } + + isolatedAssertions.Add(assert); + if (isolateAttribute.Params.OfType().Any(p => p == "path")) { + results.AddRange(GetSplitsForIsolatedPathAssertion(block, assert)); + } else { + results.Add(GetSplitForIsolatedAssertion(block, assert)); + } + } + } + + results.Add(GetSplitWithoutIsolatedAssertions(partToDivide.Implementation)); + + return results; + + Cmd TransformAssertCmd(Cmd cmd) { + if (cmd is AssertCmd assertCmd) { + return assumedAssertions.GetOrCreate(assertCmd, () => VerificationConditionGenerator.AssertTurnedIntoAssume(options, assertCmd)); + } + + return cmd; + } + + IEnumerable GetSplitsForIsolatedPathAssertion(Block blockWithAssert, AssertCmd assertCmd) { + var blockToVisit = new Stack>(); + blockToVisit.Push(ImmutableStack.Create(new Block(blockWithAssert.tok) + { + Label = blockWithAssert.Label, + TransferCmd = new GotoCmd(Token.NoToken, new List(), new List()), + Cmds = GetCommandsForBlockWithAssert(blockWithAssert, assertCmd) + })); + while(blockToVisit.Any()) { + var path = blockToVisit.Pop(); + var block = path.Peek(); + if (!block.Predecessors.Any()) { + yield return createSplit(new PathOrigin(assertCmd.tok, path, dag.DominatorMap), path.ToList()); + } + foreach (var oldPrevious in block.Predecessors) { + var newPrevious = new Block(oldPrevious.tok) + { + Label = oldPrevious.Label, + TransferCmd = oldPrevious.TransferCmd, + Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList() + }; + if (newPrevious.TransferCmd is GotoCmd gotoCmd) { + newPrevious.TransferCmd = + new GotoCmd(gotoCmd.tok, new List() { block.Label }, new List { block }); + } + blockToVisit.Push(path.Push(newPrevious)); + } + } + } + + ManualSplit GetSplitForIsolatedAssertion(Block blockWithAssert, AssertCmd assertCmd) { + var newBlocks = new List(); + var oldToNewBlockMap = new Dictionary(); + var blockToVisit = new Stack(); + blockToVisit.Push(blockWithAssert); + while(blockToVisit.Any()) { + var oldBlock = blockToVisit.Pop(); + var newBlock = new Block(oldBlock.tok) + { + Label = oldBlock.Label, + TransferCmd = oldBlock.TransferCmd + }; + oldToNewBlockMap[oldBlock] = newBlock; + + newBlocks.Add(newBlock); + newBlock.Cmds = oldBlock == blockWithAssert + ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) + : oldBlock.Cmds.Select(TransformAssertCmd).ToList(); + foreach (var previous in oldBlock.Predecessors) { + blockToVisit.Push(previous); + } + + if (newBlock.TransferCmd is GotoCmd gtc) + { + var targets = gtc.LabelTargets.Where(oldToNewBlockMap.ContainsKey).ToList(); + newBlock.TransferCmd = new GotoCmd(gtc.tok, + targets.Select(block => oldToNewBlockMap[block].Label).ToList(), + targets.Select(block => oldToNewBlockMap[block]).ToList()); + } + } + + return createSplit(new IsolatedAssertionOrigin(assertCmd), newBlocks); + } + + List GetCommandsForBlockWithAssert(Block currentBlock, AssertCmd assert) + { + var result = new List(); + foreach (var command in currentBlock.Cmds) { + if (assert == command) { + result.Add(command); + break; + } + result.Add(TransformAssertCmd(command)); + } + + return result; + } + + ManualSplit GetSplitWithoutIsolatedAssertions(Implementation implementation) { + var origin = new ImplementationRootOrigin(partToDivide.Implementation); + if (!isolatedAssertions.Any()) { + return createSplit(origin, partToDivide.Blocks); + } + + // TODO don't copy list if it is unchanged. + return createSplit(origin, ManualSplitFinder.UpdateBlocks(implementation.Blocks, block => block.Cmds.Select(TransformAssertCmd).ToList())); + } + } +} + + +class IsolatedAssertionOrigin : TokenWrapper, ImplementationPartOrigin { + public AssertCmd IsolatedAssert { get; } + + public IsolatedAssertionOrigin(AssertCmd isolatedAssert) : base(isolatedAssert.tok) { + this.IsolatedAssert = isolatedAssert; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 74f3fcdae..1f66d79a8 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -8,25 +8,48 @@ namespace VCGeneration; - public static class ManualSplitFinder { + public static List UpdateBlocks(IReadOnlyList blocks, + Func> getCommands) + { + var newBlocks = new List(blocks.Count); + var oldToNewBlockMap = new Dictionary(newBlocks.Count); + foreach (var currentBlock in blocks) { + var newBlock = new Block(currentBlock.tok) + { + Label = currentBlock.Label + }; + + oldToNewBlockMap[currentBlock] = newBlock; + newBlocks.Add(newBlock); + newBlock.Cmds = getCommands(currentBlock); + } + + AddJumpsToNewBlocks(oldToNewBlockMap); + return newBlocks; + } + public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createSplit) { - var paths = FocusApplier.GetFocusParts(options, run, createSplit); - return paths.SelectMany(GetVcsForSplits); + var focussedParts = FocusApplier.GetFocusParts(options, run, createSplit); + var isolatedParts = focussedParts.SelectMany(s => + IsolateAttributeHandler.GetPartsFromIsolatedAssertions(options, s, createSplit)).ToList(); + + if (!isolatedParts.Any()) { + return Enumerable.Empty(); + } + + var notIsolatedPart = isolatedParts.First(); + return GetVcsForSplits(notIsolatedPart).Concat(isolatedParts.Skip(1)); } private static List GetVcsForSplits(ManualSplit partToSplit) { - - var splitOnEveryAssert = partToSplit.Options.VcsSplitOnEveryAssert; - partToSplit.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); - var splitsPerBlock = new Dictionary>(); var splits = new HashSet(); foreach (var block in partToSplit.Blocks) { var splitsForThisBlock = new List(); foreach (var command in block.Cmds) { - if (!ShouldSplitHere(command, splitOnEveryAssert)) { + if (!ShouldSplitHere(command)) { continue; } @@ -48,7 +71,7 @@ private static List GetVcsForSplits(ManualSplit partToSplit) { var blockStartToSplit = GetMapFromBlockStartToSplit(partToSplit.Blocks, splitsPerBlock); var beforeSplitsVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, blockStartToSplit, - entryPoint, splits, null, splitOnEveryAssert); + entryPoint, splits, null); if (beforeSplitsVc != null) { vcs.Add(beforeSplitsVc); @@ -62,7 +85,7 @@ private static List GetVcsForSplits(ManualSplit partToSplit) { foreach (var split in splitsForBlock) { var splitVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, - blockStartToSplit, block, splits, split, splitOnEveryAssert); + blockStartToSplit, block, splits, split); if (splitVc != null) { vcs.Add(splitVc); @@ -79,13 +102,12 @@ ManualSplit CreateVc(ImplementationPartOrigin token, List blocks) { } } - private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { + private static bool ShouldSplitHere(Cmd c) { if (c is not PredicateCmd predicateCmd) { return false; } - var findBoolAttribute = QKeyValue.FindNullableBoolAttribute(predicateCmd.Attributes, "split_here"); - return findBoolAttribute ?? (c is AssertCmd && splitOnEveryAssert); + return QKeyValue.FindBoolAttribute(predicateCmd.Attributes, "split_here"); } private static Dictionary GetMapFromBlockStartToSplit(List blocks, Dictionary> splitsPerBlock) { @@ -122,35 +144,27 @@ private static bool ShouldSplitHere(Cmd c, bool splitOnEveryAssert) { private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, ManualSplit partToSplit, Dictionary blockStartToSplit, Block blockWithSplit, - HashSet splits, Cmd? split, bool implicitSplits) + HashSet splits, Cmd? split) { - var newBlocks = new List(partToSplit.Blocks.Count); var assertionCount = 0; - var oldToNewBlockMap = new Dictionary(newBlocks.Count); // Maps original blocks to their new copies in newBlocks - foreach (var currentBlock in partToSplit.Blocks) { - var newBlock = new Block(currentBlock.tok) - { - Label = currentBlock.Label - }; - - oldToNewBlockMap[currentBlock] = newBlock; - newBlocks.Add(newBlock); + + var newBlocks = UpdateBlocks(partToSplit.Implementation.Blocks, currentBlock => { if (currentBlock == blockWithSplit) { - newBlock.Cmds = GetCommandsForBlockWithSplit(currentBlock); - } else if (blockStartToSplit[currentBlock] == split) { - newBlock.Cmds = GetCommandsForBlockImmediatelyDominatedBySplit(currentBlock); - } else { - newBlock.Cmds = currentBlock.Cmds.Select(x => CommandTransformations.AssertIntoAssume(partToSplit.Options, x)).ToList(); + return GetCommandsForBlockWithSplit(currentBlock); } - } - if (assertionCount == 0) - { + if (blockStartToSplit[currentBlock] == split) { + return GetCommandsForBlockImmediatelyDominatedBySplit(currentBlock); + } + + return currentBlock.Cmds.Select(x => CommandTransformations.AssertIntoAssume(partToSplit.Options, x)).ToList(); + }); + + if (assertionCount == 0) { return null; } - - AddJumpsToNewBlocks(partToSplit.Blocks, oldToNewBlockMap); - var partToken = split == null ? partToSplit.Origin : new SplitOrigin(implicitSplits, split.tok, blockWithSplit, partToSplit.Origin); + + var partToken = split == null ? partToSplit.Origin : new SplitOrigin(false, split.tok, blockWithSplit, partToSplit.Origin); return createVc(partToken, newBlocks); List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) @@ -189,18 +203,17 @@ List GetCommandsForBlockWithSplit(Block currentBlock) } } - private static void AddJumpsToNewBlocks(List oldBlocks, Dictionary oldToNewBlockMap) + public static void AddJumpsToNewBlocks(Dictionary oldToNewBlockMap) { - foreach (var oldBlock in oldBlocks) { - var newBlock = oldToNewBlockMap[oldBlock]; + foreach (var (oldBlock, newBlock) in oldToNewBlockMap) { if (oldBlock.TransferCmd is ReturnCmd returnCmd) { ((ReturnCmd)newBlock.TransferCmd).tok = returnCmd.tok; continue; } var gotoCmd = (GotoCmd)oldBlock.TransferCmd; - var newLabelTargets = new List(gotoCmd.LabelTargets.Count()); - var newLabelNames = new List(gotoCmd.LabelTargets.Count()); + var newLabelTargets = new List(gotoCmd.LabelTargets.Count); + var newLabelNames = new List(gotoCmd.LabelTargets.Count); foreach (var target in gotoCmd.LabelTargets) { newLabelTargets.Add(oldToNewBlockMap[target]); newLabelNames.Add(oldToNewBlockMap[target].Label); From dea7493f18946a2675735b906b694e8772c6d641 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Mon, 30 Sep 2024 20:54:53 +0200 Subject: [PATCH 25/70] More progress on the {:isolate} attribute --- Source/Core/AST/AbsyCmd.cs | 5 +- .../Splits/IsolateAttributeHandler.cs | 60 ++++++++++++++----- .../VCGeneration/Splits/ManualSplitFinder.cs | 16 ++--- Source/VCGeneration/Splits/PathToken.cs | 18 ++++++ .../isolatedAssertion.bpl | 42 +++++++++++++ .../isolatedAssertion.bpl.expect | 3 + 6 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 Test/implementationDivision/isolatedAssertion.bpl create mode 100644 Test/implementationDivision/isolatedAssertion.bpl.expect diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 6e7f34efa..728de48b0 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -368,10 +368,9 @@ int FreshAnon() return anon++; } - HashSet allLabels = new HashSet(); + private readonly HashSet allLabels = new(); - Errors /*!*/ - errorHandler; + private readonly Errors /*!*/ errorHandler; [ContractInvariantMethod] void ObjectInvariant() diff --git a/Source/VCGeneration/Splits/IsolateAttributeHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeHandler.cs index 426caa9ea..a5566b029 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeHandler.cs @@ -18,17 +18,28 @@ public static List GetPartsFromIsolatedAssertions(VCGenOptions opti var isolatedAssertions = new HashSet(); var results = new List(); var dag = Program.GraphFromBlocks(partToDivide.Blocks); - + var assumedAssertions = new Dictionary(); foreach (var block in partToDivide.Blocks) { foreach (var assert in block.Cmds.OfType()) { var isolateAttribute = QKeyValue.FindAttribute(assert.Attributes, p => p.Key == "isolate"); - if (isolateAttribute == null || (splitOnEveryAssert && isolateAttribute.Params.OfType().All(p => p != "none"))) { - continue; + if (splitOnEveryAssert) { + if (isolateAttribute != null) { + if (isolateAttribute.Params.OfType().Any(p => Equals(p, "none"))) { + continue; + } + // isolate + } + // isolate + } else { + if (isolateAttribute == null) { + continue; + } + // isolate } isolatedAssertions.Add(assert); - if (isolateAttribute.Params.OfType().Any(p => p == "path")) { + if (isolateAttribute != null && isolateAttribute.Params.OfType().Any(p => Equals(p, "paths"))) { results.AddRange(GetSplitsForIsolatedPathAssertion(block, assert)); } else { results.Add(GetSplitForIsolatedAssertion(block, assert)); @@ -36,7 +47,7 @@ public static List GetPartsFromIsolatedAssertions(VCGenOptions opti } } - results.Add(GetSplitWithoutIsolatedAssertions(partToDivide.Implementation)); + results.Add(GetSplitWithoutIsolatedAssertions()); return results; @@ -52,26 +63,34 @@ IEnumerable GetSplitsForIsolatedPathAssertion(Block blockWithAssert var blockToVisit = new Stack>(); blockToVisit.Push(ImmutableStack.Create(new Block(blockWithAssert.tok) { + Predecessors = blockWithAssert.Predecessors, Label = blockWithAssert.Label, TransferCmd = new GotoCmd(Token.NoToken, new List(), new List()), Cmds = GetCommandsForBlockWithAssert(blockWithAssert, assertCmd) })); while(blockToVisit.Any()) { var path = blockToVisit.Pop(); - var block = path.Peek(); - if (!block.Predecessors.Any()) { - yield return createSplit(new PathOrigin(assertCmd.tok, path, dag.DominatorMap), path.ToList()); + var firstBlock = path.Peek(); + if (!firstBlock.Predecessors.Any()) { + yield return createSplit(new PathOrigin(assertCmd.tok, path, dag.DominatorMap), new List() { new(firstBlock.tok) { + Label = firstBlock.Label, + Cmds = path.SelectMany(b => b.Cmds).ToList() + } }); } - foreach (var oldPrevious in block.Predecessors) { + foreach (var oldPrevious in firstBlock.Predecessors) { var newPrevious = new Block(oldPrevious.tok) { + Predecessors = oldPrevious.Predecessors, Label = oldPrevious.Label, TransferCmd = oldPrevious.TransferCmd, Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList() }; if (newPrevious.TransferCmd is GotoCmd gotoCmd) { newPrevious.TransferCmd = - new GotoCmd(gotoCmd.tok, new List() { block.Label }, new List { block }); + new GotoCmd(gotoCmd.tok, new List() { firstBlock.Label }, new List + { + firstBlock + }); } blockToVisit.Push(path.Push(newPrevious)); } @@ -83,8 +102,13 @@ ManualSplit GetSplitForIsolatedAssertion(Block blockWithAssert, AssertCmd assert var oldToNewBlockMap = new Dictionary(); var blockToVisit = new Stack(); blockToVisit.Push(blockWithAssert); + var visitedBlocks = new HashSet(); while(blockToVisit.Any()) { var oldBlock = blockToVisit.Pop(); + if (!visitedBlocks.Add(oldBlock)) { + continue; + } + var newBlock = new Block(oldBlock.tok) { Label = oldBlock.Label, @@ -109,7 +133,7 @@ ManualSplit GetSplitForIsolatedAssertion(Block blockWithAssert, AssertCmd assert } } - return createSplit(new IsolatedAssertionOrigin(assertCmd), newBlocks); + return createSplit(new IsolatedAssertionOrigin(assertCmd), newBlocks.OrderBy(b => b.tok).ToList()); } List GetCommandsForBlockWithAssert(Block currentBlock, AssertCmd assert) @@ -126,20 +150,28 @@ List GetCommandsForBlockWithAssert(Block currentBlock, AssertCmd assert) return result; } - ManualSplit GetSplitWithoutIsolatedAssertions(Implementation implementation) { + ManualSplit GetSplitWithoutIsolatedAssertions() { var origin = new ImplementationRootOrigin(partToDivide.Implementation); if (!isolatedAssertions.Any()) { return createSplit(origin, partToDivide.Blocks); } // TODO don't copy list if it is unchanged. - return createSplit(origin, ManualSplitFinder.UpdateBlocks(implementation.Blocks, block => block.Cmds.Select(TransformAssertCmd).ToList())); + var newBlocks = ManualSplitFinder.UpdateBlocks(partToDivide.Blocks, + block => block.Cmds.Select(cmd => { + if (isolatedAssertions.Contains(cmd)) { + return TransformAssertCmd(cmd); + } + + return cmd; + }).ToList()); + return createSplit(origin, newBlocks); } } } -class IsolatedAssertionOrigin : TokenWrapper, ImplementationPartOrigin { +public class IsolatedAssertionOrigin : TokenWrapper, ImplementationPartOrigin { public AssertCmd IsolatedAssert { get; } public IsolatedAssertionOrigin(AssertCmd isolatedAssert) : base(isolatedAssert.tok) { diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 1f66d79a8..186b15583 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -8,6 +8,11 @@ namespace VCGeneration; +class SplitOrigin : TokenWrapper, ImplementationPartOrigin { + public SplitOrigin(IToken inner) : base(inner) + { + } +} public static class ManualSplitFinder { public static List UpdateBlocks(IReadOnlyList blocks, Func> getCommands) @@ -164,8 +169,7 @@ private static bool ShouldSplitHere(Cmd c) { return null; } - var partToken = split == null ? partToSplit.Origin : new SplitOrigin(false, split.tok, blockWithSplit, partToSplit.Origin); - return createVc(partToken, newBlocks); + return createVc(new SplitOrigin(split?.tok ?? partToSplit.Origin), newBlocks); List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) { @@ -227,16 +231,12 @@ public static void AddJumpsToNewBlocks(Dictionary oldToNewBlockMap public interface ImplementationPartOrigin : IToken { } -public class SplitOrigin : TokenWrapper, ImplementationPartOrigin { - public bool Implicit { get; } +public class IsolateOrigin : TokenWrapper, ImplementationPartOrigin { public Block ContainingBlock { get; } public ImplementationPartOrigin PartThatWasSplit { get; } - public SplitOrigin(bool @implicit, IToken split, Block containingBlock, ImplementationPartOrigin partThatWasSplit) : base(split) { - Implicit = @implicit; + public IsolateOrigin(IToken split, Block containingBlock, ImplementationPartOrigin partThatWasSplit) : base(split) { ContainingBlock = containingBlock; PartThatWasSplit = partThatWasSplit; } - - public string KindName => Implicit ? "assertion" : "split"; } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/PathToken.cs b/Source/VCGeneration/Splits/PathToken.cs index 4823a47b8..8be192a2b 100644 --- a/Source/VCGeneration/Splits/PathToken.cs +++ b/Source/VCGeneration/Splits/PathToken.cs @@ -7,6 +7,24 @@ namespace VCGeneration; + +public class AssertPathOrigin : TokenWrapper, ImplementationPartOrigin { + + public AssertPathOrigin(AssertCmd assert, Block assertBlock, + ImmutableStack branches, DomRelation dominators) : base(assert.tok) + { + Assert = assert; + AssertBlock = assertBlock; + Branches = branches; + Dominators = dominators; + } + + public AssertCmd Assert { get; } + public Block AssertBlock { get; } + public ImmutableStack Branches { get; } + public DomRelation Dominators { get; } +} + public class PathOrigin : TokenWrapper, ImplementationPartOrigin { public PathOrigin(IToken inner, ImmutableStack branches, DomRelation dominators) : base(inner) { diff --git a/Test/implementationDivision/isolatedAssertion.bpl b/Test/implementationDivision/isolatedAssertion.bpl new file mode 100644 index 000000000..a729980aa --- /dev/null +++ b/Test/implementationDivision/isolatedAssertion.bpl @@ -0,0 +1,42 @@ +// RUN: %parallel-boogie /printSplit:%t /errorTrace:0 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure IsolatedAssertion(x: int, y: int) +{ + var z: int; + z := 0; + if (x > 0) { + z := z + 1; + } else { + z := z + 2; + } + + if (y > 0) { + z := z + 3; + } else { + z := z + 4; + } + assert z > 1; + assert {:isolate} z > 2; + assert z > 3; +} + +procedure IsolatedPathsAssertion(x: int, y: int) +{ + var z: int; + z := 0; + if (x > 0) { + z := z + 1; + } else { + z := z + 2; + } + + if (y > 0) { + z := z + 3; + } else { + z := z + 4; + } + assert z > 1; + assert {:isolate "paths"} z > 2; + assert z > 3; +} \ No newline at end of file diff --git a/Test/implementationDivision/isolatedAssertion.bpl.expect b/Test/implementationDivision/isolatedAssertion.bpl.expect new file mode 100644 index 000000000..71c2b6338 --- /dev/null +++ b/Test/implementationDivision/isolatedAssertion.bpl.expect @@ -0,0 +1,3 @@ +Focus.bpl(15,5): Error: this assertion could not be proved + +Boogie program verifier finished with 1 verified, 1 error From cf450a1b928b4270e542b36d4a4318be6908169f Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 1 Oct 2024 09:54:30 +0200 Subject: [PATCH 26/70] Move some code into DesugarReturns --- Source/VCGeneration/ConditionGeneration.cs | 122 ---------------- .../Transformations/DesugarReturns.cs | 131 ++++++++++++++++++ .../VerificationConditionGenerator.cs | 6 +- 3 files changed, 134 insertions(+), 125 deletions(-) create mode 100644 Source/VCGeneration/Transformations/DesugarReturns.cs diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 6846a7707..45ff736f7 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -258,74 +258,6 @@ protected static void InjectPreconditions(VCGenOptions options, ImplementationRu } } - /// - /// Modifies an implementation by inserting all postconditions - /// as assert statements at the end of the implementation - /// Returns the possibly-new unified exit block of the implementation - /// - /// - /// The unified exit block that has - /// already been constructed for the implementation (and so - /// is already an element of impl.Blocks) - /// - protected static void InjectPostConditions(VCGenOptions options, ImplementationRun run, Block unifiedExitBlock, - Dictionary gotoCmdOrigins) - { - var impl = run.Implementation; - Contract.Requires(impl != null); - Contract.Requires(unifiedExitBlock != null); - Contract.Requires(gotoCmdOrigins != null); - Contract.Requires(impl.Proc != null); - Contract.Requires(unifiedExitBlock.TransferCmd is ReturnCmd); - - TokenTextWriter debugWriter = null; - if (options.PrintWithUniqueASTIds) - { - debugWriter = new TokenTextWriter("", run.OutputWriter, /*setTokens=*/ false, /*pretty=*/ false, options); - debugWriter.WriteLine("Effective postcondition:"); - } - - Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); - - // (free and checked) ensures clauses - foreach (Ensures ens in impl.Proc.Ensures) - { - Contract.Assert(ens != null); - - if (!ens.Free) - { - Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); - Ensures ensCopy = (Ensures) cce.NonNull(ens.Clone()); - ensCopy.Condition = e; - AssertEnsuresCmd c = new AssertEnsuresCmd(ensCopy); - c.ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; - // Copy any {:id ...} from the postcondition to the assumption, so - // we can track it while analyzing verification coverage. - (c as ICarriesAttributes).CopyIdFrom(ens.tok, ens); - unifiedExitBlock.Cmds.Add(c); - if (debugWriter != null) - { - c.Emit(debugWriter, 1); - } - } - else if (ens.CanAlwaysAssume()) - { - Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); - unifiedExitBlock.Cmds.Add(new AssumeCmd(ens.tok, e)); - } - else - { - // skip free ensures if it doesn't have the :always_assume attr - } - } - - if (debugWriter != null) - { - debugWriter.WriteLine(); - } - } - - /// /// Get the pre-condition of an implementation, including the where clauses from the in-parameters. /// @@ -519,60 +451,6 @@ public static void EmitImpl(VCGenOptions options, ImplementationRun run, bool pr } - protected Block GenerateUnifiedExit(Implementation impl, Dictionary gotoCmdOrigins) - { - Contract.Requires(impl != null); - Contract.Requires(gotoCmdOrigins != null); - Contract.Ensures(Contract.Result() != null); - - Contract.Ensures(Contract.Result().TransferCmd is ReturnCmd); - Block /*?*/ - exitBlock = null; - - #region Create a unified exit block, if there's more than one - - { - int returnBlocks = 0; - foreach (Block b in impl.Blocks) - { - if (b.TransferCmd is ReturnCmd) - { - exitBlock = b; - returnBlocks++; - } - } - - if (returnBlocks > 1) - { - string unifiedExitLabel = "GeneratedUnifiedExit"; - 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 returnCmd) - { - List labels = new List(); - labels.Add(unifiedExitLabel); - List bs = new List(); - bs.Add(unifiedExit); - GotoCmd go = new GotoCmd(returnCmd.tok, labels, bs); - gotoCmdOrigins[go] = returnCmd; - b.TransferCmd = go; - unifiedExit.Predecessors.Add(b); - } - } - - exitBlock = unifiedExit; - impl.Blocks.Add(unifiedExit); - } - - Contract.Assert(exitBlock != null); - } - return exitBlock; - - #endregion - } public static void ResetPredecessors(List blocks) { diff --git a/Source/VCGeneration/Transformations/DesugarReturns.cs b/Source/VCGeneration/Transformations/DesugarReturns.cs new file mode 100644 index 000000000..3ee9ad52c --- /dev/null +++ b/Source/VCGeneration/Transformations/DesugarReturns.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using Microsoft.Boogie; +using VC; + +namespace VCGeneration.Transformations; + +public static class DesugarReturns { + public static Block GenerateUnifiedExit(Implementation impl, out Dictionary gotoCmdOrigins) + { + Contract.Requires(impl != null); + Contract.Requires(gotoCmdOrigins != null); + Contract.Ensures(Contract.Result() != null); + + gotoCmdOrigins = new(); + Contract.Ensures(Contract.Result().TransferCmd is ReturnCmd); + Block exitBlock = null; + + #region Create a unified exit block, if there's more than one + + { + int returnBlocks = 0; + foreach (Block b in impl.Blocks) + { + if (b.TransferCmd is ReturnCmd) + { + exitBlock = b; + returnBlocks++; + } + } + + if (returnBlocks > 1) + { + string unifiedExitLabel = "GeneratedUnifiedExit"; + 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 returnCmd) + { + List labels = new List(); + labels.Add(unifiedExitLabel); + List bs = new List(); + bs.Add(unifiedExit); + GotoCmd go = new GotoCmd(returnCmd.tok, labels, bs); + gotoCmdOrigins[go] = returnCmd; + b.TransferCmd = go; + unifiedExit.Predecessors.Add(b); + } + } + + exitBlock = unifiedExit; + impl.Blocks.Add(unifiedExit); + } + + Contract.Assert(exitBlock != null); + } + return exitBlock; + + #endregion + } + + /// + /// Modifies an implementation by inserting all postconditions + /// as assert statements at the end of the implementation + /// Returns the possibly-new unified exit block of the implementation + /// + /// + /// The unified exit block that has + /// already been constructed for the implementation (and so + /// is already an element of impl.Blocks) + /// + public static void InjectPostConditions(VCGenOptions options, ImplementationRun run, Block unifiedExitBlock, + Dictionary gotoCmdOrigins) + { + var impl = run.Implementation; + Contract.Requires(impl != null); + Contract.Requires(unifiedExitBlock != null); + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(impl.Proc != null); + Contract.Requires(unifiedExitBlock.TransferCmd is ReturnCmd); + + TokenTextWriter debugWriter = null; + if (options.PrintWithUniqueASTIds) + { + debugWriter = new TokenTextWriter("", run.OutputWriter, /*setTokens=*/ false, /*pretty=*/ false, options); + debugWriter.WriteLine("Effective postcondition:"); + } + + Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); + + // (free and checked) ensures clauses + foreach (Ensures ens in impl.Proc.Ensures) + { + Contract.Assert(ens != null); + + if (!ens.Free) + { + Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); + Ensures ensCopy = (Ensures) cce.NonNull(ens.Clone()); + ensCopy.Condition = e; + AssertEnsuresCmd c = new AssertEnsuresCmd(ensCopy); + c.ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; + // Copy any {:id ...} from the postcondition to the assumption, so + // we can track it while analyzing verification coverage. + (c as ICarriesAttributes).CopyIdFrom(ens.tok, ens); + unifiedExitBlock.Cmds.Add(c); + if (debugWriter != null) + { + c.Emit(debugWriter, 1); + } + } + else if (ens.CanAlwaysAssume()) + { + Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); + unifiedExitBlock.Cmds.Add(new AssumeCmd(ens.tok, e)); + } + else + { + // skip free ensures if it doesn't have the :always_assume attr + } + } + + if (debugWriter != null) + { + debugWriter.WriteLine(); + } + } +} \ No newline at end of file diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index dcb338473..67237d7e7 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -11,6 +11,7 @@ using System.Threading; using Microsoft.BaseTypes; using Microsoft.Boogie.VCExprAST; +using VCGeneration.Transformations; namespace VC { @@ -1293,8 +1294,7 @@ public Dictionary PassifyImpl(ImplementationRun run, out Contract.Ensures(Contract.Result>() != null); var impl = run.Implementation; - Dictionary gotoCmdOrigins = new Dictionary(); - Block exitBlock = GenerateUnifiedExit(impl, gotoCmdOrigins); + var exitBlock = DesugarReturns.GenerateUnifiedExit(impl, out var gotoCmdOrigins); #region Debug Tracing @@ -1349,7 +1349,7 @@ public Dictionary PassifyImpl(ImplementationRun run, out InjectPreconditions(Options, run, cc); // append postconditions, starting in exitBlock and continuing into other blocks, if needed - InjectPostConditions(Options, run, exitBlock, gotoCmdOrigins); + DesugarReturns.InjectPostConditions(Options, run, exitBlock, gotoCmdOrigins); } #endregion From 6c8ccafb45b9859bfd169107e1ca5f845b7f13b8 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 1 Oct 2024 12:11:50 +0200 Subject: [PATCH 27/70] Work on supporting isolate for return commands --- Source/Core/AST/Absy.cs | 65 ---------- Source/Core/AST/AbsyCmd.cs | 2 + Source/Core/AST/ICarriesAttributes.cs | 75 ++++++++++++ Source/Core/Generic/ListExtensions.cs | 31 +++++ Source/Core/Generic/OrderedDictionary.cs | 31 +++++ Source/Core/{ => Generic}/Util.cs | 0 Source/VCGeneration/Splits/BlockRewriter.cs | 40 +++++++ Source/VCGeneration/Splits/FocusApplier.cs | 2 +- ...andler.cs => IsolateAttributeOnAsserts.cs} | 113 +++++++----------- .../Splits/IsolateAttributeOnReturns.cs | 105 ++++++++++++++++ .../VCGeneration/Splits/ManualSplitFinder.cs | 12 +- .../Transformations/DesugarReturns.cs | 18 +-- 12 files changed, 338 insertions(+), 156 deletions(-) create mode 100644 Source/Core/AST/ICarriesAttributes.cs create mode 100644 Source/Core/Generic/ListExtensions.cs create mode 100644 Source/Core/Generic/OrderedDictionary.cs rename Source/Core/{ => Generic}/Util.cs (100%) create mode 100644 Source/VCGeneration/Splits/BlockRewriter.cs rename Source/VCGeneration/Splits/{IsolateAttributeHandler.cs => IsolateAttributeOnAsserts.cs} (60%) create mode 100644 Source/VCGeneration/Splits/IsolateAttributeOnReturns.cs diff --git a/Source/Core/AST/Absy.cs b/Source/Core/AST/Absy.cs index 3b0366d07..d8f53dbca 100644 --- a/Source/Core/AST/Absy.cs +++ b/Source/Core/AST/Absy.cs @@ -247,71 +247,6 @@ public void SetMetadata(int index, T value) #endregion } - public interface ICarriesAttributes - { - QKeyValue Attributes { get; set; } - - public void ResolveAttributes(ResolutionContext rc) - { - for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) - { - kv.Resolve(rc); - } - } - - public void TypecheckAttributes(TypecheckingContext tc) - { - var oldGlobalAccessOnlyInOld = tc.GlobalAccessOnlyInOld; - tc.GlobalAccessOnlyInOld = false; - for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) - { - kv.Typecheck(tc); - } - tc.GlobalAccessOnlyInOld = oldGlobalAccessOnlyInOld; - } - - public List FindLayers() - { - List layers = new List(); - for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) - { - if (kv.Key == CivlAttributes.LAYER) - { - layers.AddRange(kv.Params.Select(o => ((LiteralExpr)o).asBigNum.ToIntSafe)); - } - } - return layers.Distinct().OrderBy(l => l).ToList(); - } - - // Look for {:name string} in list of attributes. - public string FindStringAttribute(string name) - { - return QKeyValue.FindStringAttribute(Attributes, name); - } - - public void AddStringAttribute(IToken tok, string name, string parameter) - { - Attributes = new QKeyValue(tok, name, new List() {parameter}, Attributes); - } - - public void CopyIdFrom(IToken tok, ICarriesAttributes src) - { - var id = src.FindStringAttribute("id"); - if (id is not null) { - AddStringAttribute(tok, "id", id); - } - } - - public void CopyIdWithModificationsFrom(IToken tok, ICarriesAttributes src, Func modifier) - { - var id = src.FindStringAttribute("id"); - if (id is not null) { - AddStringAttribute(tok, "id", modifier(id).SolverLabel); - } - } - - } - [ContractClassFor(typeof(Absy))] public abstract class AbsyContracts : Absy { diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 728de48b0..a581f226d 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -3447,6 +3447,8 @@ public override void Emit(TokenTextWriter stream, int level) public class ReturnCmd : TransferCmd { + public QKeyValue Attributes { get; set; } + public ReturnCmd(IToken /*!*/ tok) : base(tok) { diff --git a/Source/Core/AST/ICarriesAttributes.cs b/Source/Core/AST/ICarriesAttributes.cs new file mode 100644 index 000000000..1f9bbf412 --- /dev/null +++ b/Source/Core/AST/ICarriesAttributes.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Boogie; + +public interface ICarriesAttributes +{ + QKeyValue Attributes { get; set; } + + public void ResolveAttributes(ResolutionContext rc) + { + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) + { + kv.Resolve(rc); + } + } + + public void TypecheckAttributes(TypecheckingContext tc) + { + var oldGlobalAccessOnlyInOld = tc.GlobalAccessOnlyInOld; + tc.GlobalAccessOnlyInOld = false; + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) + { + kv.Typecheck(tc); + } + tc.GlobalAccessOnlyInOld = oldGlobalAccessOnlyInOld; + } + + public List FindLayers() + { + List layers = new List(); + for (QKeyValue kv = this.Attributes; kv != null; kv = kv.Next) + { + if (kv.Key == CivlAttributes.LAYER) + { + layers.AddRange(kv.Params.Select(o => ((LiteralExpr)o).asBigNum.ToIntSafe)); + } + } + return layers.Distinct().OrderBy(l => l).ToList(); + } + + // Look for {:name string} in list of attributes. + public string FindStringAttribute(string name) + { + return QKeyValue.FindStringAttribute(Attributes, name); + } + + public void AddStringAttribute(IToken tok, string name, string parameter) + { + Attributes = new QKeyValue(tok, name, new List() {parameter}, Attributes); + } + + +} + +public static class CarriesAttributesExtensions { + + public static void CopyIdFrom(this ICarriesAttributes destination, IToken tok, ICarriesAttributes src) + { + var id = src.FindStringAttribute("id"); + if (id is not null) { + destination.AddStringAttribute(tok, "id", id); + } + } + + public static void CopyIdWithModificationsFrom(this ICarriesAttributes destination, IToken tok, + ICarriesAttributes src, Func modifier) + { + var id = src.FindStringAttribute("id"); + if (id is not null) { + destination.AddStringAttribute(tok, "id", modifier(id).SolverLabel); + } + } +} \ No newline at end of file diff --git a/Source/Core/Generic/ListExtensions.cs b/Source/Core/Generic/ListExtensions.cs new file mode 100644 index 000000000..6825839b8 --- /dev/null +++ b/Source/Core/Generic/ListExtensions.cs @@ -0,0 +1,31 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Boogie; + +public static class ListExtensions { + public static IReadOnlyList Reversed(this IReadOnlyList list) { + return new ReversedReadOnlyList(list); + } +} + +class ReversedReadOnlyList : IReadOnlyList { + private readonly IReadOnlyList inner; + + public ReversedReadOnlyList(IReadOnlyList inner) { + this.inner = inner; + } + + public IEnumerator GetEnumerator() { + return Enumerable.Range(0, inner.Count).Select(index => this[index]).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + public int Count => inner.Count; + + public T this[int index] => inner[^(index + 1)]; +} \ No newline at end of file diff --git a/Source/Core/Generic/OrderedDictionary.cs b/Source/Core/Generic/OrderedDictionary.cs new file mode 100644 index 000000000..7608b4f04 --- /dev/null +++ b/Source/Core/Generic/OrderedDictionary.cs @@ -0,0 +1,31 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Boogie; + +public class OrderedDictionary : IEnumerable> { + private readonly Dictionary mapping = new(); + private readonly List orderedKeys = new(); + + public void Add(TKey key, TValue value) { + orderedKeys.Add(key); + mapping[key] = value; + } + + public bool ContainsKey(TKey key) { + return mapping.ContainsKey(key); + } + + public TValue this[TKey key] => mapping[key]; + + public IEnumerable Keys => orderedKeys; + public IEnumerable Values => orderedKeys.Select(k => mapping[k]); + public IEnumerator> GetEnumerator() { + return mapping.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } +} \ No newline at end of file diff --git a/Source/Core/Util.cs b/Source/Core/Generic/Util.cs similarity index 100% rename from Source/Core/Util.cs rename to Source/Core/Generic/Util.cs diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs new file mode 100644 index 000000000..b3c966d20 --- /dev/null +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Boogie; + +namespace VCGeneration.Splits; + +public class BlockRewriter { + public static OrderedDictionary UpdateBlocks(Stack blocksToVisit, + HashSet visitedBlocks, Func> getCommands) + { + var oldToNewBlockMap = new OrderedDictionary(); + while(blocksToVisit.Any()) { + var oldBlock = blocksToVisit.Pop(); + if (!visitedBlocks.Add(oldBlock)) { + continue; + } + + var newBlock = new Block(oldBlock.tok) + { + Label = oldBlock.Label + }; + oldToNewBlockMap.Add(oldBlock, newBlock); + newBlock.Cmds = getCommands(oldBlock); + foreach (var previous in oldBlock.Predecessors) { + blocksToVisit.Push(previous); + } + + if (oldBlock.TransferCmd is GotoCmd gtc) + { + var targets = gtc.LabelTargets.Where(oldToNewBlockMap.ContainsKey).ToList(); + newBlock.TransferCmd = new GotoCmd(gtc.tok, + targets.Select(block => oldToNewBlockMap[block].Label).ToList(), + targets.Select(block => oldToNewBlockMap[block]).ToList()); + } + } + + return oldToNewBlockMap; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Splits/FocusApplier.cs b/Source/VCGeneration/Splits/FocusApplier.cs index 17129896d..8ed0fcc46 100644 --- a/Source/VCGeneration/Splits/FocusApplier.cs +++ b/Source/VCGeneration/Splits/FocusApplier.cs @@ -85,7 +85,7 @@ void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet } } - private static List ComputeNewBlocks(VCGenOptions options, ISet blocksToInclude, List blocksReversed, + public static List ComputeNewBlocks(VCGenOptions options, ISet blocksToInclude, IEnumerable blocksReversed, ISet freeAssumeBlocks) { var duplicator = new Duplicator(); diff --git a/Source/VCGeneration/Splits/IsolateAttributeHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAsserts.cs similarity index 60% rename from Source/VCGeneration/Splits/IsolateAttributeHandler.cs rename to Source/VCGeneration/Splits/IsolateAttributeOnAsserts.cs index a5566b029..0f5adc45a 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnAsserts.cs @@ -5,11 +5,27 @@ using System.Linq; using Microsoft.Boogie; using VC; +using VCGeneration.Splits; namespace VCGeneration; -static class IsolateAttributeHandler { - public static List GetPartsFromIsolatedAssertions(VCGenOptions options, ManualSplit partToDivide, +class IsolateAttributeOnAsserts { + private readonly Dictionary assumedAssertions = new(); + private readonly VCGenOptions options; + + public IsolateAttributeOnAsserts(VCGenOptions options) { + this.options = options; + } + + private Cmd TransformAssertCmd(Cmd cmd) { + if (cmd is AssertCmd assertCmd) { + return assumedAssertions.GetOrCreate(assertCmd, () => VerificationConditionGenerator.AssertTurnedIntoAssume(options, assertCmd)); + } + + return cmd; + } + + public List GetPartsFromIsolatedAssertions(ManualSplit partToDivide, Func, ManualSplit> createSplit) { var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; @@ -19,23 +35,12 @@ public static List GetPartsFromIsolatedAssertions(VCGenOptions opti var results = new List(); var dag = Program.GraphFromBlocks(partToDivide.Blocks); - var assumedAssertions = new Dictionary(); foreach (var block in partToDivide.Blocks) { foreach (var assert in block.Cmds.OfType()) { - var isolateAttribute = QKeyValue.FindAttribute(assert.Attributes, p => p.Key == "isolate"); - if (splitOnEveryAssert) { - if (isolateAttribute != null) { - if (isolateAttribute.Params.OfType().Any(p => Equals(p, "none"))) { - continue; - } - // isolate - } - // isolate - } else { - if (isolateAttribute == null) { - continue; - } - // isolate + var attributes = assert.Attributes; + var isolateAttribute = QKeyValue.FindAttribute(attributes, p => p.Key == "isolate"); + if (!ShouldIsolate(splitOnEveryAssert, isolateAttribute)) { + continue; } isolatedAssertions.Add(assert); @@ -51,14 +56,6 @@ public static List GetPartsFromIsolatedAssertions(VCGenOptions opti return results; - Cmd TransformAssertCmd(Cmd cmd) { - if (cmd is AssertCmd assertCmd) { - return assumedAssertions.GetOrCreate(assertCmd, () => VerificationConditionGenerator.AssertTurnedIntoAssume(options, assertCmd)); - } - - return cmd; - } - IEnumerable GetSplitsForIsolatedPathAssertion(Block blockWithAssert, AssertCmd assertCmd) { var blockToVisit = new Stack>(); blockToVisit.Push(ImmutableStack.Create(new Block(blockWithAssert.tok) @@ -87,7 +84,7 @@ IEnumerable GetSplitsForIsolatedPathAssertion(Block blockWithAssert }; if (newPrevious.TransferCmd is GotoCmd gotoCmd) { newPrevious.TransferCmd = - new GotoCmd(gotoCmd.tok, new List() { firstBlock.Label }, new List + new GotoCmd(gotoCmd.tok, new List { firstBlock.Label }, new List { firstBlock }); @@ -98,42 +95,13 @@ IEnumerable GetSplitsForIsolatedPathAssertion(Block blockWithAssert } ManualSplit GetSplitForIsolatedAssertion(Block blockWithAssert, AssertCmd assertCmd) { - var newBlocks = new List(); - var oldToNewBlockMap = new Dictionary(); - var blockToVisit = new Stack(); - blockToVisit.Push(blockWithAssert); - var visitedBlocks = new HashSet(); - while(blockToVisit.Any()) { - var oldBlock = blockToVisit.Pop(); - if (!visitedBlocks.Add(oldBlock)) { - continue; - } - - var newBlock = new Block(oldBlock.tok) - { - Label = oldBlock.Label, - TransferCmd = oldBlock.TransferCmd - }; - oldToNewBlockMap[oldBlock] = newBlock; - - newBlocks.Add(newBlock); - newBlock.Cmds = oldBlock == blockWithAssert - ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) - : oldBlock.Cmds.Select(TransformAssertCmd).ToList(); - foreach (var previous in oldBlock.Predecessors) { - blockToVisit.Push(previous); - } - - if (newBlock.TransferCmd is GotoCmd gtc) - { - var targets = gtc.LabelTargets.Where(oldToNewBlockMap.ContainsKey).ToList(); - newBlock.TransferCmd = new GotoCmd(gtc.tok, - targets.Select(block => oldToNewBlockMap[block].Label).ToList(), - targets.Select(block => oldToNewBlockMap[block]).ToList()); - } - } - - return createSplit(new IsolatedAssertionOrigin(assertCmd), newBlocks.OrderBy(b => b.tok).ToList()); + var blocksToVisit = new Stack(new[] { blockWithAssert }); + var orderedNewBlocks = BlockRewriter.UpdateBlocks(blocksToVisit, + new HashSet(), + oldBlock => oldBlock == blockWithAssert + ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) + : oldBlock.Cmds.Select(TransformAssertCmd).ToList()); + return createSplit(new IsolatedAssertionOrigin(assertCmd), orderedNewBlocks.Values.OrderBy(b => b.tok).ToList()); } List GetCommandsForBlockWithAssert(Block currentBlock, AssertCmd assert) @@ -156,18 +124,23 @@ ManualSplit GetSplitWithoutIsolatedAssertions() { return createSplit(origin, partToDivide.Blocks); } - // TODO don't copy list if it is unchanged. var newBlocks = ManualSplitFinder.UpdateBlocks(partToDivide.Blocks, - block => block.Cmds.Select(cmd => { - if (isolatedAssertions.Contains(cmd)) { - return TransformAssertCmd(cmd); - } - - return cmd; - }).ToList()); + block => block.Cmds.Select(cmd => isolatedAssertions.Contains(cmd) ? TransformAssertCmd(cmd) : cmd).ToList()); return createSplit(origin, newBlocks); } } + + private static bool ShouldIsolate(bool splitOnEveryAssert, QKeyValue? isolateAttribute) { + if (splitOnEveryAssert) { + if (isolateAttribute == null) { + return true; + } + + return !isolateAttribute.Params.OfType().Any(p => Equals(p, "none")); + } + + return isolateAttribute != null; + } } diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnReturns.cs b/Source/VCGeneration/Splits/IsolateAttributeOnReturns.cs new file mode 100644 index 000000000..d2638979c --- /dev/null +++ b/Source/VCGeneration/Splits/IsolateAttributeOnReturns.cs @@ -0,0 +1,105 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.Boogie; +using VC; +using VCGeneration.Splits; + +namespace VCGeneration; + +class IsolateAttributeOnReturns { + private readonly Dictionary assumedAssertions = new(); + private readonly VCGenOptions options; + + public IsolateAttributeOnReturns(VCGenOptions options) { + this.options = options; + } + + private Cmd TransformAssertCmd(Cmd cmd) { + if (cmd is AssertCmd assertCmd) { + return assumedAssertions.GetOrCreate(assertCmd, () => VerificationConditionGenerator.AssertTurnedIntoAssume(options, assertCmd)); + } + + return cmd; + } + + public List GetPartsFromIsolatedReturns( + Dictionary gotoToOriginalReturn, + ManualSplit partToDivide, + Func, ManualSplit> createSplit) { + + var results = new List(); + var blocks = partToDivide.Blocks; + var dag = Program.GraphFromBlocks(blocks); + var reversedBlocks = dag.TopologicalSort().Reversed(); + + var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; + partToDivide.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); + + var isolatedBlocks = new HashSet(); + + foreach (var block in partToDivide.Blocks) { + if (block.TransferCmd is not GotoCmd gotoCmd) { + continue; + } + + if (!gotoToOriginalReturn.TryGetValue(gotoCmd, out var returnCmd)) { + continue; + } + + var isolateAttribute = QKeyValue.FindAttribute(returnCmd.Attributes, p => p.Key == "isolate"); + var isolate = ShouldIsolate(splitOnEveryAssert, isolateAttribute); + if (!isolate) { + continue; + } + + // TODO support isolate paths for returns + isolatedBlocks.Add(block); + var ancestors = dag.ComputeReachability(block, false); + var descendants = dag.ComputeReachability(block, true); + var blocksToInclude = ancestors.Union(descendants).ToHashSet(); + var newBlocks = FocusApplier.ComputeNewBlocks(options, blocksToInclude, + reversedBlocks, ancestors.ToHashSet()); + var partFromIsolatedReturn = createSplit(new ReturnOrigin(gotoToOriginalReturn[gotoCmd]), newBlocks); + results.Add(partFromIsolatedReturn); + } + + results.Add(GetPartWithoutIsolatedReturns()); + + return results; + + ManualSplit GetPartWithoutIsolatedReturns() { + var newBlocks = BlockRewriter.UpdateBlocks(new Stack(reversedBlocks), new HashSet(), + oldBlock => oldBlock.Cmds.Select(TransformAssertCmd).ToList()); + foreach (var (oldBlock, newBlock) in newBlocks) { + if (isolatedBlocks.Contains(oldBlock)) { + newBlock.TransferCmd = null; + } + } + return createSplit(new ImplementationRootOrigin(partToDivide.Implementation), newBlocks.Values.ToList()); + } + } + + private static bool ShouldIsolate(bool splitOnEveryAssert, QKeyValue? isolateAttribute) { + if (splitOnEveryAssert) { + if (isolateAttribute == null) { + return true; + } + + return !isolateAttribute.Params.OfType().Any(p => Equals(p, "none")); + } + + return isolateAttribute != null; + } +} + + +public class ReturnOrigin : TokenWrapper, ImplementationPartOrigin { + public ReturnCmd IsolatedReturn { get; } + + public ReturnOrigin(ReturnCmd isolatedReturn) : base(isolatedReturn.tok) { + this.IsolatedReturn = isolatedReturn; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 186b15583..cb22bbe81 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -38,7 +38,7 @@ public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions op Func, ManualSplit> createSplit) { var focussedParts = FocusApplier.GetFocusParts(options, run, createSplit); var isolatedParts = focussedParts.SelectMany(s => - IsolateAttributeHandler.GetPartsFromIsolatedAssertions(options, s, createSplit)).ToList(); + new IsolateAttributeOnAsserts(options).GetPartsFromIsolatedAssertions(s, createSplit)).ToList(); if (!isolatedParts.Any()) { return Enumerable.Empty(); @@ -229,14 +229,4 @@ public static void AddJumpsToNewBlocks(Dictionary oldToNewBlockMap } public interface ImplementationPartOrigin : IToken { -} - -public class IsolateOrigin : TokenWrapper, ImplementationPartOrigin { - public Block ContainingBlock { get; } - public ImplementationPartOrigin PartThatWasSplit { get; } - - public IsolateOrigin(IToken split, Block containingBlock, ImplementationPartOrigin partThatWasSplit) : base(split) { - ContainingBlock = containingBlock; - PartThatWasSplit = partThatWasSplit; - } } \ No newline at end of file diff --git a/Source/VCGeneration/Transformations/DesugarReturns.cs b/Source/VCGeneration/Transformations/DesugarReturns.cs index 3ee9ad52c..7335b1b3b 100644 --- a/Source/VCGeneration/Transformations/DesugarReturns.cs +++ b/Source/VCGeneration/Transformations/DesugarReturns.cs @@ -89,7 +89,7 @@ public static void InjectPostConditions(VCGenOptions options, ImplementationRun debugWriter.WriteLine("Effective postcondition:"); } - Substitution formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); + var formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); // (free and checked) ensures clauses foreach (Ensures ens in impl.Proc.Ensures) @@ -98,18 +98,18 @@ public static void InjectPostConditions(VCGenOptions options, ImplementationRun if (!ens.Free) { - Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); - Ensures ensCopy = (Ensures) cce.NonNull(ens.Clone()); - ensCopy.Condition = e; - AssertEnsuresCmd c = new AssertEnsuresCmd(ensCopy); - c.ErrorDataEnhanced = ensCopy.ErrorDataEnhanced; + var ensuresCopy = (Ensures) cce.NonNull(ens.Clone()); + ensuresCopy.Condition = Substituter.Apply(formalProcImplSubst, ens.Condition); + AssertEnsuresCmd assert = new AssertEnsuresCmd(ensuresCopy) { + ErrorDataEnhanced = ensuresCopy.ErrorDataEnhanced + }; // Copy any {:id ...} from the postcondition to the assumption, so // we can track it while analyzing verification coverage. - (c as ICarriesAttributes).CopyIdFrom(ens.tok, ens); - unifiedExitBlock.Cmds.Add(c); + assert.CopyIdFrom(ens.tok, ens); + unifiedExitBlock.Cmds.Add(assert); if (debugWriter != null) { - c.Emit(debugWriter, 1); + assert.Emit(debugWriter, 1); } } else if (ens.CanAlwaysAssume()) From 5bbe9719f0cc8c14b316196a4463cb044b557927 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 1 Oct 2024 13:27:10 +0200 Subject: [PATCH 28/70] Refactoring --- Source/Core/AST/AbsyCmd.cs | 2 + Source/Core/AST/Program.cs | 2 +- Source/ExecutionEngine/ExecutionEngine.cs | 2 +- Source/VCGeneration/Splits/BlockRewriter.cs | 118 +++++++++ ...cusApplier.cs => FocusAttributeHandler.cs} | 72 +----- .../Splits/IsolateAttributeOnAsserts.cs | 153 ------------ .../IsolateAttributeOnAssertsHandler.cs | 106 ++++++++ ...s.cs => IsolateAttributeOnJumpsHandler.cs} | 55 ++--- .../VCGeneration/Splits/ManualSplitFinder.cs | 228 ++---------------- .../Splits/SplitAndVerifyWorker.cs | 2 +- .../Splits/SplitAttributeHandler.cs | 223 +++++++++++++++++ 11 files changed, 504 insertions(+), 459 deletions(-) rename Source/VCGeneration/Splits/{FocusApplier.cs => FocusAttributeHandler.cs} (68%) delete mode 100644 Source/VCGeneration/Splits/IsolateAttributeOnAsserts.cs create mode 100644 Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs rename Source/VCGeneration/Splits/{IsolateAttributeOnReturns.cs => IsolateAttributeOnJumpsHandler.cs} (60%) create mode 100644 Source/VCGeneration/Splits/SplitAttributeHandler.cs diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index a581f226d..784843309 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -3480,6 +3480,8 @@ public class GotoCmd : TransferCmd [Rep] public List LabelNames; [Rep] public List LabelTargets; + public QKeyValue Attributes { get; set; } + [ContractInvariantMethod] void ObjectInvariant() { diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index 45011b574..3ed1e94ac 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -498,7 +498,7 @@ public static Graph BuildCallGraph(CoreOptions options, Program return callGraph; } - public static Graph GraphFromBlocks(List blocks, bool forward = true) + public static Graph GraphFromBlocks(IReadOnlyList blocks, bool forward = true) { var result = new Graph(); if (!blocks.Any()) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 327304aff..7657382c6 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -744,7 +744,7 @@ public async Task> GetVerificationTasks(Program out var modelViewInfo); ConditionGeneration.ResetPredecessors(run.Implementation.Blocks); - var splits = ManualSplitFinder.SplitOnPathsAndAssertions(Options, run, + var splits = ManualSplitFinder.GetParts(Options, run, (token, blocks) => new ManualSplit(Options, () => blocks, gotoCmdOrigins, vcGenerator, run, token)).ToList(); for (var index = 0; index < splits.Count; index++) { var split = splits[index]; diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index b3c966d20..8f57cff8b 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -1,11 +1,117 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.Boogie; +using Microsoft.Boogie.GraphUtil; +using VC; namespace VCGeneration.Splits; public class BlockRewriter { + private readonly Dictionary assumedAssertions = new(); + public VCGenOptions Options { get; } + private readonly Graph dag; + public Func, ManualSplit> CreateSplit { get; } + + public BlockRewriter(VCGenOptions options, List blocks, Func, ManualSplit> createSplit) { + this.Options = options; + CreateSplit = createSplit; + dag = Program.GraphFromBlocks(blocks); + } + + public Cmd TransformAssertCmd(Cmd cmd) { + if (cmd is AssertCmd assertCmd) { + return assumedAssertions.GetOrCreate(assertCmd, () => VerificationConditionGenerator.AssertTurnedIntoAssume(Options, assertCmd)); + } + + return cmd; + } + + public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, ISet? blocksToInclude, IToken origin) { + var blockToVisit = new Stack>(); + blockToVisit.Push(ImmutableStack.Create(new Block(lastBlock.tok) + { + Predecessors = lastBlock.Predecessors, + Label = lastBlock.Label, + TransferCmd = null, + Cmds = lastBlock.Cmds + })); + + while(blockToVisit.Any()) { + var path = blockToVisit.Pop(); + var firstBlock = path.Peek(); + IEnumerable predecessors = firstBlock.Predecessors; + if (blocksToInclude != null) { + predecessors = predecessors.Where(blocksToInclude.Contains); + } + + var hadPredecessors = false; + foreach (var oldPrevious in predecessors) { + hadPredecessors = true; + var newPrevious = new Block(oldPrevious.tok) + { + Predecessors = oldPrevious.Predecessors, + Label = oldPrevious.Label, + TransferCmd = oldPrevious.TransferCmd, + Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList() + }; + if (newPrevious.TransferCmd is GotoCmd gotoCmd) { + newPrevious.TransferCmd = + new GotoCmd(gotoCmd.tok, new List { firstBlock.Label }, new List + { + firstBlock + }); + } + blockToVisit.Push(path.Push(newPrevious)); + } + if (!hadPredecessors) { + yield return CreateSplit(new PathOrigin(origin, path, dag.DominatorMap), new List { new(firstBlock.tok) { + Label = firstBlock.Label, + Cmds = path.SelectMany(b => b.Cmds).ToList() + } }); + } + } + } + + public List ComputeNewBlocks(ISet blocksToInclude, IEnumerable blocksReversed, + ISet freeAssumeBlocks) + { + var duplicator = new Duplicator(); + var newBlocks = new List(); + var oldToNewBlockMap = new Dictionary(blocksToInclude.Count); + + // TODO, use ManualSplitFinder.CreateSplit() + // Traverse backwards to allow settings the jumps to the new blocks + foreach (var block in blocksReversed) + { + if (!blocksToInclude.Contains(block)) { + continue; + } + var newBlock = (Block)duplicator.Visit(block); + newBlocks.Add(newBlock); + oldToNewBlockMap[block] = newBlock; + // freeBlocks consist of the predecessors of the relevant foci. + // Their assertions turn into assumes and any splits inside them are disabled. + if(freeAssumeBlocks.Contains(block)) + { + newBlock.Cmds = block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).Select(DisableSplits).ToList(); + } + if (block.TransferCmd is GotoCmd gtc) + { + var targets = gtc.LabelTargets.Where(blocksToInclude.Contains).ToList(); + newBlock.TransferCmd = new GotoCmd(gtc.tok, + targets.Select(blk => oldToNewBlockMap[blk].Label).ToList(), + targets.Select(blk => oldToNewBlockMap[blk]).ToList()); + } + } + newBlocks.Reverse(); + + // TODO remove? + BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); + return newBlocks; + } + public static OrderedDictionary UpdateBlocks(Stack blocksToVisit, HashSet visitedBlocks, Func> getCommands) { @@ -37,4 +143,16 @@ public static OrderedDictionary UpdateBlocks(Stack blocksTo return oldToNewBlockMap; } + + private static Cmd DisableSplits(Cmd command) + { + if (command is not PredicateCmd pc) + { + return command; + } + + pc.Attributes = new QKeyValue(Token.NoToken, "split", + new List { new LiteralExpr(Token.NoToken, false) }, pc.Attributes); + return command; + } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/FocusApplier.cs b/Source/VCGeneration/Splits/FocusAttributeHandler.cs similarity index 68% rename from Source/VCGeneration/Splits/FocusApplier.cs rename to Source/VCGeneration/Splits/FocusAttributeHandler.cs index 8ed0fcc46..7385bfb73 100644 --- a/Source/VCGeneration/Splits/FocusApplier.cs +++ b/Source/VCGeneration/Splits/FocusAttributeHandler.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.Boogie; using VC; +using VCGeneration.Splits; using Block = Microsoft.Boogie.Block; using Cmd = Microsoft.Boogie.Cmd; using PredicateCmd = Microsoft.Boogie.PredicateCmd; @@ -12,15 +13,18 @@ namespace VCGeneration; -public static class FocusApplier -{ - +public class FocusAttributeHandler { + private readonly BlockRewriter rewriter; + + public FocusAttributeHandler(BlockRewriter rewriter) { + this.rewriter = rewriter; + } + /// /// Each focus block creates two options. /// We recurse twice for each focus, leading to potentially 2^N splits /// - public static List GetFocusParts(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createPart) + public List GetParts(ImplementationRun run) { var implementation = run.Implementation; var dag = Program.GraphFromImpl(implementation); @@ -29,11 +33,11 @@ public static List GetFocusParts(VCGenOptions options, Implementati // By default, we process the foci in a top-down fashion, i.e., in the topological order. // If the user sets the RelaxFocus flag, we use the reverse (topological) order. var focusBlocks = GetFocusBlocks(topologicallySortedBlocks); - if (options.RelaxFocus) { + if (rewriter.Options.RelaxFocus) { focusBlocks.Reverse(); } if (!focusBlocks.Any()) { - return new List { createPart(new ImplementationRootOrigin(run.Implementation), implementation.Blocks) }; + return new List { rewriter.CreateSplit(new ImplementationRootOrigin(run.Implementation), implementation.Blocks) }; } var ancestorsPerBlock = new Dictionary>(); @@ -53,11 +57,11 @@ public static List GetFocusParts(VCGenOptions options, Implementati void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet blocksToInclude, ISet freeAssumeBlocks) { var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; if (allFocusBlocksHaveBeenProcessed) { - var newBlocks = ComputeNewBlocks(options, blocksToInclude, blocksReversed, freeAssumeBlocks); + var newBlocks = rewriter.ComputeNewBlocks(blocksToInclude, blocksReversed, freeAssumeBlocks); ImplementationPartOrigin token = path.Any() ? new PathOrigin(run.Implementation.tok, path, dominators) : new ImplementationRootOrigin(run.Implementation); - result.Add(createPart(token, newBlocks)); + result.Add(rewriter.CreateSplit(token, newBlocks)); } else { var (focusBlock, nextToken) = focusBlocks[focusIndex]; // assert b in blocks if (!blocksToInclude.Contains(focusBlock) || freeAssumeBlocks.Contains(focusBlock)) @@ -85,44 +89,6 @@ void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet } } - public static List ComputeNewBlocks(VCGenOptions options, ISet blocksToInclude, IEnumerable blocksReversed, - ISet freeAssumeBlocks) - { - var duplicator = new Duplicator(); - var newBlocks = new List(); - var oldToNewBlockMap = new Dictionary(blocksToInclude.Count); - - // TODO, use ManualSplitFinder.CreateSplit() - // Traverse backwards to allow settings the jumps to the new blocks - foreach (var block in blocksReversed) - { - if (!blocksToInclude.Contains(block)) { - continue; - } - var newBlock = (Block)duplicator.Visit(block); - newBlocks.Add(newBlock); - oldToNewBlockMap[block] = newBlock; - // freeBlocks consist of the predecessors of the relevant foci. - // Their assertions turn into assumes and any splits inside them are disabled. - if(freeAssumeBlocks.Contains(block)) - { - newBlock.Cmds = block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(options, c)).Select(DisableSplits).ToList(); - } - if (block.TransferCmd is GotoCmd gtc) - { - var targets = gtc.LabelTargets.Where(blocksToInclude.Contains).ToList(); - newBlock.TransferCmd = new GotoCmd(gtc.tok, - targets.Select(blk => oldToNewBlockMap[blk].Label).ToList(), - targets.Select(blk => oldToNewBlockMap[blk]).ToList()); - } - } - newBlocks.Reverse(); - - // TODO remove? - BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); - return newBlocks; - } - // finds all the blocks dominated by focusBlock in the subgraph // which only contains vertices of subgraph. private static HashSet DominatedBlocks(List topologicallySortedBlocks, Block focusBlock, ISet subgraph) @@ -142,18 +108,6 @@ private static HashSet DominatedBlocks(List topologicallySortedBlo } return subgraph.Where(blk => dominatorsPerBlock[blk].Contains(focusBlock)).ToHashSet(); } - - private static Cmd DisableSplits(Cmd command) - { - if (command is not PredicateCmd pc) - { - return command; - } - - pc.Attributes = new QKeyValue(Token.NoToken, "split", - new List { new LiteralExpr(Token.NoToken, false) }, pc.Attributes); - return command; - } private static List<(Block Block, IToken Token)> GetFocusBlocks(List blocks) { return blocks. diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnAsserts.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAsserts.cs deleted file mode 100644 index 0f5adc45a..000000000 --- a/Source/VCGeneration/Splits/IsolateAttributeOnAsserts.cs +++ /dev/null @@ -1,153 +0,0 @@ -#nullable enable -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using Microsoft.Boogie; -using VC; -using VCGeneration.Splits; - -namespace VCGeneration; - -class IsolateAttributeOnAsserts { - private readonly Dictionary assumedAssertions = new(); - private readonly VCGenOptions options; - - public IsolateAttributeOnAsserts(VCGenOptions options) { - this.options = options; - } - - private Cmd TransformAssertCmd(Cmd cmd) { - if (cmd is AssertCmd assertCmd) { - return assumedAssertions.GetOrCreate(assertCmd, () => VerificationConditionGenerator.AssertTurnedIntoAssume(options, assertCmd)); - } - - return cmd; - } - - public List GetPartsFromIsolatedAssertions(ManualSplit partToDivide, - Func, ManualSplit> createSplit) { - - var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; - partToDivide.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); - - var isolatedAssertions = new HashSet(); - var results = new List(); - var dag = Program.GraphFromBlocks(partToDivide.Blocks); - - foreach (var block in partToDivide.Blocks) { - foreach (var assert in block.Cmds.OfType()) { - var attributes = assert.Attributes; - var isolateAttribute = QKeyValue.FindAttribute(attributes, p => p.Key == "isolate"); - if (!ShouldIsolate(splitOnEveryAssert, isolateAttribute)) { - continue; - } - - isolatedAssertions.Add(assert); - if (isolateAttribute != null && isolateAttribute.Params.OfType().Any(p => Equals(p, "paths"))) { - results.AddRange(GetSplitsForIsolatedPathAssertion(block, assert)); - } else { - results.Add(GetSplitForIsolatedAssertion(block, assert)); - } - } - } - - results.Add(GetSplitWithoutIsolatedAssertions()); - - return results; - - IEnumerable GetSplitsForIsolatedPathAssertion(Block blockWithAssert, AssertCmd assertCmd) { - var blockToVisit = new Stack>(); - blockToVisit.Push(ImmutableStack.Create(new Block(blockWithAssert.tok) - { - Predecessors = blockWithAssert.Predecessors, - Label = blockWithAssert.Label, - TransferCmd = new GotoCmd(Token.NoToken, new List(), new List()), - Cmds = GetCommandsForBlockWithAssert(blockWithAssert, assertCmd) - })); - while(blockToVisit.Any()) { - var path = blockToVisit.Pop(); - var firstBlock = path.Peek(); - if (!firstBlock.Predecessors.Any()) { - yield return createSplit(new PathOrigin(assertCmd.tok, path, dag.DominatorMap), new List() { new(firstBlock.tok) { - Label = firstBlock.Label, - Cmds = path.SelectMany(b => b.Cmds).ToList() - } }); - } - foreach (var oldPrevious in firstBlock.Predecessors) { - var newPrevious = new Block(oldPrevious.tok) - { - Predecessors = oldPrevious.Predecessors, - Label = oldPrevious.Label, - TransferCmd = oldPrevious.TransferCmd, - Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList() - }; - if (newPrevious.TransferCmd is GotoCmd gotoCmd) { - newPrevious.TransferCmd = - new GotoCmd(gotoCmd.tok, new List { firstBlock.Label }, new List - { - firstBlock - }); - } - blockToVisit.Push(path.Push(newPrevious)); - } - } - } - - ManualSplit GetSplitForIsolatedAssertion(Block blockWithAssert, AssertCmd assertCmd) { - var blocksToVisit = new Stack(new[] { blockWithAssert }); - var orderedNewBlocks = BlockRewriter.UpdateBlocks(blocksToVisit, - new HashSet(), - oldBlock => oldBlock == blockWithAssert - ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) - : oldBlock.Cmds.Select(TransformAssertCmd).ToList()); - return createSplit(new IsolatedAssertionOrigin(assertCmd), orderedNewBlocks.Values.OrderBy(b => b.tok).ToList()); - } - - List GetCommandsForBlockWithAssert(Block currentBlock, AssertCmd assert) - { - var result = new List(); - foreach (var command in currentBlock.Cmds) { - if (assert == command) { - result.Add(command); - break; - } - result.Add(TransformAssertCmd(command)); - } - - return result; - } - - ManualSplit GetSplitWithoutIsolatedAssertions() { - var origin = new ImplementationRootOrigin(partToDivide.Implementation); - if (!isolatedAssertions.Any()) { - return createSplit(origin, partToDivide.Blocks); - } - - var newBlocks = ManualSplitFinder.UpdateBlocks(partToDivide.Blocks, - block => block.Cmds.Select(cmd => isolatedAssertions.Contains(cmd) ? TransformAssertCmd(cmd) : cmd).ToList()); - return createSplit(origin, newBlocks); - } - } - - private static bool ShouldIsolate(bool splitOnEveryAssert, QKeyValue? isolateAttribute) { - if (splitOnEveryAssert) { - if (isolateAttribute == null) { - return true; - } - - return !isolateAttribute.Params.OfType().Any(p => Equals(p, "none")); - } - - return isolateAttribute != null; - } -} - - -public class IsolatedAssertionOrigin : TokenWrapper, ImplementationPartOrigin { - public AssertCmd IsolatedAssert { get; } - - public IsolatedAssertionOrigin(AssertCmd isolatedAssert) : base(isolatedAssert.tok) { - this.IsolatedAssert = isolatedAssert; - } -} \ No newline at end of file diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs new file mode 100644 index 000000000..72090ef44 --- /dev/null +++ b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs @@ -0,0 +1,106 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Boogie; +using VC; +using VCGeneration.Splits; + +namespace VCGeneration; + +class IsolateAttributeOnAssertsHandler { + private readonly BlockRewriter rewriter; + + public IsolateAttributeOnAssertsHandler(BlockRewriter rewriter) { + this.rewriter = rewriter; + } + + public (List IsolatedParts, ManualSplit Remainder) GetParts(ManualSplit partToDivide) { + + var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; + partToDivide.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); + + var isolatedAssertions = new HashSet(); + var results = new List(); + + foreach (var block in partToDivide.Blocks) { + foreach (var assert in block.Cmds.OfType()) { + var attributes = assert.Attributes; + var isolateAttribute = QKeyValue.FindAttribute(attributes, p => p.Key == "isolate"); + if (!ShouldIsolate(splitOnEveryAssert, isolateAttribute)) { + continue; + } + + isolatedAssertions.Add(assert); + if (isolateAttribute != null && isolateAttribute.Params.OfType().Any(p => Equals(p, "paths"))) { + results.AddRange(rewriter.GetSplitsForIsolatedPaths(block, null, assert.tok).Select(p => { + var newAssertBlock = p.Blocks.Last(); + newAssertBlock.Cmds = GetCommandsForBlockWithAssert(newAssertBlock, assert); + return p; + })); + } else { + results.Add(GetSplitForIsolatedAssertion(block, assert)); + } + } + } + + return (results,GetSplitWithoutIsolatedAssertions()); + + + ManualSplit GetSplitForIsolatedAssertion(Block blockWithAssert, AssertCmd assertCmd) { + var blocksToVisit = new Stack(new[] { blockWithAssert }); + var orderedNewBlocks = BlockRewriter.UpdateBlocks(blocksToVisit, + new HashSet(), + oldBlock => oldBlock == blockWithAssert + ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) + : oldBlock.Cmds.Select(rewriter.TransformAssertCmd).ToList()); + return rewriter.CreateSplit(new IsolatedAssertionOrigin(assertCmd), orderedNewBlocks.Values.OrderBy(b => b.tok).ToList()); + } + + List GetCommandsForBlockWithAssert(Block currentBlock, AssertCmd assert) + { + var result = new List(); + foreach (var command in currentBlock.Cmds) { + if (assert == command) { + result.Add(command); + break; + } + result.Add(rewriter.TransformAssertCmd(command)); + } + + return result; + } + + ManualSplit GetSplitWithoutIsolatedAssertions() { + var origin = new ImplementationRootOrigin(partToDivide.Implementation); + if (!isolatedAssertions.Any()) { + return rewriter.CreateSplit(origin, partToDivide.Blocks); + } + + var newBlocks = ManualSplitFinder.UpdateBlocks(partToDivide.Blocks, + block => block.Cmds.Select(cmd => isolatedAssertions.Contains(cmd) ? rewriter.TransformAssertCmd(cmd) : cmd).ToList()); + return rewriter.CreateSplit(origin, newBlocks); + } + } + + private static bool ShouldIsolate(bool splitOnEveryAssert, QKeyValue? isolateAttribute) { + if (splitOnEveryAssert) { + if (isolateAttribute == null) { + return true; + } + + return !isolateAttribute.Params.OfType().Any(p => Equals(p, "none")); + } + + return isolateAttribute != null; + } +} + + +public class IsolatedAssertionOrigin : TokenWrapper, ImplementationPartOrigin { + public AssertCmd IsolatedAssert { get; } + + public IsolatedAssertionOrigin(AssertCmd isolatedAssert) : base(isolatedAssert.tok) { + this.IsolatedAssert = isolatedAssert; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnReturns.cs b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs similarity index 60% rename from Source/VCGeneration/Splits/IsolateAttributeOnReturns.cs rename to Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs index d2638979c..bd6e43b9d 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnReturns.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using Microsoft.Boogie; using VC; @@ -9,26 +10,16 @@ namespace VCGeneration; -class IsolateAttributeOnReturns { - private readonly Dictionary assumedAssertions = new(); - private readonly VCGenOptions options; +class IsolateAttributeOnJumpsHandler { + private readonly BlockRewriter rewriter; - public IsolateAttributeOnReturns(VCGenOptions options) { - this.options = options; - } - - private Cmd TransformAssertCmd(Cmd cmd) { - if (cmd is AssertCmd assertCmd) { - return assumedAssertions.GetOrCreate(assertCmd, () => VerificationConditionGenerator.AssertTurnedIntoAssume(options, assertCmd)); - } - - return cmd; + public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { + this.rewriter = rewriter; } - public List GetPartsFromIsolatedReturns( + public (List Isolated, ManualSplit Remainder) GetParts( Dictionary gotoToOriginalReturn, - ManualSplit partToDivide, - Func, ManualSplit> createSplit) { + ManualSplit partToDivide) { var results = new List(); var blocks = partToDivide.Blocks; @@ -45,34 +36,36 @@ public List GetPartsFromIsolatedReturns( continue; } - if (!gotoToOriginalReturn.TryGetValue(gotoCmd, out var returnCmd)) { - continue; - } - - var isolateAttribute = QKeyValue.FindAttribute(returnCmd.Attributes, p => p.Key == "isolate"); - var isolate = ShouldIsolate(splitOnEveryAssert, isolateAttribute); + var isTypeOfAssert = gotoToOriginalReturn.ContainsKey(gotoCmd); + var isolateAttribute = QKeyValue.FindAttribute(gotoCmd.Attributes, p => p.Key == "isolate"); + var isolate = ShouldIsolate(isTypeOfAssert && splitOnEveryAssert, isolateAttribute); if (!isolate) { continue; } - // TODO support isolate paths for returns isolatedBlocks.Add(block); var ancestors = dag.ComputeReachability(block, false); var descendants = dag.ComputeReachability(block, true); var blocksToInclude = ancestors.Union(descendants).ToHashSet(); - var newBlocks = FocusApplier.ComputeNewBlocks(options, blocksToInclude, - reversedBlocks, ancestors.ToHashSet()); - var partFromIsolatedReturn = createSplit(new ReturnOrigin(gotoToOriginalReturn[gotoCmd]), newBlocks); - results.Add(partFromIsolatedReturn); + + if (isolateAttribute != null && isolateAttribute.Params.OfType().Any(p => Equals(p, "paths"))) { + // These conditions hold if the goto was originally a return + Debug.Assert(gotoCmd.LabelTargets.Count == 1); + Debug.Assert(gotoCmd.LabelTargets[0].TransferCmd is not GotoCmd); + results.AddRange(rewriter.GetSplitsForIsolatedPaths(gotoCmd.LabelTargets[0], blocksToInclude, gotoToOriginalReturn[gotoCmd].tok)); + } else { + var newBlocks = rewriter.ComputeNewBlocks(blocksToInclude, + reversedBlocks, ancestors.ToHashSet()); + results.Add(createSplit(new ReturnOrigin(gotoToOriginalReturn[gotoCmd]), newBlocks)); + } + } - - results.Add(GetPartWithoutIsolatedReturns()); - return results; + return (results, GetPartWithoutIsolatedReturns()); ManualSplit GetPartWithoutIsolatedReturns() { var newBlocks = BlockRewriter.UpdateBlocks(new Stack(reversedBlocks), new HashSet(), - oldBlock => oldBlock.Cmds.Select(TransformAssertCmd).ToList()); + oldBlock => oldBlock.Cmds.Select(rewriter.TransformAssertCmd).ToList()); foreach (var (oldBlock, newBlock) in newBlocks) { if (isolatedBlocks.Contains(oldBlock)) { newBlock.TransferCmd = null; diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index cb22bbe81..be21fa0d0 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -5,226 +5,28 @@ using System.Linq; using Microsoft.Boogie; using VC; +using VCGeneration.Splits; namespace VCGeneration; -class SplitOrigin : TokenWrapper, ImplementationPartOrigin { - public SplitOrigin(IToken inner) : base(inner) - { - } -} -public static class ManualSplitFinder { - public static List UpdateBlocks(IReadOnlyList blocks, - Func> getCommands) - { - var newBlocks = new List(blocks.Count); - var oldToNewBlockMap = new Dictionary(newBlocks.Count); - foreach (var currentBlock in blocks) { - var newBlock = new Block(currentBlock.tok) - { - Label = currentBlock.Label - }; - - oldToNewBlockMap[currentBlock] = newBlock; - newBlocks.Add(newBlock); - newBlock.Cmds = getCommands(currentBlock); - } - - AddJumpsToNewBlocks(oldToNewBlockMap); - return newBlocks; - } - - public static IEnumerable SplitOnPathsAndAssertions(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createSplit) { - var focussedParts = FocusApplier.GetFocusParts(options, run, createSplit); - var isolatedParts = focussedParts.SelectMany(s => - new IsolateAttributeOnAsserts(options).GetPartsFromIsolatedAssertions(s, createSplit)).ToList(); - - if (!isolatedParts.Any()) { - return Enumerable.Empty(); - } - - var notIsolatedPart = isolatedParts.First(); - return GetVcsForSplits(notIsolatedPart).Concat(isolatedParts.Skip(1)); - } - - private static List GetVcsForSplits(ManualSplit partToSplit) { - var splitsPerBlock = new Dictionary>(); - var splits = new HashSet(); - foreach (var block in partToSplit.Blocks) { - var splitsForThisBlock = new List(); - foreach (var command in block.Cmds) { - if (!ShouldSplitHere(command)) { - continue; - } - - splits.Add(command); - splitsForThisBlock.Add(command); - } - - if (splitsForThisBlock.Any()) { - splitsPerBlock[block] = splitsForThisBlock; - } - } - - if (!splits.Any()) { - return new List { partToSplit }; - } - - var vcs = new List(); - var entryPoint = partToSplit.Blocks[0]; - var blockStartToSplit = GetMapFromBlockStartToSplit(partToSplit.Blocks, splitsPerBlock); - - var beforeSplitsVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, blockStartToSplit, - entryPoint, splits, null); - if (beforeSplitsVc != null) - { - vcs.Add(beforeSplitsVc); - } - foreach (var block in partToSplit.Blocks) { - var splitsForBlock = splitsPerBlock.GetValueOrDefault(block); - if (splitsForBlock == null) { - continue; - } - - foreach (var split in splitsForBlock) - { - var splitVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, - blockStartToSplit, block, splits, split); - if (splitVc != null) - { - vcs.Add(splitVc); - } - } - } - return vcs; - - ManualSplit CreateVc(ImplementationPartOrigin token, List blocks) { - return new ManualSplit(partToSplit.Options, () => { - BlockTransformations.Optimize(blocks); - return blocks; - }, partToSplit.GotoCmdOrigins, partToSplit.parent, partToSplit.Run, token); - } - } - - private static bool ShouldSplitHere(Cmd c) { - if (c is not PredicateCmd predicateCmd) { - return false; - } - return QKeyValue.FindBoolAttribute(predicateCmd.Attributes, "split_here"); - } - - private static Dictionary GetMapFromBlockStartToSplit(List blocks, Dictionary> splitsPerBlock) { - var todo = new Stack(); - var blockAssignments = new Dictionary(); - var immediateDominators = Program.GraphFromBlocks(blocks).ImmediateDominator(); - todo.Push(blocks[0]); - while (todo.Count > 0) { - var currentBlock = todo.Pop(); - if (blockAssignments.Keys.Contains(currentBlock)) { - continue; - } - - if (!immediateDominators.TryGetValue(currentBlock, out var immediateDominator)) - { - blockAssignments[currentBlock] = null; - } - else if (splitsPerBlock.TryGetValue(immediateDominator, out var splitsForDominator)) // if the currentBlock's dominator has a split then it will be associated with that split - { - blockAssignments[currentBlock] = splitsForDominator.Last(); - } - else { - Contract.Assert(blockAssignments.Keys.Contains(immediateDominator)); - blockAssignments[currentBlock] = blockAssignments[immediateDominator]; - } - - if (currentBlock.TransferCmd is GotoCmd gotoCmd) { - gotoCmd.LabelTargets.ForEach(block => todo.Push(block)); - } - } - return blockAssignments; - } +public static class ManualSplitFinder { - private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, - ManualSplit partToSplit, - Dictionary blockStartToSplit, Block blockWithSplit, - HashSet splits, Cmd? split) + public static IEnumerable GetParts(VCGenOptions options, ImplementationRun run, + Dictionary gotoToOriginalReturn, + Func, ManualSplit> createPart) { - var assertionCount = 0; - - var newBlocks = UpdateBlocks(partToSplit.Implementation.Blocks, currentBlock => { - if (currentBlock == blockWithSplit) { - return GetCommandsForBlockWithSplit(currentBlock); - } - - if (blockStartToSplit[currentBlock] == split) { - return GetCommandsForBlockImmediatelyDominatedBySplit(currentBlock); - } - - return currentBlock.Cmds.Select(x => CommandTransformations.AssertIntoAssume(partToSplit.Options, x)).ToList(); + var blockRewriter = new BlockRewriter(options, run.Implementation.Blocks, createPart); + var focussedParts = new FocusAttributeHandler(blockRewriter).GetParts(run); + return focussedParts.SelectMany(focussedPart => { + var (isolatedJumps, withoutIsolatedJumps) = + new IsolateAttributeOnJumpsHandler(blockRewriter).GetParts(gotoToOriginalReturn, focussedPart); + var (isolatedAssertions, withoutIsolatedAssertions) = + new IsolateAttributeOnAssertsHandler(blockRewriter).GetParts(withoutIsolatedJumps); + + var splitParts = new SplitAttributeHandler(blockRewriter).GetParts(withoutIsolatedAssertions); + return isolatedJumps.Concat(isolatedAssertions).Concat(splitParts); }); - - if (assertionCount == 0) { - return null; - } - - return createVc(new SplitOrigin(split?.tok ?? partToSplit.Origin), newBlocks); - - List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) - { - var verify = true; - var newCmds = new List(); - foreach (var command in currentBlock.Cmds) { - verify &= !splits.Contains(command); - if (verify && BlockTransformations.IsNonTrivialAssert(command)) - { - assertionCount++; - } - newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(partToSplit.Options, command)); - } - - return newCmds; - } - - List GetCommandsForBlockWithSplit(Block currentBlock) - { - var newCmds = new List(); - var verify = false; - foreach (var command in currentBlock.Cmds) { - if (splits.Contains(command)) { - verify = command == split; - } - - if (verify && BlockTransformations.IsNonTrivialAssert(command)) - { - assertionCount++; - } - newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(partToSplit.Options, command)); - } - - return newCmds; - } - } - - public static void AddJumpsToNewBlocks(Dictionary oldToNewBlockMap) - { - foreach (var (oldBlock, newBlock) in oldToNewBlockMap) { - if (oldBlock.TransferCmd is ReturnCmd returnCmd) { - ((ReturnCmd)newBlock.TransferCmd).tok = returnCmd.tok; - continue; - } - - var gotoCmd = (GotoCmd)oldBlock.TransferCmd; - var newLabelTargets = new List(gotoCmd.LabelTargets.Count); - var newLabelNames = new List(gotoCmd.LabelTargets.Count); - foreach (var target in gotoCmd.LabelTargets) { - newLabelTargets.Add(oldToNewBlockMap[target]); - newLabelNames.Add(oldToNewBlockMap[target].Label); - } - - oldToNewBlockMap[oldBlock].TransferCmd = new GotoCmd(gotoCmd.tok, newLabelNames, newLabelTargets); - } } } diff --git a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs index b029b561c..be82da496 100644 --- a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs @@ -66,7 +66,7 @@ public SplitAndVerifyWorker( ResetPredecessors(Implementation.Blocks); - ManualSplits = ManualSplitFinder.SplitOnPathsAndAssertions(options, run, + ManualSplits = ManualSplitFinder.GetParts(options, run, (token, blocks) => new ManualSplit(options, () => blocks, gotoCmdOrigins, verificationConditionGenerator, run, token)).ToList(); if (ManualSplits.Count == 1 && maxSplits > 1) diff --git a/Source/VCGeneration/Splits/SplitAttributeHandler.cs b/Source/VCGeneration/Splits/SplitAttributeHandler.cs new file mode 100644 index 000000000..9ed2f58b6 --- /dev/null +++ b/Source/VCGeneration/Splits/SplitAttributeHandler.cs @@ -0,0 +1,223 @@ +#nullable enable +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using Microsoft.Boogie; +using VC; +using VCGeneration.Splits; + +namespace VCGeneration; + +class SplitAttributeHandler { + private readonly BlockRewriter rewriter; + + public SplitAttributeHandler(BlockRewriter rewriter) { + this.rewriter = rewriter; + } + + public List GetParts(ManualSplit partToSplit) { + var splitsPerBlock = new Dictionary>(); + var splits = new HashSet(); + foreach (var block in partToSplit.Blocks) { + var splitsForThisBlock = new List(); + foreach (var command in block.Cmds) { + if (!ShouldSplitHere(command)) { + continue; + } + + splits.Add(command); + splitsForThisBlock.Add(command); + } + + if (splitsForThisBlock.Any()) { + splitsPerBlock[block] = splitsForThisBlock; + } + } + + if (!splits.Any()) { + return new List { partToSplit }; + } + + var vcs = new List(); + var entryPoint = partToSplit.Blocks[0]; + var blockStartToSplit = GetMapFromBlockStartToSplit(partToSplit.Blocks, splitsPerBlock); + + var beforeSplitsVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, blockStartToSplit, + entryPoint, splits, null); + if (beforeSplitsVc != null) + { + vcs.Add(beforeSplitsVc); + } + foreach (var block in partToSplit.Blocks) { + var splitsForBlock = splitsPerBlock.GetValueOrDefault(block); + if (splitsForBlock == null) { + continue; + } + + foreach (var split in splitsForBlock) + { + var splitVc = GetImplementationPartAfterSplit(CreateVc, partToSplit, + blockStartToSplit, block, splits, split); + if (splitVc != null) + { + vcs.Add(splitVc); + } + } + } + return vcs; + + ManualSplit CreateVc(ImplementationPartOrigin token, List blocks) { + return new ManualSplit(partToSplit.Options, () => { + BlockTransformations.Optimize(blocks); + return blocks; + }, partToSplit.GotoCmdOrigins, partToSplit.parent, partToSplit.Run, token); + } + } + + private static bool ShouldSplitHere(Cmd c) { + if (c is not PredicateCmd predicateCmd) { + return false; + } + + return QKeyValue.FindBoolAttribute(predicateCmd.Attributes, "split_here"); + } + + private static Dictionary GetMapFromBlockStartToSplit(List blocks, Dictionary> splitsPerBlock) { + var todo = new Stack(); + var blockAssignments = new Dictionary(); + var immediateDominators = Program.GraphFromBlocks(blocks).ImmediateDominator(); + todo.Push(blocks[0]); + while (todo.Count > 0) { + var currentBlock = todo.Pop(); + if (blockAssignments.Keys.Contains(currentBlock)) { + continue; + } + + if (!immediateDominators.TryGetValue(currentBlock, out var immediateDominator)) + { + blockAssignments[currentBlock] = null; + } + else if (splitsPerBlock.TryGetValue(immediateDominator, out var splitsForDominator)) // if the currentBlock's dominator has a split then it will be associated with that split + { + blockAssignments[currentBlock] = splitsForDominator.Last(); + } + else { + Contract.Assert(blockAssignments.Keys.Contains(immediateDominator)); + blockAssignments[currentBlock] = blockAssignments[immediateDominator]; + } + + if (currentBlock.TransferCmd is GotoCmd gotoCmd) { + gotoCmd.LabelTargets.ForEach(block => todo.Push(block)); + } + } + return blockAssignments; + } + + private static ManualSplit? GetImplementationPartAfterSplit(Func, ManualSplit> createVc, + ManualSplit partToSplit, + Dictionary blockStartToSplit, Block blockWithSplit, + HashSet splits, Cmd? split) + { + var assertionCount = 0; + + var newBlocks = UpdateBlocks(partToSplit.Implementation.Blocks, currentBlock => { + if (currentBlock == blockWithSplit) { + return GetCommandsForBlockWithSplit(currentBlock); + } + + if (blockStartToSplit[currentBlock] == split) { + return GetCommandsForBlockImmediatelyDominatedBySplit(currentBlock); + } + + return currentBlock.Cmds.Select(x => CommandTransformations.AssertIntoAssume(partToSplit.Options, x)).ToList(); + }); + + if (assertionCount == 0) { + return null; + } + + return createVc(new SplitOrigin(split?.tok ?? partToSplit.Origin), newBlocks); + + List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) + { + var verify = true; + var newCmds = new List(); + foreach (var command in currentBlock.Cmds) { + verify &= !splits.Contains(command); + if (verify && BlockTransformations.IsNonTrivialAssert(command)) + { + assertionCount++; + } + newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(partToSplit.Options, command)); + } + + return newCmds; + } + + List GetCommandsForBlockWithSplit(Block currentBlock) + { + var newCmds = new List(); + var verify = false; + foreach (var command in currentBlock.Cmds) { + if (splits.Contains(command)) { + verify = command == split; + } + + if (verify && BlockTransformations.IsNonTrivialAssert(command)) + { + assertionCount++; + } + newCmds.Add(verify ? command : CommandTransformations.AssertIntoAssume(partToSplit.Options, command)); + } + + return newCmds; + } + } + + private static List UpdateBlocks(IReadOnlyList blocks, + Func> getCommands) + { + var newBlocks = new List(blocks.Count); + var oldToNewBlockMap = new Dictionary(newBlocks.Count); + foreach (var currentBlock in blocks) { + var newBlock = new Block(currentBlock.tok) + { + Label = currentBlock.Label + }; + + oldToNewBlockMap[currentBlock] = newBlock; + newBlocks.Add(newBlock); + newBlock.Cmds = getCommands(currentBlock); + } + + AddJumpsToNewBlocks(oldToNewBlockMap); + return newBlocks; + } + + private static void AddJumpsToNewBlocks(Dictionary oldToNewBlockMap) + { + foreach (var (oldBlock, newBlock) in oldToNewBlockMap) { + if (oldBlock.TransferCmd is ReturnCmd returnCmd) { + ((ReturnCmd)newBlock.TransferCmd).tok = returnCmd.tok; + continue; + } + + var gotoCmd = (GotoCmd)oldBlock.TransferCmd; + var newLabelTargets = new List(gotoCmd.LabelTargets.Count); + var newLabelNames = new List(gotoCmd.LabelTargets.Count); + foreach (var target in gotoCmd.LabelTargets) { + newLabelTargets.Add(oldToNewBlockMap[target]); + newLabelNames.Add(oldToNewBlockMap[target].Label); + } + + oldToNewBlockMap[oldBlock].TransferCmd = new GotoCmd(gotoCmd.tok, newLabelNames, newLabelTargets); + } + } +} + +class SplitOrigin : TokenWrapper, ImplementationPartOrigin { + public SplitOrigin(IToken inner) : base(inner) + { + } +} \ No newline at end of file From 0d0e8fcb86a237d198db2c627f5c6b576da1aaa3 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 1 Oct 2024 13:29:58 +0200 Subject: [PATCH 29/70] Reorder tests --- Test/{pruning => implementationDivision/focus}/Focus.bpl | 0 Test/{pruning => implementationDivision/focus}/Focus.bpl.expect | 0 .../isolatePaths}/isolatePaths.bpl | 0 .../isolatePaths}/isolatePaths.bpl.EarlyAssertions--1.expect | 0 .../isolatePaths.bpl.EarlyAssertionsVariant-0.expect | 0 .../isolatePaths.bpl.EarlyAssertionsVariant-1.expect | 0 .../isolatePaths}/isolatePaths.bpl.EmptyPath--1.expect | 0 .../isolatePaths}/isolatePaths.bpl.NoDuplicateErrors-0.expect | 0 .../isolatePaths}/isolatePaths.bpl.NoDuplicateErrors-1.expect | 0 .../isolatePaths}/isolatePaths.bpl.expect | 0 .../multiFocus/multiFocus.bpl} | 0 .../multiFocus/multiFocus.bpl.expect} | 0 .../split/assumeFalseSplit}/AssumeFalseSplit.bpl | 0 .../split/assumeFalseSplit}/AssumeFalseSplit.bpl.expect | 0 .../split/assumeFalseSplit}/Foo.split.0.bpl.expect | 0 .../split/assumeFalseSplit}/Foo.split.1.bpl.expect | 0 .../split/assumeFalseSplit}/Foo.split.2.bpl.expect | 0 .../split/originalSplit}/Foo.split.0.bpl.expect | 0 .../split/originalSplit}/Foo.split.1.bpl.expect | 0 .../split/originalSplit}/Foo.split.2.bpl.expect | 0 .../split/originalSplit}/Foo.split.3.bpl.expect | 0 .../split/originalSplit}/Foo.split.4.bpl.expect | 0 .../split/originalSplit}/Split.bpl | 0 .../split/originalSplit}/Split.bpl.expect | 0 24 files changed, 0 insertions(+), 0 deletions(-) rename Test/{pruning => implementationDivision/focus}/Focus.bpl (100%) rename Test/{pruning => implementationDivision/focus}/Focus.bpl.expect (100%) rename Test/{pruning => implementationDivision/isolatePaths}/isolatePaths.bpl (100%) rename Test/{pruning => implementationDivision/isolatePaths}/isolatePaths.bpl.EarlyAssertions--1.expect (100%) rename Test/{pruning => implementationDivision/isolatePaths}/isolatePaths.bpl.EarlyAssertionsVariant-0.expect (100%) rename Test/{pruning => implementationDivision/isolatePaths}/isolatePaths.bpl.EarlyAssertionsVariant-1.expect (100%) rename Test/{pruning => implementationDivision/isolatePaths}/isolatePaths.bpl.EmptyPath--1.expect (100%) rename Test/{pruning => implementationDivision/isolatePaths}/isolatePaths.bpl.NoDuplicateErrors-0.expect (100%) rename Test/{pruning => implementationDivision/isolatePaths}/isolatePaths.bpl.NoDuplicateErrors-1.expect (100%) rename Test/{pruning => implementationDivision/isolatePaths}/isolatePaths.bpl.expect (100%) rename Test/{pruning/MultiFocus.bpl => implementationDivision/multiFocus/multiFocus.bpl} (100%) rename Test/{pruning/MultiFocus.bpl.expect => implementationDivision/multiFocus/multiFocus.bpl.expect} (100%) rename Test/{test0/AssumeFalseSplit => implementationDivision/split/assumeFalseSplit}/AssumeFalseSplit.bpl (100%) rename Test/{test0/AssumeFalseSplit => implementationDivision/split/assumeFalseSplit}/AssumeFalseSplit.bpl.expect (100%) rename Test/{test0/AssumeFalseSplit => implementationDivision/split/assumeFalseSplit}/Foo.split.0.bpl.expect (100%) rename Test/{test0/AssumeFalseSplit => implementationDivision/split/assumeFalseSplit}/Foo.split.1.bpl.expect (100%) rename Test/{test0/AssumeFalseSplit => implementationDivision/split/assumeFalseSplit}/Foo.split.2.bpl.expect (100%) rename Test/{test0/Split => implementationDivision/split/originalSplit}/Foo.split.0.bpl.expect (100%) rename Test/{test0/Split => implementationDivision/split/originalSplit}/Foo.split.1.bpl.expect (100%) rename Test/{test0/Split => implementationDivision/split/originalSplit}/Foo.split.2.bpl.expect (100%) rename Test/{test0/Split => implementationDivision/split/originalSplit}/Foo.split.3.bpl.expect (100%) rename Test/{test0/Split => implementationDivision/split/originalSplit}/Foo.split.4.bpl.expect (100%) rename Test/{test0/Split => implementationDivision/split/originalSplit}/Split.bpl (100%) rename Test/{test0/Split => implementationDivision/split/originalSplit}/Split.bpl.expect (100%) diff --git a/Test/pruning/Focus.bpl b/Test/implementationDivision/focus/Focus.bpl similarity index 100% rename from Test/pruning/Focus.bpl rename to Test/implementationDivision/focus/Focus.bpl diff --git a/Test/pruning/Focus.bpl.expect b/Test/implementationDivision/focus/Focus.bpl.expect similarity index 100% rename from Test/pruning/Focus.bpl.expect rename to Test/implementationDivision/focus/Focus.bpl.expect diff --git a/Test/pruning/isolatePaths.bpl b/Test/implementationDivision/isolatePaths/isolatePaths.bpl similarity index 100% rename from Test/pruning/isolatePaths.bpl rename to Test/implementationDivision/isolatePaths/isolatePaths.bpl diff --git a/Test/pruning/isolatePaths.bpl.EarlyAssertions--1.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertions--1.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.EarlyAssertions--1.expect rename to Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertions--1.expect diff --git a/Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-0.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-0.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-0.expect rename to Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-0.expect diff --git a/Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-1.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-1.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.EarlyAssertionsVariant-1.expect rename to Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-1.expect diff --git a/Test/pruning/isolatePaths.bpl.EmptyPath--1.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EmptyPath--1.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.EmptyPath--1.expect rename to Test/implementationDivision/isolatePaths/isolatePaths.bpl.EmptyPath--1.expect diff --git a/Test/pruning/isolatePaths.bpl.NoDuplicateErrors-0.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-0.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.NoDuplicateErrors-0.expect rename to Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-0.expect diff --git a/Test/pruning/isolatePaths.bpl.NoDuplicateErrors-1.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-1.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.NoDuplicateErrors-1.expect rename to Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-1.expect diff --git a/Test/pruning/isolatePaths.bpl.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.expect similarity index 100% rename from Test/pruning/isolatePaths.bpl.expect rename to Test/implementationDivision/isolatePaths/isolatePaths.bpl.expect diff --git a/Test/pruning/MultiFocus.bpl b/Test/implementationDivision/multiFocus/multiFocus.bpl similarity index 100% rename from Test/pruning/MultiFocus.bpl rename to Test/implementationDivision/multiFocus/multiFocus.bpl diff --git a/Test/pruning/MultiFocus.bpl.expect b/Test/implementationDivision/multiFocus/multiFocus.bpl.expect similarity index 100% rename from Test/pruning/MultiFocus.bpl.expect rename to Test/implementationDivision/multiFocus/multiFocus.bpl.expect diff --git a/Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl b/Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl similarity index 100% rename from Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl rename to Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl diff --git a/Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl.expect b/Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl.expect similarity index 100% rename from Test/test0/AssumeFalseSplit/AssumeFalseSplit.bpl.expect rename to Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl.expect diff --git a/Test/test0/AssumeFalseSplit/Foo.split.0.bpl.expect b/Test/implementationDivision/split/assumeFalseSplit/Foo.split.0.bpl.expect similarity index 100% rename from Test/test0/AssumeFalseSplit/Foo.split.0.bpl.expect rename to Test/implementationDivision/split/assumeFalseSplit/Foo.split.0.bpl.expect diff --git a/Test/test0/AssumeFalseSplit/Foo.split.1.bpl.expect b/Test/implementationDivision/split/assumeFalseSplit/Foo.split.1.bpl.expect similarity index 100% rename from Test/test0/AssumeFalseSplit/Foo.split.1.bpl.expect rename to Test/implementationDivision/split/assumeFalseSplit/Foo.split.1.bpl.expect diff --git a/Test/test0/AssumeFalseSplit/Foo.split.2.bpl.expect b/Test/implementationDivision/split/assumeFalseSplit/Foo.split.2.bpl.expect similarity index 100% rename from Test/test0/AssumeFalseSplit/Foo.split.2.bpl.expect rename to Test/implementationDivision/split/assumeFalseSplit/Foo.split.2.bpl.expect diff --git a/Test/test0/Split/Foo.split.0.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.0.bpl.expect similarity index 100% rename from Test/test0/Split/Foo.split.0.bpl.expect rename to Test/implementationDivision/split/originalSplit/Foo.split.0.bpl.expect diff --git a/Test/test0/Split/Foo.split.1.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.1.bpl.expect similarity index 100% rename from Test/test0/Split/Foo.split.1.bpl.expect rename to Test/implementationDivision/split/originalSplit/Foo.split.1.bpl.expect diff --git a/Test/test0/Split/Foo.split.2.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.2.bpl.expect similarity index 100% rename from Test/test0/Split/Foo.split.2.bpl.expect rename to Test/implementationDivision/split/originalSplit/Foo.split.2.bpl.expect diff --git a/Test/test0/Split/Foo.split.3.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.3.bpl.expect similarity index 100% rename from Test/test0/Split/Foo.split.3.bpl.expect rename to Test/implementationDivision/split/originalSplit/Foo.split.3.bpl.expect diff --git a/Test/test0/Split/Foo.split.4.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.4.bpl.expect similarity index 100% rename from Test/test0/Split/Foo.split.4.bpl.expect rename to Test/implementationDivision/split/originalSplit/Foo.split.4.bpl.expect diff --git a/Test/test0/Split/Split.bpl b/Test/implementationDivision/split/originalSplit/Split.bpl similarity index 100% rename from Test/test0/Split/Split.bpl rename to Test/implementationDivision/split/originalSplit/Split.bpl diff --git a/Test/test0/Split/Split.bpl.expect b/Test/implementationDivision/split/originalSplit/Split.bpl.expect similarity index 100% rename from Test/test0/Split/Split.bpl.expect rename to Test/implementationDivision/split/originalSplit/Split.bpl.expect From a73bba0c2585cb7af62f05b5a2f1a6928fc99f58 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 1 Oct 2024 15:05:23 +0200 Subject: [PATCH 30/70] Fixes. Isolatepaths for assertions seems to work --- Source/ExecutionEngine/ExecutionEngine.cs | 2 +- Source/VCGeneration/Splits/BlockRewriter.cs | 3 +++ .../Splits/IsolateAttributeOnAssertsHandler.cs | 9 +++++---- .../Splits/IsolateAttributeOnJumpsHandler.cs | 11 ++++++----- Source/VCGeneration/Splits/SplitAndVerifyWorker.cs | 2 +- Source/VCGeneration/Splits/SplitAttributeHandler.cs | 2 +- .../isolateAssertion.bpl} | 4 ++-- .../isolateAssertion.bpl.expect} | 2 +- 8 files changed, 20 insertions(+), 15 deletions(-) rename Test/implementationDivision/{isolatedAssertion.bpl => isolateAssertion/isolateAssertion.bpl} (85%) rename Test/implementationDivision/{isolatedAssertion.bpl.expect => isolateAssertion/isolateAssertion.bpl.expect} (50%) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 7657382c6..84f9f0383 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -744,7 +744,7 @@ public async Task> GetVerificationTasks(Program out var modelViewInfo); ConditionGeneration.ResetPredecessors(run.Implementation.Blocks); - var splits = ManualSplitFinder.GetParts(Options, run, + var splits = ManualSplitFinder.GetParts(Options, run, gotoCmdOrigins, (token, blocks) => new ManualSplit(Options, () => blocks, gotoCmdOrigins, vcGenerator, run, token)).ToList(); for (var index = 0; index < splits.Count; index++) { var split = splits[index]; diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index 8f57cff8b..ef89b4fa0 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -132,6 +132,9 @@ public static OrderedDictionary UpdateBlocks(Stack blocksTo blocksToVisit.Push(previous); } + } + + foreach (var (oldBlock, newBlock) in oldToNewBlockMap) { if (oldBlock.TransferCmd is GotoCmd gtc) { var targets = gtc.LabelTargets.Where(oldToNewBlockMap.ContainsKey).ToList(); diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs index 72090ef44..515a8feb4 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs @@ -1,5 +1,4 @@ #nullable enable -using System; using System.Collections.Generic; using System.Linq; using Microsoft.Boogie; @@ -22,7 +21,8 @@ public IsolateAttributeOnAssertsHandler(BlockRewriter rewriter) { var isolatedAssertions = new HashSet(); var results = new List(); - + + Implementation.ComputePredecessorsForBlocks(partToDivide.Blocks); foreach (var block in partToDivide.Blocks) { foreach (var assert in block.Cmds.OfType()) { var attributes = assert.Attributes; @@ -77,9 +77,10 @@ ManualSplit GetSplitWithoutIsolatedAssertions() { return rewriter.CreateSplit(origin, partToDivide.Blocks); } - var newBlocks = ManualSplitFinder.UpdateBlocks(partToDivide.Blocks, + var newBlocks = BlockRewriter.UpdateBlocks(new Stack(partToDivide.Blocks), + new HashSet(), block => block.Cmds.Select(cmd => isolatedAssertions.Contains(cmd) ? rewriter.TransformAssertCmd(cmd) : cmd).ToList()); - return rewriter.CreateSplit(origin, newBlocks); + return rewriter.CreateSplit(origin, newBlocks.Values.OrderBy(b => b.tok).ToList()); } } diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs index bd6e43b9d..279640734 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs @@ -24,7 +24,8 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { var results = new List(); var blocks = partToDivide.Blocks; var dag = Program.GraphFromBlocks(blocks); - var reversedBlocks = dag.TopologicalSort().Reversed(); + var topoSorted = dag.TopologicalSort(); + var reversedBlocks = topoSorted.Reversed(); var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; partToDivide.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); @@ -56,7 +57,7 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { } else { var newBlocks = rewriter.ComputeNewBlocks(blocksToInclude, reversedBlocks, ancestors.ToHashSet()); - results.Add(createSplit(new ReturnOrigin(gotoToOriginalReturn[gotoCmd]), newBlocks)); + results.Add(rewriter.CreateSplit(new ReturnOrigin(gotoToOriginalReturn[gotoCmd]), newBlocks)); } } @@ -64,14 +65,14 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { return (results, GetPartWithoutIsolatedReturns()); ManualSplit GetPartWithoutIsolatedReturns() { - var newBlocks = BlockRewriter.UpdateBlocks(new Stack(reversedBlocks), new HashSet(), - oldBlock => oldBlock.Cmds.Select(rewriter.TransformAssertCmd).ToList()); + var newBlocks = BlockRewriter.UpdateBlocks(new Stack(topoSorted), new HashSet(), + oldBlock => oldBlock.Cmds.ToList()); foreach (var (oldBlock, newBlock) in newBlocks) { if (isolatedBlocks.Contains(oldBlock)) { newBlock.TransferCmd = null; } } - return createSplit(new ImplementationRootOrigin(partToDivide.Implementation), newBlocks.Values.ToList()); + return rewriter.CreateSplit(new ImplementationRootOrigin(partToDivide.Implementation), newBlocks.Values.ToList()); } } diff --git a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs index be82da496..ca477fe2c 100644 --- a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs @@ -66,7 +66,7 @@ public SplitAndVerifyWorker( ResetPredecessors(Implementation.Blocks); - ManualSplits = ManualSplitFinder.GetParts(options, run, + ManualSplits = ManualSplitFinder.GetParts(options, run, gotoCmdOrigins, (token, blocks) => new ManualSplit(options, () => blocks, gotoCmdOrigins, verificationConditionGenerator, run, token)).ToList(); if (ManualSplits.Count == 1 && maxSplits > 1) diff --git a/Source/VCGeneration/Splits/SplitAttributeHandler.cs b/Source/VCGeneration/Splits/SplitAttributeHandler.cs index 9ed2f58b6..cae4dd3eb 100644 --- a/Source/VCGeneration/Splits/SplitAttributeHandler.cs +++ b/Source/VCGeneration/Splits/SplitAttributeHandler.cs @@ -121,7 +121,7 @@ private static bool ShouldSplitHere(Cmd c) { { var assertionCount = 0; - var newBlocks = UpdateBlocks(partToSplit.Implementation.Blocks, currentBlock => { + var newBlocks = UpdateBlocks(partToSplit.Blocks, currentBlock => { if (currentBlock == blockWithSplit) { return GetCommandsForBlockWithSplit(currentBlock); } diff --git a/Test/implementationDivision/isolatedAssertion.bpl b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl similarity index 85% rename from Test/implementationDivision/isolatedAssertion.bpl rename to Test/implementationDivision/isolateAssertion/isolateAssertion.bpl index a729980aa..28aa22494 100644 --- a/Test/implementationDivision/isolatedAssertion.bpl +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl @@ -1,7 +1,7 @@ // RUN: %parallel-boogie /printSplit:%t /errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" -procedure IsolatedAssertion(x: int, y: int) +procedure IsolateAssertion(x: int, y: int) { var z: int; z := 0; @@ -21,7 +21,7 @@ procedure IsolatedAssertion(x: int, y: int) assert z > 3; } -procedure IsolatedPathsAssertion(x: int, y: int) +procedure IsolatePathsAssertion(x: int, y: int) { var z: int; z := 0; diff --git a/Test/implementationDivision/isolatedAssertion.bpl.expect b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect similarity index 50% rename from Test/implementationDivision/isolatedAssertion.bpl.expect rename to Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect index 71c2b6338..d66944447 100644 --- a/Test/implementationDivision/isolatedAssertion.bpl.expect +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect @@ -1,3 +1,3 @@ Focus.bpl(15,5): Error: this assertion could not be proved -Boogie program verifier finished with 1 verified, 1 error +Boogie program verifier finished with 2 verified, 1 error From 9a3ee5176b236da42842401406339545aac64649 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Tue, 1 Oct 2024 15:09:07 +0200 Subject: [PATCH 31/70] Added isolateJump test --- .../isolateJump/isolateJump.bpl | 38 ++++++++ .../isolateJump/isolateJump.bpl.expect | 0 .../isolatePaths/isolatePaths.bpl | 90 ------------------- ...isolatePaths.bpl.EarlyAssertions--1.expect | 10 --- ...ePaths.bpl.EarlyAssertionsVariant-0.expect | 10 --- ...ePaths.bpl.EarlyAssertionsVariant-1.expect | 10 --- .../isolatePaths.bpl.EmptyPath--1.expect | 9 -- ...solatePaths.bpl.NoDuplicateErrors-0.expect | 10 --- ...solatePaths.bpl.NoDuplicateErrors-1.expect | 10 --- .../isolatePaths/isolatePaths.bpl.expect | 3 - 10 files changed, 38 insertions(+), 152 deletions(-) create mode 100644 Test/implementationDivision/isolateJump/isolateJump.bpl create mode 100644 Test/implementationDivision/isolateJump/isolateJump.bpl.expect delete mode 100644 Test/implementationDivision/isolatePaths/isolatePaths.bpl delete mode 100644 Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertions--1.expect delete mode 100644 Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-0.expect delete mode 100644 Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-1.expect delete mode 100644 Test/implementationDivision/isolatePaths/isolatePaths.bpl.EmptyPath--1.expect delete mode 100644 Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-0.expect delete mode 100644 Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-1.expect delete mode 100644 Test/implementationDivision/isolatePaths/isolatePaths.bpl.expect diff --git a/Test/implementationDivision/isolateJump/isolateJump.bpl b/Test/implementationDivision/isolateJump/isolateJump.bpl new file mode 100644 index 000000000..25a26d25d --- /dev/null +++ b/Test/implementationDivision/isolateJump/isolateJump.bpl @@ -0,0 +1,38 @@ +// RUN: %parallel-boogie /printSplit:%t /errorTrace:0 "%s" > "%t" +// RUN: %diff "%s.expect" "%t" + +procedure IsolateReturn(x: int) returns (r: int) + ensures r > 4 +{ + z := 0; + if (x > 0) { + z := z + 1; + } else { + z := z + 2 + } + + if (y > 0) { + z := z + 3; + return {:isolate}; + } + + z := z + 4; +} + +procedure IsolateReturnPath(x: int) returns (r: int) + ensures r > 4 +{ + z := 0; + if (x > 0) { + z := z + 1; + } else { + z := z + 2 + } + + if (y > 0) { + z := z + 3; + return {:isolate "path"}; + } + + z := z + 4; +} \ No newline at end of file diff --git a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect new file mode 100644 index 000000000..e69de29bb diff --git a/Test/implementationDivision/isolatePaths/isolatePaths.bpl b/Test/implementationDivision/isolatePaths/isolatePaths.bpl deleted file mode 100644 index 1a14bb2b6..000000000 --- a/Test/implementationDivision/isolatePaths/isolatePaths.bpl +++ /dev/null @@ -1,90 +0,0 @@ -// RUN: %parallel-boogie "%s" /printSplit:%t /trace /errorTrace:0 > "%t" -// RUN: %diff "%s.expect" "%t" -// RUN: %diff %S/isolatePaths.bpl.NoDuplicateErrors-0.expect %t-NoDuplicateErrors-0.spl -// RUN: %diff %S/isolatePaths.bpl.NoDuplicateErrors-1.expect %t-NoDuplicateErrors-1.spl -// RUN: %diff %S/isolatePaths.bpl.EarlyAssertions--1.expect %t-EarlyAssertions--1.spl -// RUN: %diff %S/isolatePaths.bpl.EarlyAssertionsVariant-0.expect %t-EarlyAssertionsVariant-0.spl -// RUN: %diff %S/isolatePaths.bpl.EarlyAssertionsVariant-1.expect %t-EarlyAssertionsVariant-1.spl -// RUN: %diff %S/isolatePaths.bpl.EmptyPath--1.expect %t-EmptyPath--1.spl - -// Two VCs, each with one assertion -procedure {:isolate_paths} NoDuplicateErrors() -{ - var x: int; - if (*) { - x := 3; - } - else { - x := 2; - } - assert x == 1; -} - -// We expect only a single VC -procedure {:isolate_paths} EmptyPath() -{ - var x: int; - x := 10; - if (*) { - assert x > 5; - } else { - } -} - -// We expect a single VC that contains both assertions -procedure {:isolate_paths} EarlyAssertions() -{ - var x: int; - x := 10; - assert x > 5; // Should only be asserted once - if (*) { - assert x > 6; - } else { - } -} - -// We expect two VCs, one with two assertions -procedure {:isolate_paths} EarlyAssertionsVariant() -{ - var x: int; - x := 10; - assert x > 5; // Should only be asserted once - if (*) { - assert x > 4; - } else { - assert x > 6; - } -} - -// Only three VCs, not four -procedure {:isolate_paths} AssumeFalse() { - var x: int; - x := 10; - if (*) { - assume false; - } - assert x > 4; // Only asserted once - - if (*) { - assume false; - } else { - } - assert x > 5; // Only asserted once -} - -procedure {:isolate_paths} EquivalentPrePaths() { - var x: int; - x := 10; - if (*) { - //check true; - } - assert x > 4; // Only asserted once - - if (*) { - // Previous assertion stays one in the first follow-up path - // check true; - } else { - // Previous assertion became an assume in this path - } - assert x > 5; // Only asserted once -} diff --git a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertions--1.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertions--1.expect deleted file mode 100644 index 3739eb6f3..000000000 --- a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertions--1.expect +++ /dev/null @@ -1,10 +0,0 @@ -implementation {:isolate_paths} EarlyAssertions() -{ - - PreconditionGeneratedEntry: - assert 10 > 5; - assert 10 > 6; - goto ; -} - - diff --git a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-0.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-0.expect deleted file mode 100644 index acb78aef4..000000000 --- a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-0.expect +++ /dev/null @@ -1,10 +0,0 @@ -implementation {:isolate_paths} EarlyAssertionsVariant() -{ - - PreconditionGeneratedEntry: - assume 10 > 5; - assert 10 > 6; - goto ; -} - - diff --git a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-1.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-1.expect deleted file mode 100644 index e493e0687..000000000 --- a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EarlyAssertionsVariant-1.expect +++ /dev/null @@ -1,10 +0,0 @@ -implementation {:isolate_paths} EarlyAssertionsVariant() -{ - - PreconditionGeneratedEntry: - assert 10 > 5; - assert 10 > 4; - goto ; -} - - diff --git a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EmptyPath--1.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EmptyPath--1.expect deleted file mode 100644 index 40fdbde10..000000000 --- a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.EmptyPath--1.expect +++ /dev/null @@ -1,9 +0,0 @@ -implementation {:isolate_paths} EmptyPath() -{ - - anon0: - assert 10 > 5; - goto ; -} - - diff --git a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-0.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-0.expect deleted file mode 100644 index 2bb28d5b0..000000000 --- a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-0.expect +++ /dev/null @@ -1,10 +0,0 @@ -implementation {:isolate_paths} NoDuplicateErrors() -{ - - anon0: - assume x#AT#0 == 2; - assert x#AT#0 == 1; - return; -} - - diff --git a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-1.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-1.expect deleted file mode 100644 index 56342ebf8..000000000 --- a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.NoDuplicateErrors-1.expect +++ /dev/null @@ -1,10 +0,0 @@ -implementation {:isolate_paths} NoDuplicateErrors() -{ - - anon0: - assume x#AT#0 == 3; - assert x#AT#0 == 1; - return; -} - - diff --git a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.expect b/Test/implementationDivision/isolatePaths/isolatePaths.bpl.expect deleted file mode 100644 index 4ecca4d93..000000000 --- a/Test/implementationDivision/isolatePaths/isolatePaths.bpl.expect +++ /dev/null @@ -1,3 +0,0 @@ -isolatePaths.bpl(19,3): Error: this assertion could not be proved - -Boogie program verifier finished with 3 verified, 2 errors From 9ebb0a2fe42aad3745ccbc70e3b57499f0813205 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 13:12:51 +0200 Subject: [PATCH 32/70] Update tests and fixes --- Source/Core/AST/Implementation.cs | 8 +- Source/Core/AST/Program.cs | 31 +++++ Source/Core/BoogiePL.atg | 15 ++- Source/Core/Parser.cs | 15 ++- Source/VCGeneration/BlockTransformations.cs | 2 +- Source/VCGeneration/ConditionGeneration.cs | 2 - Source/VCGeneration/Splits/BlockRewriter.cs | 27 ++-- .../Splits/FocusAttributeHandler.cs | 4 +- .../IsolateAttributeOnAssertsHandler.cs | 5 + .../Splits/IsolateAttributeOnJumpsHandler.cs | 17 ++- .../VCGeneration/Splits/ManualSplitFinder.cs | 3 +- Source/VCGeneration/Splits/PathToken.cs | 28 +--- Source/VCGeneration/Splits/Split.cs | 22 ++- .../Splits/SplitAttributeHandler.cs | 2 + .../Transformations/DesugarReturns.cs | 105 +++++++-------- .../VerificationConditionGenerator.cs | 2 +- .../{multiFocus => focus}/multiFocus.bpl | 0 .../multiFocus.bpl.expect | 0 .../isolateAssertion/isolateAssertion.bpl | 2 +- .../isolateJump/isolateJump.bpl | 32 ++--- .../isolateJump/isolateJump.bpl.expect | 126 ++++++++++++++++++ 21 files changed, 314 insertions(+), 134 deletions(-) rename Test/implementationDivision/{multiFocus => focus}/multiFocus.bpl (100%) rename Test/implementationDivision/{multiFocus => focus}/multiFocus.bpl.expect (100%) diff --git a/Source/Core/AST/Implementation.cs b/Source/Core/AST/Implementation.cs index 6d6d2415c..a48a18b5a 100644 --- a/Source/Core/AST/Implementation.cs +++ b/Source/Core/AST/Implementation.cs @@ -462,19 +462,19 @@ void BlocksWriters(TokenTextWriter stream) { } public void EmitImplementation(TokenTextWriter stream, int level, IEnumerable blocks, - bool showLocals) { + bool showLocals, string namePrefix = "") { EmitImplementation(stream, level, writer => { foreach (var block in blocks) { block.Emit(writer, level + 1); } - }, showLocals); + }, showLocals, namePrefix); } - public void EmitImplementation(TokenTextWriter stream, int level, Action printBlocks, bool showLocals) + public void EmitImplementation(TokenTextWriter stream, int level, Action printBlocks, bool showLocals, string namePrefix = "") { stream.Write(this, level, "implementation "); EmitAttributes(stream); - stream.Write(this, level, "{0}", TokenTextWriter.SanitizeIdentifier(this.Name)); + stream.Write(this, level, "{0}", TokenTextWriter.SanitizeIdentifier(Name) + namePrefix); EmitSignature(stream, false); stream.WriteLine(); diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index 3ed1e94ac..2ec24d7fb 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -498,6 +498,37 @@ public static Graph BuildCallGraph(CoreOptions options, Program return callGraph; } + public static Graph GraphFromBlocksSet(Block source, IReadOnlySet blocks, bool forward = true) + { + var result = new Graph(); + if (!blocks.Any()) + { + return result; + } + void AddEdge(Block a, Block b) { + if (!blocks.Contains(a) || !blocks.Contains(b)) { + return; + } + Contract.Assert(a != null && b != null); + if (forward) { + result.AddEdge(a, b); + } else { + result.AddEdge(b, a); + } + } + + result.AddSource(source); + foreach (var block in blocks) + { + if (block.TransferCmd is GotoCmd gtc) + { + Contract.Assume(gtc.LabelTargets != null); + gtc.LabelTargets.ForEach(dest => AddEdge(block, dest)); + } + } + return result; + } + public static Graph GraphFromBlocks(IReadOnlyList blocks, bool forward = true) { var result = new Graph(); diff --git a/Source/Core/BoogiePL.atg b/Source/Core/BoogiePL.atg index b850527c2..eecb93c6b 100644 --- a/Source/Core/BoogiePL.atg +++ b/Source/Core/BoogiePL.atg @@ -1032,6 +1032,7 @@ TransferCmd = (. Contract.Ensures(Contract.ValueAtReturn(out tc) != null); tc = dummyTransferCmd; Token y; List/*!*/ xs; List ss = new List(); + QKeyValue kv = null; .) ( "goto" (. y = t; .) Idents (. foreach(IToken/*!*/ s in xs){ @@ -1039,7 +1040,9 @@ TransferCmd ss.Add(s.val); } tc = new GotoCmd(y, ss); .) - | "return" (. tc = new ReturnCmd(t); .) + | "return" + { Attribute } + (. tc = new ReturnCmd(t) { Attributes = kv }; .) ) ";" . @@ -1688,6 +1691,7 @@ SpecBlock List/*!*/ xs; List ss = new List(); b = dummyBlock; + QKeyValue kv = null; Expr/*!*/ e; .) Ident ":" @@ -1706,8 +1710,13 @@ SpecBlock ss.Add(s.val); } b = new Block(x,x.val,cs,new GotoCmd(y,ss)); .) - | "return" Expression - (. b = new Block(x,x.val,cs,new ReturnExprCmd(t,e)); .) + | "return" + { Attribute } + Expression + (. b = new Block(x,x.val,cs,new ReturnExprCmd(t,e) { + Attributes = kv + }); + .) ) ";" . diff --git a/Source/Core/Parser.cs b/Source/Core/Parser.cs index ff6ecb84f..cc67d930f 100644 --- a/Source/Core/Parser.cs +++ b/Source/Core/Parser.cs @@ -1536,6 +1536,7 @@ void TransferCmd(out TransferCmd/*!*/ tc) { Contract.Ensures(Contract.ValueAtReturn(out tc) != null); tc = dummyTransferCmd; Token y; List/*!*/ xs; List ss = new List(); + QKeyValue kv = null; if (la.kind == 55) { Get(); @@ -1548,7 +1549,10 @@ void TransferCmd(out TransferCmd/*!*/ tc) { } else if (la.kind == 56) { Get(); - tc = new ReturnCmd(t); + while (la.kind == 26) { + Attribute(ref kv); + } + tc = new ReturnCmd(t) { Attributes = kv }; } else SynErr(148); Expect(10); } @@ -2558,6 +2562,7 @@ void SpecBlock(out Block/*!*/ b) { List/*!*/ xs; List ss = new List(); b = dummyBlock; + QKeyValue kv = null; Expr/*!*/ e; Ident(out x); @@ -2583,8 +2588,14 @@ void SpecBlock(out Block/*!*/ b) { } else if (la.kind == 56) { Get(); + while (la.kind == 26) { + Attribute(ref kv); + } Expression(out e); - b = new Block(x,x.val,cs,new ReturnExprCmd(t,e)); + b = new Block(x,x.val,cs,new ReturnExprCmd(t,e) { + Attributes = kv + }); + } else SynErr(174); Expect(10); } diff --git a/Source/VCGeneration/BlockTransformations.cs b/Source/VCGeneration/BlockTransformations.cs index fe6507899..95e0d7788 100644 --- a/Source/VCGeneration/BlockTransformations.cs +++ b/Source/VCGeneration/BlockTransformations.cs @@ -97,7 +97,7 @@ private static bool ContainsAssert(Block b) return b.Cmds.Exists(IsNonTrivialAssert); } - public static bool IsNonTrivialAssert (Cmd c) { return c is AssertCmd { Expr: LiteralExpr { asBool: true } }; } + public static bool IsNonTrivialAssert (Cmd c) { return c is AssertCmd { Expr: not LiteralExpr { asBool: true } }; } private static void DeleteUselessBlocks(List blocks) { var toVisit = new HashSet(); diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 45ff736f7..ae78190d8 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -450,8 +450,6 @@ public static void EmitImpl(VCGenOptions options, ImplementationRun run, bool pr options.PrintDesugarings = oldPrintDesugaringSetting; } - - public static void ResetPredecessors(List blocks) { Contract.Requires(blocks != null); diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index ef89b4fa0..0a7af5f44 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -28,16 +29,19 @@ public Cmd TransformAssertCmd(Cmd cmd) { return cmd; } - public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, ISet? blocksToInclude, IToken origin) { + public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IReadOnlySet? blocksToInclude, IToken origin) { var blockToVisit = new Stack>(); - blockToVisit.Push(ImmutableStack.Create(new Block(lastBlock.tok) + var newToOldBlocks = new Dictionary(); + var newLastBlock = new Block(lastBlock.tok) { Predecessors = lastBlock.Predecessors, Label = lastBlock.Label, - TransferCmd = null, + TransferCmd = new ReturnCmd(Token.NoToken), Cmds = lastBlock.Cmds - })); - + }; + blockToVisit.Push(ImmutableStack.Create(newLastBlock)); + newToOldBlocks[newLastBlock] = lastBlock; + while(blockToVisit.Any()) { var path = blockToVisit.Pop(); var firstBlock = path.Peek(); @@ -56,6 +60,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, ISet< TransferCmd = oldPrevious.TransferCmd, Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList() }; + newToOldBlocks[newPrevious] = oldPrevious; if (newPrevious.TransferCmd is GotoCmd gotoCmd) { newPrevious.TransferCmd = new GotoCmd(gotoCmd.tok, new List { firstBlock.Label }, new List @@ -66,7 +71,11 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, ISet< blockToVisit.Push(path.Push(newPrevious)); } if (!hadPredecessors) { - yield return CreateSplit(new PathOrigin(origin, path, dag.DominatorMap), new List { new(firstBlock.tok) { + + var filteredDag = blocksToInclude == null ? dag : Program.GraphFromBlocksSet(newToOldBlocks[path.Peek()], blocksToInclude); + var nonDominatedBranches = path.Where(b => + !filteredDag.DominatorMap.DominatedBy(lastBlock, newToOldBlocks[b])).ToList(); + yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), new List { new(firstBlock.tok) { Label = firstBlock.Label, Cmds = path.SelectMany(b => b.Cmds).ToList() } }); @@ -74,7 +83,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, ISet< } } - public List ComputeNewBlocks(ISet blocksToInclude, IEnumerable blocksReversed, + public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks(ISet blocksToInclude, IEnumerable blocksReversed, ISet freeAssumeBlocks) { var duplicator = new Duplicator(); @@ -95,7 +104,7 @@ public List ComputeNewBlocks(ISet blocksToInclude, IEnumerable CommandTransformations.AssertIntoAssume(Options, c)).Select(DisableSplits).ToList(); + newBlock.Cmds = block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList(); } if (block.TransferCmd is GotoCmd gtc) { @@ -109,7 +118,7 @@ public List ComputeNewBlocks(ISet blocksToInclude, IEnumerable UpdateBlocks(Stack blocksToVisit, diff --git a/Source/VCGeneration/Splits/FocusAttributeHandler.cs b/Source/VCGeneration/Splits/FocusAttributeHandler.cs index 7385bfb73..5449ef404 100644 --- a/Source/VCGeneration/Splits/FocusAttributeHandler.cs +++ b/Source/VCGeneration/Splits/FocusAttributeHandler.cs @@ -57,9 +57,9 @@ public List GetParts(ImplementationRun run) void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet blocksToInclude, ISet freeAssumeBlocks) { var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; if (allFocusBlocksHaveBeenProcessed) { - var newBlocks = rewriter.ComputeNewBlocks(blocksToInclude, blocksReversed, freeAssumeBlocks); + var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, blocksReversed, freeAssumeBlocks); ImplementationPartOrigin token = path.Any() - ? new PathOrigin(run.Implementation.tok, path, dominators) + ? new PathOrigin(run.Implementation.tok, path.ToList()) // TODO fix : new ImplementationRootOrigin(run.Implementation); result.Add(rewriter.CreateSplit(token, newBlocks)); } else { diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs index 515a8feb4..9cccd0772 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs @@ -22,6 +22,9 @@ public IsolateAttributeOnAssertsHandler(BlockRewriter rewriter) { var isolatedAssertions = new HashSet(); var results = new List(); + foreach (var b in partToDivide.Blocks) { + b.Predecessors.Clear(); + } Implementation.ComputePredecessorsForBlocks(partToDivide.Blocks); foreach (var block in partToDivide.Blocks) { foreach (var assert in block.Cmds.OfType()) { @@ -104,4 +107,6 @@ public class IsolatedAssertionOrigin : TokenWrapper, ImplementationPartOrigin { public IsolatedAssertionOrigin(AssertCmd isolatedAssert) : base(isolatedAssert.tok) { this.IsolatedAssert = isolatedAssert; } + + public string ShortName => $"/assert@{IsolatedAssert.Line}"; } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs index 279640734..bf13a5f81 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs @@ -55,24 +55,25 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { Debug.Assert(gotoCmd.LabelTargets[0].TransferCmd is not GotoCmd); results.AddRange(rewriter.GetSplitsForIsolatedPaths(gotoCmd.LabelTargets[0], blocksToInclude, gotoToOriginalReturn[gotoCmd].tok)); } else { - var newBlocks = rewriter.ComputeNewBlocks(blocksToInclude, + var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, reversedBlocks, ancestors.ToHashSet()); results.Add(rewriter.CreateSplit(new ReturnOrigin(gotoToOriginalReturn[gotoCmd]), newBlocks)); } - } return (results, GetPartWithoutIsolatedReturns()); ManualSplit GetPartWithoutIsolatedReturns() { - var newBlocks = BlockRewriter.UpdateBlocks(new Stack(topoSorted), new HashSet(), - oldBlock => oldBlock.Cmds.ToList()); - foreach (var (oldBlock, newBlock) in newBlocks) { + // TODO this needs an extra test. In case the isolated jump is followed by something it dominates + var (newBlocks, mapping) = rewriter.ComputeNewBlocks(blocks.ToHashSet(), reversedBlocks, new HashSet()); + foreach (var (oldBlock, newBlock) in mapping) { if (isolatedBlocks.Contains(oldBlock)) { - newBlock.TransferCmd = null; + newBlock.TransferCmd = new ReturnCmd(Token.NoToken); } } - return rewriter.CreateSplit(new ImplementationRootOrigin(partToDivide.Implementation), newBlocks.Values.ToList()); + BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); + return rewriter.CreateSplit(new ImplementationRootOrigin(partToDivide.Implementation), + newBlocks); } } @@ -96,4 +97,6 @@ public class ReturnOrigin : TokenWrapper, ImplementationPartOrigin { public ReturnOrigin(ReturnCmd isolatedReturn) : base(isolatedReturn.tok) { this.IsolatedReturn = isolatedReturn; } + + public string ShortName => $"/return@{IsolatedReturn.Line}"; } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index be21fa0d0..2843b660d 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -22,7 +22,7 @@ public static IEnumerable GetParts(VCGenOptions options, Implementa var (isolatedJumps, withoutIsolatedJumps) = new IsolateAttributeOnJumpsHandler(blockRewriter).GetParts(gotoToOriginalReturn, focussedPart); var (isolatedAssertions, withoutIsolatedAssertions) = - new IsolateAttributeOnAssertsHandler(blockRewriter).GetParts(withoutIsolatedJumps); + new IsolateAttributeOnAssertsHandler(new BlockRewriter(options, withoutIsolatedJumps.Blocks, createPart)).GetParts(withoutIsolatedJumps); var splitParts = new SplitAttributeHandler(blockRewriter).GetParts(withoutIsolatedAssertions); return isolatedJumps.Concat(isolatedAssertions).Concat(splitParts); @@ -31,4 +31,5 @@ public static IEnumerable GetParts(VCGenOptions options, Implementa } public interface ImplementationPartOrigin : IToken { + string ShortName { get; } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/PathToken.cs b/Source/VCGeneration/Splits/PathToken.cs index 8be192a2b..825176b8b 100644 --- a/Source/VCGeneration/Splits/PathToken.cs +++ b/Source/VCGeneration/Splits/PathToken.cs @@ -1,4 +1,5 @@ #nullable enable +using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Linq; @@ -7,37 +8,20 @@ namespace VCGeneration; - -public class AssertPathOrigin : TokenWrapper, ImplementationPartOrigin { - - public AssertPathOrigin(AssertCmd assert, Block assertBlock, - ImmutableStack branches, DomRelation dominators) : base(assert.tok) - { - Assert = assert; - AssertBlock = assertBlock; - Branches = branches; - Dominators = dominators; - } - - public AssertCmd Assert { get; } - public Block AssertBlock { get; } - public ImmutableStack Branches { get; } - public DomRelation Dominators { get; } -} - public class PathOrigin : TokenWrapper, ImplementationPartOrigin { - public PathOrigin(IToken inner, ImmutableStack branches, DomRelation dominators) : base(inner) { + public PathOrigin(IToken inner, List branches) : base(inner) { Branches = branches; - Dominators = dominators; } - public ImmutableStack Branches { get; } - public DomRelation Dominators { get; } + public List Branches { get; } + public string ShortName => $"/assert@{line}[{string.Join(",", Branches.Select(b => b.tok.line))}]"; } class ImplementationRootOrigin : TokenWrapper, ImplementationPartOrigin { public ImplementationRootOrigin(Implementation implementation) : base(implementation.tok) { } + + public string ShortName => ""; } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/Split.cs b/Source/VCGeneration/Splits/Split.cs index 8eb2da555..ecc88fcc7 100644 --- a/Source/VCGeneration/Splits/Split.cs +++ b/Source/VCGeneration/Splits/Split.cs @@ -10,6 +10,7 @@ using Microsoft.Boogie.VCExprAST; using Microsoft.Boogie.SMTLib; using System.Threading.Tasks; +using VCGeneration; namespace VC { @@ -92,17 +93,26 @@ public Split(VCGenOptions options, Func> /*!*/ getBlocks, RandomSeed = randomSeed ?? Implementation.RandomSeed ?? Options.RandomSeed ?? 0; randomGen = new Random(RandomSeed); } - - public void PrintSplit() { + + private void PrintSplit() { if (Options.PrintSplitFile == null) { return; } + + var printToConsole = Options.PrintSplitFile == "-"; + if (printToConsole) { + Thread.Sleep(100); + } - using var writer = new TokenTextWriter( - $"{Options.PrintSplitFile}-{Util.EscapeFilename(Implementation.Name)}-{SplitIndex}.spl", false, - Options.PrettyPrint, Options); + var prefix = (this is ManualSplit manualSplit) ? manualSplit.Origin.ShortName : ""; + var name = Implementation.Name + prefix; + using var writer = printToConsole + ? new TokenTextWriter("", Options.OutputWriter, false, Options.PrettyPrint, Options) + : new TokenTextWriter( + $"{Options.PrintSplitFile}-{Util.EscapeFilename(name)}.spl", false, + Options.PrettyPrint, Options); - Implementation.EmitImplementation(writer, 0, Blocks, false); + Implementation.EmitImplementation(writer, 0, Blocks, false, prefix); PrintSplitDeclarations(writer); } diff --git a/Source/VCGeneration/Splits/SplitAttributeHandler.cs b/Source/VCGeneration/Splits/SplitAttributeHandler.cs index cae4dd3eb..34a1fa37e 100644 --- a/Source/VCGeneration/Splits/SplitAttributeHandler.cs +++ b/Source/VCGeneration/Splits/SplitAttributeHandler.cs @@ -220,4 +220,6 @@ class SplitOrigin : TokenWrapper, ImplementationPartOrigin { public SplitOrigin(IToken inner) : base(inner) { } + + public string ShortName => $"/split@{line}"; } \ No newline at end of file diff --git a/Source/VCGeneration/Transformations/DesugarReturns.cs b/Source/VCGeneration/Transformations/DesugarReturns.cs index 7335b1b3b..e47d62dd7 100644 --- a/Source/VCGeneration/Transformations/DesugarReturns.cs +++ b/Source/VCGeneration/Transformations/DesugarReturns.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.Contracts; +using System.Linq; using Microsoft.Boogie; using VC; @@ -8,71 +9,61 @@ namespace VCGeneration.Transformations; public static class DesugarReturns { public static Block GenerateUnifiedExit(Implementation impl, out Dictionary gotoCmdOrigins) - { - Contract.Requires(impl != null); - Contract.Requires(gotoCmdOrigins != null); - Contract.Ensures(Contract.Result() != null); + { + Contract.Requires(impl != null); + Contract.Requires(gotoCmdOrigins != null); + Contract.Ensures(Contract.Result() != null); - gotoCmdOrigins = new(); - Contract.Ensures(Contract.Result().TransferCmd is ReturnCmd); - Block exitBlock = null; + gotoCmdOrigins = new(); + Contract.Ensures(Contract.Result().TransferCmd is ReturnCmd); + Block exitBlock = null; - #region Create a unified exit block, if there's more than one - - { - int returnBlocks = 0; - foreach (Block b in impl.Blocks) - { - if (b.TransferCmd is ReturnCmd) - { - exitBlock = b; - returnBlocks++; - } - } - - if (returnBlocks > 1) - { - string unifiedExitLabel = "GeneratedUnifiedExit"; - 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 returnCmd) - { - List labels = new List(); - labels.Add(unifiedExitLabel); - List bs = new List(); - bs.Add(unifiedExit); - GotoCmd go = new GotoCmd(returnCmd.tok, labels, bs); - gotoCmdOrigins[go] = returnCmd; - b.TransferCmd = go; - unifiedExit.Predecessors.Add(b); - } - } + int returnBlocks = 0; + foreach (var block in impl.Blocks.Where(block => block.TransferCmd is ReturnCmd)) + { + exitBlock = block; + returnBlocks++; + } - exitBlock = unifiedExit; - impl.Blocks.Add(unifiedExit); + if (returnBlocks > 1) + { + string unifiedExitLabel = "GeneratedUnifiedExit"; + var unifiedExit = new Block(Token.NoToken, unifiedExitLabel, new List(), + new ReturnCmd(impl.StructuredStmts != null ? impl.StructuredStmts.EndCurly : Token.NoToken)); + Contract.Assert(unifiedExit != null); + foreach (var block in impl.Blocks) { + if (block.TransferCmd is not ReturnCmd returnCmd) { + continue; } - Contract.Assert(exitBlock != null); + var gotoLabels = new List { unifiedExitLabel }; + var gotoTargets = new List { unifiedExit }; + var gotoCmd = new GotoCmd(returnCmd.tok, gotoLabels, gotoTargets) { + Attributes = returnCmd.Attributes + }; + gotoCmdOrigins[gotoCmd] = returnCmd; + block.TransferCmd = gotoCmd; + unifiedExit.Predecessors.Add(block); } - return exitBlock; - #endregion + exitBlock = unifiedExit; + impl.Blocks.Add(unifiedExit); } - - /// - /// Modifies an implementation by inserting all postconditions - /// as assert statements at the end of the implementation - /// Returns the possibly-new unified exit block of the implementation - /// - /// - /// The unified exit block that has - /// already been constructed for the implementation (and so - /// is already an element of impl.Blocks) - /// - public static void InjectPostConditions(VCGenOptions options, ImplementationRun run, Block unifiedExitBlock, + + Contract.Assert(exitBlock != null); + return exitBlock; + } + + /// + /// Modifies an implementation by inserting all postconditions + /// as assert statements at the end of the implementation + /// Returns the possibly-new unified exit block of the implementation + /// + /// The unified exit block that has + /// already been constructed for the implementation (and so + /// is already an element of impl.Blocks) + /// + public static void InjectPostConditions(VCGenOptions options, ImplementationRun run, Block unifiedExitBlock, Dictionary gotoCmdOrigins) { var impl = run.Implementation; diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index 67237d7e7..5824a4c7f 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -77,7 +77,7 @@ public static AssumeCmd AssertTurnedIntoAssume(VCGenOptions options, AssertCmd a // Copy any {:id ...} from the assertion to the assumption, so // we can track it while analyzing verification coverage. But // skip it if it's `true` because that's never useful to track. - (assume as ICarriesAttributes).CopyIdFrom(assrt.tok, assrt); + assume.CopyIdFrom(assrt.tok, assrt); } return assume; diff --git a/Test/implementationDivision/multiFocus/multiFocus.bpl b/Test/implementationDivision/focus/multiFocus.bpl similarity index 100% rename from Test/implementationDivision/multiFocus/multiFocus.bpl rename to Test/implementationDivision/focus/multiFocus.bpl diff --git a/Test/implementationDivision/multiFocus/multiFocus.bpl.expect b/Test/implementationDivision/focus/multiFocus.bpl.expect similarity index 100% rename from Test/implementationDivision/multiFocus/multiFocus.bpl.expect rename to Test/implementationDivision/focus/multiFocus.bpl.expect diff --git a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl index 28aa22494..e65864732 100644 --- a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl @@ -1,4 +1,4 @@ -// RUN: %parallel-boogie /printSplit:%t /errorTrace:0 "%s" > "%t" +// RUN: %boogie /printSplit:- /errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" procedure IsolateAssertion(x: int, y: int) diff --git a/Test/implementationDivision/isolateJump/isolateJump.bpl b/Test/implementationDivision/isolateJump/isolateJump.bpl index 25a26d25d..d40ee651c 100644 --- a/Test/implementationDivision/isolateJump/isolateJump.bpl +++ b/Test/implementationDivision/isolateJump/isolateJump.bpl @@ -1,38 +1,38 @@ -// RUN: %parallel-boogie /printSplit:%t /errorTrace:0 "%s" > "%t" +// RUN: %boogie /printSplit:- /errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" -procedure IsolateReturn(x: int) returns (r: int) - ensures r > 4 +procedure IsolateReturn(x: int, y: int) returns (r: int) + ensures r > 4; { - z := 0; + r := 0; if (x > 0) { - z := z + 1; + r := r + 1; } else { - z := z + 2 + r := r + 2; } if (y > 0) { - z := z + 3; + r := r + 3; return {:isolate}; } - z := z + 4; + r := r + 4; } -procedure IsolateReturnPath(x: int) returns (r: int) - ensures r > 4 +procedure IsolateReturnPaths(x: int, y: int) returns (r: int) + ensures r > 4; { - z := 0; + r := 0; if (x > 0) { - z := z + 1; + r := r + 1; } else { - z := z + 2 + r := r + 2; } if (y > 0) { - z := z + 3; - return {:isolate "path"}; + r := r + 3; + return {:isolate "paths"}; } - z := z + 4; + r := r + 4; } \ No newline at end of file diff --git a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect index e69de29bb..fc715a4d0 100644 --- a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect +++ b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect @@ -0,0 +1,126 @@ +implementation IsolateReturn/return@16(x: int, y: int) returns (r: int) +{ + + anon0: + goto anon6_Then, anon6_Else; + + anon6_Then: + assume {:partition} x > 0; + assume r#AT#0 == 0 + 1; + assume r#AT#2 == r#AT#0; + goto anon7_Then; + + anon6_Else: + assume {:partition} 0 >= x; + assume r#AT#1 == 0 + 2; + assume r#AT#2 == r#AT#1; + goto anon7_Then; + + anon7_Then: + assume {:partition} y > 0; + assume r#AT#3 == r#AT#2 + 3; + assume r#AT#5 == r#AT#3; + goto GeneratedUnifiedExit; + + GeneratedUnifiedExit: + assert r#AT#5 > 4; + return; +} + + +implementation IsolateReturn(x: int, y: int) returns (r: int) +{ + + anon0: + goto anon6_Then, anon6_Else; + + anon6_Then: + assume {:partition} x > 0; + assume r#AT#0 == 0 + 1; + assume r#AT#2 == r#AT#0; + goto anon7_Else; + + anon6_Else: + assume {:partition} 0 >= x; + assume r#AT#1 == 0 + 2; + assume r#AT#2 == r#AT#1; + goto anon7_Else; + + anon7_Else: + assume {:partition} 0 >= y; + assume r#AT#4 == r#AT#2 + 4; + assume r#AT#5 == r#AT#4; + goto GeneratedUnifiedExit; + + GeneratedUnifiedExit: + assert r#AT#5 > 4; + return; +} + + +isolateJump.bpl(20,1): Error: a postcondition could not be proved on this return path +isolateJump.bpl(5,3): Related location: this is the postcondition that could not be proved +implementation IsolateReturnPaths/assert@34[27](x: int, y: int) returns (r: int) +{ + + anon0: + assume {:partition} x > 0; + assume r#AT#0 == 0 + 1; + assume r#AT#2 == r#AT#0; + assume {:partition} y > 0; + assume r#AT#3 == r#AT#2 + 3; + assume r#AT#5 == r#AT#3; + assert r#AT#5 > 4; + return; +} + + +implementation IsolateReturnPaths/assert@34[29](x: int, y: int) returns (r: int) +{ + + anon0: + assume {:partition} 0 >= x; + assume r#AT#1 == 0 + 2; + assume r#AT#2 == r#AT#1; + assume {:partition} y > 0; + assume r#AT#3 == r#AT#2 + 3; + assume r#AT#5 == r#AT#3; + assert r#AT#5 > 4; + return; +} + + +implementation IsolateReturnPaths(x: int, y: int) returns (r: int) +{ + + anon0: + goto anon6_Then, anon6_Else; + + anon6_Then: + assume {:partition} x > 0; + assume r#AT#0 == 0 + 1; + assume r#AT#2 == r#AT#0; + goto anon7_Else; + + anon6_Else: + assume {:partition} 0 >= x; + assume r#AT#1 == 0 + 2; + assume r#AT#2 == r#AT#1; + goto anon7_Else; + + anon7_Else: + assume {:partition} 0 >= y; + assume r#AT#4 == r#AT#2 + 4; + assume r#AT#5 == r#AT#4; + goto GeneratedUnifiedExit; + + GeneratedUnifiedExit: + assert r#AT#5 > 4; + return; +} + + +isolateJump.bpl(25,5): Error: a postcondition could not be proved on this return path +isolateJump.bpl(23,3): Related location: this is the postcondition that could not be proved + +Boogie program verifier finished with 0 verified, 2 errors From db798623d1e6f9df0e3c5e551d4cf9e7c5e34a8d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 13:21:33 +0200 Subject: [PATCH 33/70] Code review --- Source/Core/AST/AbsyQuant.cs | 11 ----------- Source/Core/AST/Program.cs | 8 ++++---- Source/Core/CoreOptions.cs | 10 ---------- Source/ExecutionEngine/CommandLineOptions.cs | 5 ----- Source/ExecutionEngine/ExecutionEngine.cs | 1 + Source/VCGeneration/Splits/BlockRewriter.cs | 2 +- .../isolateAssertion/isolateAssertion.bpl | 4 ++-- 7 files changed, 8 insertions(+), 33 deletions(-) diff --git a/Source/Core/AST/AbsyQuant.cs b/Source/Core/AST/AbsyQuant.cs index 5d112862c..aa827671f 100644 --- a/Source/Core/AST/AbsyQuant.cs +++ b/Source/Core/AST/AbsyQuant.cs @@ -553,17 +553,6 @@ public static bool FindBoolAttribute(QKeyValue kv, string name) return kv != null; } - public static bool? FindNullableBoolAttribute(QKeyValue kv, string name) - { - Contract.Requires(name != null); - kv = FindAttribute(kv, qkv => qkv.Key == name); - if (kv == null) { - return null; - } - return kv.Params.Count == 0 || (kv.Params.Count == 1 && kv.Params[0] is LiteralExpr && - ((LiteralExpr)kv.Params[0]).IsTrue); - } - public static int FindIntAttribute(QKeyValue kv, string name, int defl) { Contract.Requires(name != null); diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index 2ec24d7fb..d764284e8 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -498,15 +498,15 @@ public static Graph BuildCallGraph(CoreOptions options, Program return callGraph; } - public static Graph GraphFromBlocksSet(Block source, IReadOnlySet blocks, bool forward = true) + public static Graph GraphFromBlocksSubset(Block source, IReadOnlySet subset, bool forward = true) { var result = new Graph(); - if (!blocks.Any()) + if (!subset.Any()) { return result; } void AddEdge(Block a, Block b) { - if (!blocks.Contains(a) || !blocks.Contains(b)) { + if (!subset.Contains(a) || !subset.Contains(b)) { return; } Contract.Assert(a != null && b != null); @@ -518,7 +518,7 @@ void AddEdge(Block a, Block b) { } result.AddSource(source); - foreach (var block in blocks) + foreach (var block in subset) { if (block.TransferCmd is GotoCmd gtc) { diff --git a/Source/Core/CoreOptions.cs b/Source/Core/CoreOptions.cs index 549965cc8..b7ab2e965 100644 --- a/Source/Core/CoreOptions.cs +++ b/Source/Core/CoreOptions.cs @@ -3,16 +3,6 @@ namespace Microsoft.Boogie { - public static class CoreOptionsExtensions { - public static string GetFileNameForConsole(this CoreOptions options, string filename) - { - return options.UseBaseNameForFileName && !string.IsNullOrEmpty(filename) && - filename != "" - ? Path.GetFileName(filename) - : filename; - } - } - /// /// Boogie command-line options (other tools can subclass this class in order to support a /// superset of Boogie's options). diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 8c6b1ff3a..171797990 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -1915,11 +1915,6 @@ verify each input program separately /removeEmptyBlocks: 0 - do not remove empty blocks during VC generation 1 - remove empty blocks (default) - /isolatePaths: - 0 = do not isolate paths - 1 = generate a separate group of VCs for each control flow path through an implementation. - Each group has only a single VC by default, but usage of options and attribute that split VCs based on assertions - can introduce more. /coalesceBlocks: 0 = do not coalesce blocks 1 = coalesce blocks (default) diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 84f9f0383..e75bca69d 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -1310,6 +1310,7 @@ public void ProcessErrors(OutputPrinter printer, return; } + // When an assertion fails on multiple paths, only show an error for one of them. errors = errors.DistinctBy(e => e.FailingAssert).ToList(); errors.Sort(new CounterexampleComparer()); foreach (Counterexample error in errors) diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index 0a7af5f44..fdf552704 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -72,7 +72,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead } if (!hadPredecessors) { - var filteredDag = blocksToInclude == null ? dag : Program.GraphFromBlocksSet(newToOldBlocks[path.Peek()], blocksToInclude); + var filteredDag = blocksToInclude == null ? dag : Program.GraphFromBlocksSubset(newToOldBlocks[path.Peek()], blocksToInclude); var nonDominatedBranches = path.Where(b => !filteredDag.DominatorMap.DominatedBy(lastBlock, newToOldBlocks[b])).ToList(); yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), new List { new(firstBlock.tok) { diff --git a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl index e65864732..1582a6b2e 100644 --- a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl @@ -37,6 +37,6 @@ procedure IsolatePathsAssertion(x: int, y: int) z := z + 4; } assert z > 1; - assert {:isolate "paths"} z > 2; - assert z > 3; + assert {:isolate "paths"} z > 5; // fails on three out of four paths + assert z > 6; } \ No newline at end of file From 206acde4b11bbebfbb0ca28d57368200015696b5 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 13:27:46 +0200 Subject: [PATCH 34/70] Add refactoring --- Source/Graph/Graph.cs | 200 +++++++++++++++++++------------------- Source/Houdini/Houdini.cs | 53 +++++----- 2 files changed, 130 insertions(+), 123 deletions(-) diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 67024c10d..6fb588faa 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -43,7 +43,7 @@ internal static class Util sb.Append(", "); } - Contract.Assert(!object.Equals(de.Key, default(Node))); + Contract.Assert(!Equals(de.Key, default(Node))); sb.Append(de.Key.ToString()); sb.Append("~>"); sb.Append(ListToString(de.Value)); @@ -55,49 +55,49 @@ internal static class Util } } - public class DomRelation + public class DomRelation { // doms maps (unique) node numbers to the node numbers of the immediate dominator // to use it on Nodes, one needs the two way mapping between nodes and their numbers. private int[] nodeNumberToImmediateDominator; // 0 is unused: means undefined // here are the two mappings - private TNode[] postOrderNumberToNode; - private Dictionary nodeToPostOrderNumber; + private Node[] postOrderNumberToNode; + private Dictionary nodeToPostOrderNumber; private int sourceNum; // (number for) root of the graph - private TNode source; // root of the graph - private Graph graph; - private Dictionary> immediateDominatorMap; + private Node source; // root of the graph + private Graph graph; + private Dictionary> immediateDominatorMap; [NotDelayed] - internal DomRelation(Graph g, TNode source) + internal DomRelation(Graph g, Node source) { // TODO should we enable saying that the graph is a DAG, to enable an O(N) dominance algorithm? // Or is the algorithm already O(N) for DAG graphs? - this.graph = g; + graph = g; // slot 0 not used: nodes are numbered from 1 to n so zero // can represent undefined. this.source = source; //:base(); - this.NewComputeDominators(); + NewComputeDominators(); } - public Dictionary> ImmediateDominatorMap + public Dictionary> ImmediateDominatorMap { get { - Contract.Assume(this.immediateDominatorMap != null); - return this.immediateDominatorMap; + Contract.Assume(immediateDominatorMap != null); + return immediateDominatorMap; } } - public bool DominatedBy(TNode dominee, TNode dominator, List path = null) + public bool DominatedBy(Node dominee, Node dominator, List path = null) { - Contract.Assume(this.nodeToPostOrderNumber != null); - Contract.Assume(this.nodeNumberToImmediateDominator != null); - int domineeNum = this.nodeToPostOrderNumber[dominee]; - int dominatorNum = this.nodeToPostOrderNumber[dominator]; + Contract.Assume(nodeToPostOrderNumber != null); + Contract.Assume(nodeNumberToImmediateDominator != null); + int domineeNum = nodeToPostOrderNumber[dominee]; + int dominatorNum = nodeToPostOrderNumber[dominator]; if (domineeNum == dominatorNum) { return true; @@ -122,45 +122,45 @@ public bool DominatedBy(TNode dominee, TNode dominator, List path = null) } } - private Dictionary> domMap = null; + private Dictionary> domMap = null; [Pure] public override string ToString() { - Contract.Assume(this.nodeNumberToImmediateDominator != null); - int[] localDoms = this.nodeNumberToImmediateDominator; - Contract.Assume(this.postOrderNumberToNode != null); + Contract.Assume(nodeNumberToImmediateDominator != null); + int[] localDoms = nodeNumberToImmediateDominator; + Contract.Assume(postOrderNumberToNode != null); if (domMap == null) { - domMap = new Dictionary>(); + domMap = new Dictionary>(); for (int i = 1; i < localDoms.Length; i++) { // 0 slot is not used int domineeNum = i; int currentNodeNum = domineeNum; - List dominators = new List(); - while (currentNodeNum != this.sourceNum) + List dominators = new List(); + while (currentNodeNum != sourceNum) { - dominators.Add(this.postOrderNumberToNode[currentNodeNum]); - currentNodeNum = this.nodeNumberToImmediateDominator[currentNodeNum]; + dominators.Add(postOrderNumberToNode[currentNodeNum]); + currentNodeNum = nodeNumberToImmediateDominator[currentNodeNum]; } - dominators.Add(this.postOrderNumberToNode[this.sourceNum]); - domMap.Add(this.postOrderNumberToNode[i], dominators); + dominators.Add(postOrderNumberToNode[sourceNum]); + domMap.Add(postOrderNumberToNode[i], dominators); } } StringBuilder sb = new StringBuilder(); sb.Append("{"); bool first = true; - foreach (KeyValuePair> de in domMap) + foreach (KeyValuePair> de in domMap) { if (!first) { sb.Append(", "); } - Contract.Assert(!object.Equals(de.Key, default(TNode))); + Contract.Assert(!Equals(de.Key, default(Node))); sb.Append(de.Key.ToString()); sb.Append("~>"); sb.Append(ListToString(de.Value)); @@ -232,19 +232,19 @@ public void PrintList(IEnumerable xs) // http://citeseer.ist.psu.edu/cooper01simple.html private void NewComputeDominators() { - int n = this.graph.Nodes.Count; - this.postOrderNumberToNode = new TNode[n + 1]; - this.nodeToPostOrderNumber = new Dictionary(); + int n = graph.Nodes.Count; + postOrderNumberToNode = new Node[n + 1]; + nodeToPostOrderNumber = new Dictionary(); //HashSet visited = new HashSet(); //int currentNumber = 1; - Contract.Assume(this.source != null); + Contract.Assume(source != null); //this.PostOrderVisit(this.source, visited, ref currentNumber); - this.PostOrderVisitIterative(this.source); - this.sourceNum = this.nodeToPostOrderNumber[source]; + PostOrderVisitIterative(source); + this.sourceNum = nodeToPostOrderNumber[source]; // for (int i = 1; i <= n; i++){ Console.WriteLine(postOrderNumberToNode[i]); } - this.nodeNumberToImmediateDominator = new int[n + 1]; // 0 is unused: means undefined - TNode start_node = this.source; - this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[start_node]] = this.nodeToPostOrderNumber[start_node]; + nodeNumberToImmediateDominator = new int[n + 1]; // 0 is unused: means undefined + Node start_node = source; + nodeNumberToImmediateDominator[nodeToPostOrderNumber[start_node]] = nodeToPostOrderNumber[start_node]; bool changed = true; // PrintIntArray(doms); while (changed) @@ -253,8 +253,8 @@ private void NewComputeDominators() // for all nodes, b, in reverse postorder (except start_node) for (int nodeNum = n - 1; 1 <= nodeNum; nodeNum--) { - TNode b = this.postOrderNumberToNode[nodeNum]; - IEnumerable predecessors = this.graph.Predecessors(b); + Node b = postOrderNumberToNode[nodeNum]; + IEnumerable predecessors = graph.Predecessors(b); // find a predecessor (i.e., a higher number) for which // the doms array has been set int new_idom = 0; @@ -262,11 +262,11 @@ private void NewComputeDominators() #region new_idom <- number of first (processed) predecessor of b (pick one) - foreach (TNode p in predecessors) + foreach (Node p in predecessors) { - if (this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[p]] != 0) + if (nodeNumberToImmediateDominator[nodeToPostOrderNumber[p]] != 0) { - int x = this.nodeToPostOrderNumber[p]; + int x = nodeToPostOrderNumber[p]; new_idom = x; first_processed_predecessor = x; break; @@ -277,24 +277,24 @@ private void NewComputeDominators() #region for all other predecessors, p, of b - foreach (TNode p in predecessors) + foreach (Node p in predecessors) { - if (this.nodeToPostOrderNumber[p] == first_processed_predecessor) + if (nodeToPostOrderNumber[p] == first_processed_predecessor) { continue; } - if (this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[p]] != 0) + if (nodeNumberToImmediateDominator[nodeToPostOrderNumber[p]] != 0) { - new_idom = Intersect(this.nodeToPostOrderNumber[p], new_idom, this.nodeNumberToImmediateDominator); + new_idom = Intersect(nodeToPostOrderNumber[p], new_idom, nodeNumberToImmediateDominator); } } #endregion - if (this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[b]] != new_idom) + if (nodeNumberToImmediateDominator[nodeToPostOrderNumber[b]] != new_idom) { - this.nodeNumberToImmediateDominator[this.nodeToPostOrderNumber[b]] = new_idom; + nodeNumberToImmediateDominator[nodeToPostOrderNumber[b]] = new_idom; changed = true; } } @@ -302,13 +302,13 @@ private void NewComputeDominators() #region Populate the Immediate Dominator Map - int sourceNum = this.nodeToPostOrderNumber[this.source]; - immediateDominatorMap = new Dictionary>(); + int sourceNum = nodeToPostOrderNumber[source]; + immediateDominatorMap = new Dictionary>(); for (int i = 1; i <= n; i++) { - TNode node = this.postOrderNumberToNode[i]; - TNode idomNode = this.postOrderNumberToNode[this.nodeNumberToImmediateDominator[i]]; - if (i == sourceNum && this.nodeNumberToImmediateDominator[i] == sourceNum) + Node node = postOrderNumberToNode[i]; + Node idomNode = postOrderNumberToNode[nodeNumberToImmediateDominator[i]]; + if (i == sourceNum && nodeNumberToImmediateDominator[i] == sourceNum) { continue; } @@ -319,7 +319,7 @@ private void NewComputeDominators() } else { - List l = new List(); + List l = new List(); l.Add(node); immediateDominatorMap.Add(idomNode, l); } @@ -348,7 +348,7 @@ private int Intersect(int b1, int b2, int[] doms) return finger1; } - private void PostOrderVisit(TNode /*!*/ n, HashSet visited, ref int currentNumber) + private void PostOrderVisit(Node /*!*/ n, HashSet visited, ref int currentNumber) { Contract.Requires(n != null); if (visited.Contains(n)) @@ -357,27 +357,27 @@ private void PostOrderVisit(TNode /*!*/ n, HashSet visited, ref int curre } visited.Add(n); - foreach (TNode /*!*/ child in this.graph.Successors(n)) + foreach (Node /*!*/ child in graph.Successors(n)) { Contract.Assert(child != null); PostOrderVisit(child, visited, ref currentNumber); } - Contract.Assume(this.postOrderNumberToNode != null); - Contract.Assume(this.nodeToPostOrderNumber != null); - this.postOrderNumberToNode[currentNumber] = n; - this.nodeToPostOrderNumber[n] = currentNumber; + Contract.Assume(postOrderNumberToNode != null); + Contract.Assume(nodeToPostOrderNumber != null); + postOrderNumberToNode[currentNumber] = n; + nodeToPostOrderNumber[n] = currentNumber; currentNumber++; return; } // Iterative version: mimics the above recursive procedure - private void PostOrderVisitIterative(TNode n) + private void PostOrderVisitIterative(Node n) { Contract.Requires(n != null); - var visited = new HashSet(); - var grey = new HashSet(); - var stack = new Stack(); + var visited = new HashSet(); + var grey = new HashSet(); + var stack = new Stack(); int currentNumber = 1; @@ -390,17 +390,17 @@ private void PostOrderVisitIterative(TNode n) if (grey.Contains(curr)) { - Contract.Assume(this.postOrderNumberToNode != null); - Contract.Assume(this.nodeToPostOrderNumber != null); - this.postOrderNumberToNode[currentNumber] = curr; - this.nodeToPostOrderNumber[curr] = currentNumber; + Contract.Assume(postOrderNumberToNode != null); + Contract.Assume(nodeToPostOrderNumber != null); + postOrderNumberToNode[currentNumber] = curr; + nodeToPostOrderNumber[curr] = currentNumber; currentNumber++; } else { grey.Add(curr); stack.Push(curr); - foreach (TNode /*!*/ child in this.graph.Successors(curr)) + foreach (Node /*!*/ child in graph.Successors(curr)) { Contract.Assert(child != null); if (!visited.Contains(child)) @@ -413,10 +413,10 @@ private void PostOrderVisitIterative(TNode n) } } - public TNode LeastCommonAncestor(TNode n1, TNode n2) + public Node LeastCommonAncestor(Node n1, Node n2) { int num1 = nodeToPostOrderNumber[n1], num2 = nodeToPostOrderNumber[n2]; - int lca = Intersect(num1, num2, this.nodeNumberToImmediateDominator); + int lca = Intersect(num1, num2, nodeNumberToImmediateDominator); return postOrderNumberToNode[lca]; } } @@ -618,12 +618,12 @@ public List SuccessorsAsList(Node n) get { Contract.Assert(source != null); - if (this.dominatorMap == null) + if (dominatorMap == null) { - this.dominatorMap = new DomRelation(this, this.source); + dominatorMap = new DomRelation(this, source); } - return this.dominatorMap; + return dominatorMap; } } @@ -695,19 +695,19 @@ public Dictionary> ImmediateDominatorMap get { Contract.Assert(source != null); - if (this.dominatorMap == null) + if (dominatorMap == null) { - this.dominatorMap = new DomRelation(this, this.source); + dominatorMap = new DomRelation(this, source); } - return this.dominatorMap.ImmediateDominatorMap; + return dominatorMap.ImmediateDominatorMap; } } public List ImmediatelyDominatedBy(Node /*!*/ n) { Contract.Requires(n != null); - this.ImmediateDominatorMap.TryGetValue(n, out var dominees); + ImmediateDominatorMap.TryGetValue(n, out var dominees); return dominees ?? new List(); } @@ -720,7 +720,7 @@ public List TopologicalSort(bool reversed = false) // From Tarjan 1972 public void TarjanTopSort(out bool acyclic, out List sortedNodes, bool reversed = false) { - int n = this.Nodes.Count; + int n = Nodes.Count; if (n == 0) { acyclic = true; @@ -734,14 +734,14 @@ public void TarjanTopSort(out bool acyclic, out List sortedNodes, bool rev Dictionary nodeToNumber = new Dictionary(n); Node[] numberToNode = new Node[n]; int counter = 0; - foreach (Node node in this.Nodes) + foreach (Node node in Nodes) { numberToNode[counter] = node; nodeToNumber[node] = counter; counter++; } - foreach (Tuple e in this.Edges) + foreach (Tuple e in Edges) { Contract.Assert(e.Item1 != null); Contract.Assert(e.Item2 != null); @@ -791,7 +791,7 @@ public void TarjanTopSort(out bool acyclic, out List sortedNodes, bool rev Node root = numberToNode[rootIndex]; sorted.Add(root); ++sortedIndex; - foreach (Node s in this.Successors(root)) + foreach (Node s in Successors(root)) { incomingEdges[nodeToNumber[s]]--; } @@ -804,7 +804,7 @@ public void TarjanTopSort(out bool acyclic, out List sortedNodes, bool rev private IEnumerable OldTopologicalSort() { - Tuple> result = this.TopSort(); + Tuple> result = TopSort(); return result.Item1 ? result.Item2 : (IEnumerable) new List(); } @@ -812,7 +812,7 @@ private IEnumerable OldTopologicalSort() private Tuple> TopSort() { List S = new List(); - HashSet V = this.Nodes; + HashSet V = Nodes; HashSet X = new HashSet(); foreach (Node /*!*/ n in V) { @@ -835,7 +835,7 @@ private Tuple> TopSort() foreach (Node /*!*/ u in X) { Contract.Assert(u != null); - if (this.Edge(u, n)) + if (Edge(u, n)) { inDegreeZero = false; break; // no point looking further @@ -922,7 +922,7 @@ internal ReducibleResult(bool b, HashSet headers, Dictionary Key.Item1 != null && Key.Item2 != null)); - this.reducible = b; + reducible = b; this.headers = headers; this.backEdgeNodes = backEdgeNodes; this.naturalLoops = naturalLoops; @@ -1105,12 +1105,12 @@ public HashSet SplitCandidates public void ComputeLoops() { - ReducibleResult r = ComputeReducible(this, this.source); - this.reducible = r.reducible; - this.headers = r.headers; - this.backEdgeNodes = r.backEdgeNodes; - this.naturalLoops = r.naturalLoops; - this.splitCandidates = r.splitCandidates; + ReducibleResult r = ComputeReducible(this, source); + reducible = r.reducible; + headers = r.headers; + backEdgeNodes = r.backEdgeNodes; + naturalLoops = r.naturalLoops; + splitCandidates = r.splitCandidates; return; } @@ -1620,7 +1620,7 @@ public void Compute() Contract.Requires(!Computed); Contract.Ensures(Computed); // Compute post times on graph with edges reversed - this.dfsNext = this.preds; + dfsNext = preds; foreach (Node /*!*/ n in cce.NonNull(graph.Keys)) { Contract.Assert(n != null); @@ -1634,7 +1634,7 @@ public void Compute() seen.Clear(); // Compute SCCs - this.dfsNext = this.succs; + dfsNext = succs; while (postOrder.Count > 0) { Node /*!*/ @@ -1653,7 +1653,7 @@ public void Compute() // Clear seen seen.Clear(); - this.computed = true; + computed = true; } private Adjacency /*?*/ @@ -1728,7 +1728,7 @@ public override string ToString() foreach (ICollection component in this) { string /*!*/ - tmp = System.String.Format("\nComponent #{0} = ", i++); + tmp = String.Format("\nComponent #{0} = ", i++); Contract.Assert(tmp != null); outStr += tmp; @@ -1737,7 +1737,7 @@ public override string ToString() foreach (Node b in component) { string /*!*/ - tmpComponent = System.String.Format("{0}{1}", firstInRow ? "" : ", ", b); + tmpComponent = String.Format("{0}{1}", firstInRow ? "" : ", ", b); Contract.Assert(tmpComponent != null); outStr += tmpComponent; firstInRow = false; diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index 14f68ce7a..aeafd7bbb 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -1451,35 +1451,42 @@ protected void DebugRefutedCandidates(Implementation curFunc, List Date: Wed, 2 Oct 2024 13:32:19 +0200 Subject: [PATCH 35/70] Refactoring --- Source/Graph/Graph.cs | 9 ++++----- Source/Houdini/Houdini.cs | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 6fb588faa..0efd5ef58 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -65,8 +65,8 @@ public class DomRelation private Node[] postOrderNumberToNode; private Dictionary nodeToPostOrderNumber; private int sourceNum; // (number for) root of the graph - private Node source; // root of the graph - private Graph graph; + private readonly Node source; // root of the graph + private readonly Graph graph; private Dictionary> immediateDominatorMap; [NotDelayed] @@ -79,7 +79,6 @@ internal DomRelation(Graph g, Node source) // slot 0 not used: nodes are numbered from 1 to n so zero // can represent undefined. this.source = source; - //:base(); NewComputeDominators(); } @@ -122,7 +121,7 @@ public bool DominatedBy(Node dominee, Node dominator, List path = null) } } - private Dictionary> domMap = null; + private Dictionary> domMap; [Pure] public override string ToString() @@ -328,7 +327,7 @@ private void NewComputeDominators() #endregion } - private int Intersect(int b1, int b2, int[] doms) + private static int Intersect(int b1, int b2, int[] doms) { int finger1 = b1; int finger2 = b2; diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index aeafd7bbb..7a74a1f5d 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -1455,8 +1455,8 @@ private RefutedAnnotation ExtractRefutedAnnotation(Counterexample error) { case CallCounterexample callCounterexample: { - Procedure failingProcedure = callCounterexample.FailingCall.Proc; - Requires failingRequires = callCounterexample.FailingRequires; + var failingProcedure = callCounterexample.FailingCall.Proc; + var failingRequires = callCounterexample.FailingRequires; if (MatchCandidate(failingRequires.Condition, out houdiniConstant)) { Contract.Assert(houdiniConstant != null); @@ -1468,7 +1468,7 @@ private RefutedAnnotation ExtractRefutedAnnotation(Counterexample error) } case ReturnCounterexample returnCounterexample: { - Ensures failingEnsures = returnCounterexample.FailingEnsures; + var failingEnsures = returnCounterexample.FailingEnsures; if (MatchCandidate(failingEnsures.Condition, out houdiniConstant)) { Contract.Assert(houdiniConstant != null); @@ -1479,7 +1479,7 @@ private RefutedAnnotation ExtractRefutedAnnotation(Counterexample error) } case AssertCounterexample assertCounterexample: { - AssertCmd failingAssert = assertCounterexample.FailingAssert; + var failingAssert = assertCounterexample.FailingAssert; if (MatchCandidate(failingAssert.OrigExpr, out houdiniConstant)) { Contract.Assert(houdiniConstant != null); From 2886d7ebc998a4ec4a76011e747c6e252cf7c1bb Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 15:02:16 +0200 Subject: [PATCH 36/70] Fix --- Source/VCGeneration/ConditionGeneration.cs | 12 ++---------- Source/VCGeneration/Splits/BlockRewriter.cs | 14 ++++++++------ .../VCGeneration/VerificationConditionGenerator.cs | 2 +- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index ae78190d8..0e93652af 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -879,16 +879,8 @@ private void AddDebugInfo(Cmd c, Dictionary incarnationMap, List { foreach (var param in current.Params) { - if (param is IdentifierExpr identifierExpr) - { - if (incarnationMap.ContainsKey(identifierExpr.Decl)) - { - debugExprs.Add(incarnationMap[identifierExpr.Decl]); - } - else - { - debugExprs.Add(identifierExpr); - } + if (param is IdentifierExpr identifierExpr) { + debugExprs.Add(incarnationMap.GetValueOrDefault(identifierExpr.Decl, identifierExpr)); } else { diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index fdf552704..15c79c4ba 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -86,7 +86,6 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks(ISet blocksToInclude, IEnumerable blocksReversed, ISet freeAssumeBlocks) { - var duplicator = new Duplicator(); var newBlocks = new List(); var oldToNewBlockMap = new Dictionary(blocksToInclude.Count); @@ -97,15 +96,18 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead if (!blocksToInclude.Contains(block)) { continue; } - var newBlock = (Block)duplicator.Visit(block); + + var newBlock = new Block(block.tok) { + Label = block.Label + }; newBlocks.Add(newBlock); oldToNewBlockMap[block] = newBlock; // freeBlocks consist of the predecessors of the relevant foci. // Their assertions turn into assumes and any splits inside them are disabled. - if(freeAssumeBlocks.Contains(block)) - { - newBlock.Cmds = block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList(); - } + newBlock.Cmds = freeAssumeBlocks.Contains(block) + ? block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList() + : block.Cmds; + if (block.TransferCmd is GotoCmd gtc) { var targets = gtc.LabelTargets.Where(blocksToInclude.Contains).ToList(); diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index 5824a4c7f..e7cad8c53 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -568,7 +568,7 @@ public override void OnModel(IList labels /*!*/ /*!*/, Model model, Contract.Assert(traceNodes.Contains(entryBlock)); trace.Add(entryBlock); - Counterexample newCounterexample = TraceCounterexample(options, entryBlock, traceNodes, trace, model, MvInfo, + var newCounterexample = TraceCounterexample(options, entryBlock, traceNodes, trace, model, MvInfo, debugInfos, context, split, new Dictionary()); if (newCounterexample == null) From feeda38a93ac89b4d7611ad6358d2c04a9fffebc Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 15:03:45 +0200 Subject: [PATCH 37/70] Update test --- Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs index 6c39f1ec0..2500faf71 100644 --- a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs @@ -98,8 +98,9 @@ procedure Procedure(y: int) var engine = ExecutionEngine.CreateWithoutSharedCache(options); var tasks = await engine.GetVerificationTasks(program); - // The first split is empty. Maybe it can be optimized away - Assert.AreEqual(4, tasks.Count); + // The implicit return at the end gets a separate VC. + // first split is empty. Maybe it can be optimized away + Assert.AreEqual(5, tasks.Count); var outcomes = new List { SolverOutcome.Invalid, SolverOutcome.Valid, SolverOutcome.Invalid, SolverOutcome.Valid }; for (var index = 0; index < outcomes.Count; index++) From 1ae99e300aa10df4eac29ef9298112ec9fc39675 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 15:11:33 +0200 Subject: [PATCH 38/70] Fix test --- Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs b/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs index fb0087cd8..94505c1c9 100644 --- a/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/RandomSeedTest.cs @@ -17,7 +17,7 @@ public class RandomSeedTest axiom N <= 3; procedure nEquals3() - ensures true; + ensures 1 == 1; { }"; From 2547c8737da09d367cf8095f01b98ac252944750 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 15:28:53 +0200 Subject: [PATCH 39/70] Save --- Source/VCGeneration/ConditionGeneration.cs | 4 +- .../Counterexample/Counterexample.cs | 60 +++++++++---------- .../VerificationConditionGenerator.cs | 28 ++++----- Test/aitest0/Issue25.bpl | 8 +-- 4 files changed, 49 insertions(+), 51 deletions(-) diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 0e93652af..c45c8a1de 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -222,7 +222,7 @@ protected static void InjectPreconditions(VCGenOptions options, ImplementationRu Block origStartBlock = impl.Blocks[0]; Block insertionPoint = new Block( - new Token(-17, -4), blockLabel, startCmds, + Token.NoToken, blockLabel, startCmds, new GotoCmd(impl.tok, new List {origStartBlock.Label}, new List {origStartBlock})); impl.Blocks.Insert(0, insertionPoint); // make insertionPoint the start block @@ -1391,7 +1391,7 @@ protected Block CreateBlockBetween(int predIndex, Block succ) bs.Add(succ); Block newBlock = new Block( - new Token(-17, -4), + Token.NoToken, newBlockLabel, new List(), new GotoCmd(Token.NoToken, ls, bs) diff --git a/Source/VCGeneration/Counterexample/Counterexample.cs b/Source/VCGeneration/Counterexample/Counterexample.cs index f49abf15c..6f7997e68 100644 --- a/Source/VCGeneration/Counterexample/Counterexample.cs +++ b/Source/VCGeneration/Counterexample/Counterexample.cs @@ -114,49 +114,47 @@ public void Print(int indent, TextWriter tw, Action blockAction = null) { int numBlock = -1; string ind = new string(' ', indent); - foreach (Block b in Trace) + foreach (Block block in Trace) { - Contract.Assert(b != null); + Contract.Assert(block != null); numBlock++; - if (b.tok == null) + if (block.tok == null) { tw.WriteLine("{0}", ind); } - else - { + else { // for ErrorTrace == 1 restrict the output; // do not print tokens with -17:-4 as their location because they have been // introduced in the translation and do not give any useful feedback to the user - if (!(Options.ErrorTrace == 1 && b.tok.line == -17 && b.tok.col == -4)) + if (Options.ErrorTrace == 1 && block.tok == Token.NoToken) { + continue; + } + + blockAction?.Invoke(block); + + tw.WriteLine("{4}{0}({1},{2}): {3}", block.tok.filename, block.tok.line, block.tok.col, block.Label, ind); + + for (int numInstr = 0; numInstr < block.Cmds.Count; numInstr++) { - if (blockAction != null) - { - blockAction(b); + var loc = new TraceLocation(numBlock, numInstr); + if (!CalleeCounterexamples.ContainsKey(loc)) { + continue; } - tw.WriteLine("{4}{0}({1},{2}): {3}", b.tok.filename, b.tok.line, b.tok.col, b.Label, ind); - - for (int numInstr = 0; numInstr < b.Cmds.Count; numInstr++) + var cmd = GetTraceCmd(loc); + var calleeName = GetCalledProcName(cmd); + if (calleeName.StartsWith(VC.StratifiedVerificationConditionGeneratorBase.recordProcName) && + Options.StratifiedInlining > 0) { - var loc = new TraceLocation(numBlock, numInstr); - if (CalleeCounterexamples.ContainsKey(loc)) - { - var cmd = GetTraceCmd(loc); - var calleeName = GetCalledProcName(cmd); - if (calleeName.StartsWith(VC.StratifiedVerificationConditionGeneratorBase.recordProcName) && - Options.StratifiedInlining > 0) - { - Contract.Assert(CalleeCounterexamples[loc].Args.Count == 1); - var arg = CalleeCounterexamples[loc].Args[0]; - tw.WriteLine("{0}value = {1}", ind, arg.ToString()); - } - else - { - tw.WriteLine("{1}Inlined call to procedure {0} begins", calleeName, ind); - CalleeCounterexamples[loc].Counterexample.Print(indent + 4, tw); - tw.WriteLine("{1}Inlined call to procedure {0} ends", calleeName, ind); - } - } + Contract.Assert(CalleeCounterexamples[loc].Args.Count == 1); + var arg = CalleeCounterexamples[loc].Args[0]; + tw.WriteLine("{0}value = {1}", ind, arg.ToString()); + } + else + { + tw.WriteLine("{1}Inlined call to procedure {0} begins", calleeName, ind); + CalleeCounterexamples[loc].Counterexample.Print(indent + 4, tw); + tw.WriteLine("{1}Inlined call to procedure {0} ends", calleeName, ind); } } } diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index e7cad8c53..310e72224 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -680,7 +680,7 @@ public void ConvertCFG2DAG(ImplementationRun run, Dictionary> // Recompute the predecessors, but first insert a dummy start node that is sure not to be the target of any goto (because the cutting of back edges // below assumes that the start node has no predecessor) impl.Blocks.Insert(0, - new Block(new Token(-17, -4), "0", new List(), + new Block(Token.NoToken, "0", new List(), new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] }))); ResetPredecessors(impl.Blocks); @@ -882,19 +882,19 @@ private void ConvertCFG2DAGStandard(Implementation impl, Dictionary 1) + GotoCmd gotoCmd = backEdgeNode.TransferCmd as GotoCmd; + if (gotoCmd is { LabelTargets.Count: > 1 }) { // then remove the backedge by removing the target block from the list of gotos List remainingTargets = new List(); List remainingLabels = new List(); - Contract.Assume(gtc.LabelNames != null); - for (int i = 0, n = gtc.LabelTargets.Count; i < n; i++) + Contract.Assume(gotoCmd.LabelNames != null); + for (int i = 0, n = gotoCmd.LabelTargets.Count; i < n; i++) { - if (gtc.LabelTargets[i] != header) + if (gotoCmd.LabelTargets[i] != header) { - remainingTargets.Add(gtc.LabelTargets[i]); - remainingLabels.Add(gtc.LabelNames[i]); + remainingTargets.Add(gotoCmd.LabelTargets[i]); + remainingLabels.Add(gotoCmd.LabelNames[i]); } else { @@ -902,20 +902,20 @@ private void ConvertCFG2DAGStandard(Implementation impl, Dictionary N < N); // Used to verify at some point (see https://github.com/boogie-org/boogie/issues/25). { -var x: int; -x := -N; -while (x != x) { -} + var x: int; + x := -N; + while (x != x) { + } } From 24e5b4dac57a830be979eaf8097101a31f8378b3 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 15:39:32 +0200 Subject: [PATCH 40/70] Extract some code into file RemoveBackEdges --- Source/Houdini/Houdini.cs | 2 +- Source/Houdini/HoudiniSession.cs | 5 +- Source/VCGeneration/ConditionGeneration.cs | 18 +- Source/VCGeneration/StratifiedVC.cs | 5 +- .../Transformations/RemoveBackEdges.cs | 607 +++++++++++++++++ .../VerificationConditionGenerator.cs | 632 +----------------- 6 files changed, 626 insertions(+), 643 deletions(-) create mode 100644 Source/VCGeneration/Transformations/RemoveBackEdges.cs diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index 7a74a1f5d..4efc13c77 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -491,7 +491,7 @@ protected void Initialize(TextWriter traceWriter, Program program, HoudiniSessio } var session = new HoudiniSession(this, vcgen, proverInterface, program, - new ImplementationRun(impl, traceWriter), stats, taskID: GetTaskID()); + new ImplementationRun(impl, traceWriter), stats, taskId: GetTaskID()); houdiniSessions.Add(impl, session); } catch (VCGenException) diff --git a/Source/Houdini/HoudiniSession.cs b/Source/Houdini/HoudiniSession.cs index 35dcfe685..ee24e7bfa 100644 --- a/Source/Houdini/HoudiniSession.cs +++ b/Source/Houdini/HoudiniSession.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using VCGeneration.Transformations; namespace Microsoft.Boogie.Houdini { @@ -157,7 +158,7 @@ public bool InUnsatCore(Variable constant) } public HoudiniSession(Houdini houdini, VerificationConditionGenerator vcgen, ProverInterface proverInterface, Program program, - ImplementationRun run, HoudiniStatistics stats, int taskID = -1) + ImplementationRun run, HoudiniStatistics stats, int taskId = -1) { var impl = run.Implementation; this.Description = impl.Name; @@ -166,7 +167,7 @@ public HoudiniSession(Houdini houdini, VerificationConditionGenerator vcgen, Pro collector = new VerificationResultCollector(houdini.Options); collector.OnProgress?.Invoke("HdnVCGen", 0, 0, 0.0); - vcgen.ConvertCFG2DAG(run, taskID: taskID); + new RemoveBackEdges(vcgen).ConvertCfg2Dag(run, taskID: taskId); var gotoCmdOrigins = vcgen.PassifyImpl(run, out var mvInfo); ExistentialConstantCollector.CollectHoudiniConstants(houdini, impl, out var ecollector); diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index c45c8a1de..8188ebb07 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -66,7 +66,7 @@ public static VcOutcome ProverInterfaceOutcomeToConditionGenerationOutcome(Solve [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(cce.NonNullDictionaryAndValues(incarnationOriginMap)); + Contract.Invariant(cce.NonNullDictionaryAndValues(IncarnationOriginMap)); Contract.Invariant(program != null); } @@ -81,9 +81,9 @@ void ObjectInvariant() public List CurrentLocalVariables { get; set; } = null; // shared across each implementation; created anew for each implementation - protected Dictionary variable2SequenceNumber; + public Dictionary Variable2SequenceNumber; - public Dictionary incarnationOriginMap = new Dictionary(); + public Dictionary IncarnationOriginMap = new(); public Program program; public CheckerPool CheckerPool { get; } @@ -472,7 +472,7 @@ public static void ResetPredecessors(List blocks) protected Variable CreateIncarnation(Variable x, Absy a) { - Contract.Requires(this.variable2SequenceNumber != null); + Contract.Requires(this.Variable2SequenceNumber != null); Contract.Requires(this.CurrentLocalVariables != null); Contract.Requires(a is Block || a is AssignCmd || a is HavocCmd); @@ -480,13 +480,13 @@ protected Variable CreateIncarnation(Variable x, Absy a) Contract.Ensures(Contract.Result() != null); int currentIncarnationNumber = - variable2SequenceNumber.ContainsKey(x) - ? variable2SequenceNumber[x] + Variable2SequenceNumber.ContainsKey(x) + ? Variable2SequenceNumber[x] : -1; Variable v = new Incarnation(x, currentIncarnationNumber + 1); - variable2SequenceNumber[x] = currentIncarnationNumber + 1; + Variable2SequenceNumber[x] = currentIncarnationNumber + 1; CurrentLocalVariables.Add(v); - incarnationOriginMap.Add((Incarnation) v, a); + IncarnationOriginMap.Add((Incarnation) v, a); return v; } @@ -1372,7 +1372,7 @@ NAryExpr TypedExprEq(Expr e0, Expr e1, bool doNotResolveOverloading = false) /// Creates a new block to add to impl.Blocks, where impl is the implementation that contains /// succ. Caller must do the add to impl.Blocks. /// - protected Block CreateBlockBetween(int predIndex, Block succ) + public Block CreateBlockBetween(int predIndex, Block succ) { Contract.Requires(0 <= predIndex && predIndex < succ.Predecessors.Count); diff --git a/Source/VCGeneration/StratifiedVC.cs b/Source/VCGeneration/StratifiedVC.cs index f23404af0..8d33b9848 100644 --- a/Source/VCGeneration/StratifiedVC.cs +++ b/Source/VCGeneration/StratifiedVC.cs @@ -7,6 +7,7 @@ using System.Diagnostics.Contracts; using Microsoft.BaseTypes; using Microsoft.Boogie.VCExprAST; +using VCGeneration.Transformations; namespace VC { @@ -485,7 +486,7 @@ public void GenerateVCBoolControl() // Passify Program program = vcgen.program; ProverInterface proverInterface = vcgen.prover; - vcgen.ConvertCFG2DAG(run); + new RemoveBackEdges(vcgen).ConvertCfg2Dag(run); vcgen.PassifyImpl(run, out mvInfo); VCExpressionGenerator gen = proverInterface.VCExprGen; @@ -633,7 +634,7 @@ public void GenerateVC() Program program = vcgen.program; ProverInterface proverInterface = vcgen.prover; - vcgen.ConvertCFG2DAG(run); + new RemoveBackEdges(vcgen).ConvertCfg2Dag(run); vcgen.PassifyImpl(run, out mvInfo); VCExpressionGenerator gen = proverInterface.VCExprGen; diff --git a/Source/VCGeneration/Transformations/RemoveBackEdges.cs b/Source/VCGeneration/Transformations/RemoveBackEdges.cs new file mode 100644 index 000000000..d8a43e66b --- /dev/null +++ b/Source/VCGeneration/Transformations/RemoveBackEdges.cs @@ -0,0 +1,607 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using Microsoft.Boogie; +using Microsoft.Boogie.GraphUtil; +using VC; + +namespace VCGeneration.Transformations; + +public class RemoveBackEdges { + private VerificationConditionGenerator generator; + + public RemoveBackEdges(VerificationConditionGenerator generator) { + this.generator = generator; + } + + public void ConvertCfg2Dag(ImplementationRun run, Dictionary> edgesCut = null, int taskID = -1) + { + var impl = run.Implementation; + Contract.Requires(impl != null); + impl.PruneUnreachableBlocks(generator.Options); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization + + generator.CurrentLocalVariables = impl.LocVars; + generator.Variable2SequenceNumber = new Dictionary(); + generator.IncarnationOriginMap = new Dictionary(); + + #region Debug Tracing + + if (generator.Options.TraceVerify) + { + run.OutputWriter.WriteLine("original implementation"); + ConditionGeneration.EmitImpl(generator.Options, run, false); + } + + #endregion + + #region Debug Tracing + + if (generator.Options.TraceVerify) + { + run.OutputWriter.WriteLine("after desugaring sugared commands like procedure calls"); + ConditionGeneration.EmitImpl(generator.Options, run, true); + } + + #endregion + + // Recompute the predecessors, but first insert a dummy start node that is sure not to be the target of any goto (because the cutting of back edges + // below assumes that the start node has no predecessor) + impl.Blocks.Insert(0, + new Block(Token.NoToken, "0", new List(), + new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] }))); + ConditionGeneration.ResetPredecessors(impl.Blocks); + + var k = Math.Max(generator.Options.KInductionDepth, + QKeyValue.FindIntAttribute(impl.Attributes, "kInductionDepth", -1)); + if (k < 0) + { + ConvertCfg2DagStandard(impl, edgesCut, taskID); + } + else + { + ConvertCfg2DagkInduction(impl, edgesCut, taskID, k); + } + + #region Debug Tracing + + if (generator.Options.TraceVerify) + { + run.OutputWriter.WriteLine("after conversion into a DAG"); + ConditionGeneration.EmitImpl(generator.Options, run, true); + } + + #endregion + } + + private void ConvertCfg2DagStandard(Implementation impl, Dictionary> edgesCut, int taskID) + { + #region Convert program CFG into a DAG + + #region Use the graph library to figure out where the (natural) loops are + + #region Create the graph by adding the source node and each edge + + Graph g = Program.GraphFromImpl(impl); + + #endregion + + //Graph g = program.ProcessLoops(impl); + + g.ComputeLoops(); // this is the call that does all of the processing + if (!g.Reducible) + { + throw new VCGenException("Irreducible flow graphs are unsupported."); + } + + #endregion + + #region Cut the backedges, push assert/assume statements from loop header into predecessors, change them all into assume statements at top of loop, introduce havoc statements + + foreach (Block header in cce.NonNull(g.Headers)) + { + Contract.Assert(header != null); + IDictionary backEdgeNodes = new Dictionary(); + foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(b != null); + backEdgeNodes.Add(b, null); + } + + #region Find the (possibly empty) prefix of assert commands in the header, replace each assert with an assume of the same condition + + List prefixOfPredicateCmdsInit = new List(); + List prefixOfPredicateCmdsMaintained = new List(); + for (int i = 0, n = header.Cmds.Count; i < n; i++) { + if (header.Cmds[i] is not PredicateCmd predicateCmd) { + if (header.Cmds[i] is CommentCmd) { + // ignore + continue; + } + + break; // stop when an assignment statement (or any other non-predicate cmd) is encountered + } + + if (predicateCmd is AssertCmd assertCmd) { + AssertCmd initAssertCmd; + + if (generator.Options.ConcurrentHoudini) { + Contract.Assert(taskID >= 0); + initAssertCmd = generator.Options.Cho[taskID].DisableLoopInvEntryAssert + ? new LoopInitAssertCmd(assertCmd.tok, Expr.True, assertCmd) + : new LoopInitAssertCmd(assertCmd.tok, assertCmd.Expr, assertCmd); + } else { + initAssertCmd = new LoopInitAssertCmd(assertCmd.tok, assertCmd.Expr, assertCmd); + } + + initAssertCmd.Attributes = (QKeyValue)assertCmd.Attributes?.Clone(); + // Copy any {:id ...} from the invariant to the assertion that it's established, so + // we can track it while analyzing verification coverage. + initAssertCmd.CopyIdWithModificationsFrom(assertCmd.tok, assertCmd, + id => new TrackedInvariantEstablished(id)); + + prefixOfPredicateCmdsInit.Add(initAssertCmd); + + LoopInvMaintainedAssertCmd maintainedAssertCmd; + if (generator.Options.ConcurrentHoudini) { + Contract.Assert(taskID >= 0); + maintainedAssertCmd = generator.Options.Cho[taskID].DisableLoopInvMaintainedAssert + ? new LoopInvMaintainedAssertCmd(assertCmd.tok, Expr.True, assertCmd) + : new LoopInvMaintainedAssertCmd(assertCmd.tok, assertCmd.Expr, assertCmd); + } else { + maintainedAssertCmd = new LoopInvMaintainedAssertCmd(assertCmd.tok, assertCmd.Expr, assertCmd); + } + + maintainedAssertCmd.Attributes = (QKeyValue)assertCmd.Attributes?.Clone(); + // Copy any {:id ...} from the invariant to the assertion that it's maintained, so + // we can track it while analyzing verification coverage. + (maintainedAssertCmd as ICarriesAttributes).CopyIdWithModificationsFrom(assertCmd.tok, assertCmd, + id => new TrackedInvariantMaintained(id)); + + prefixOfPredicateCmdsMaintained.Add(maintainedAssertCmd); + AssumeCmd assume = new AssumeCmd(assertCmd.tok, assertCmd.Expr); + // Copy any {:id ...} from the invariant to the assumption used within the body, so + // we can track it while analyzing verification coverage. + assume.CopyIdWithModificationsFrom(assertCmd.tok, assertCmd, + id => new TrackedInvariantAssumed(id)); + + header.Cmds[i] = assume; + } else { + Contract.Assert(predicateCmd is AssumeCmd); + if (generator.Options.AlwaysAssumeFreeLoopInvariants) { + // Usually, "free" stuff, like free loop invariants (and the assume statements + // that stand for such loop invariants) are ignored on the checking side. This + // command-line option changes that behavior to always assume the conditions. + prefixOfPredicateCmdsInit.Add(predicateCmd); + prefixOfPredicateCmdsMaintained.Add(predicateCmd); + } + } + } + + #endregion + + #region Copy the prefix of predicate commands into each predecessor. Do this *before* cutting the backedge!! + + for (int predIndex = 0, n = header.Predecessors.Count; predIndex < n; predIndex++) + { + Block pred = cce.NonNull(header.Predecessors[predIndex]); + + // Create a block between header and pred for the predicate commands if pred has more than one successor + GotoCmd gotocmd = cce.NonNull((GotoCmd)pred.TransferCmd); + Contract.Assert(gotocmd.LabelNames != + null); // if "pred" is really a predecessor, it may be a GotoCmd with at least one label + if (gotocmd.LabelNames.Count > 1) + { + Block newBlock = generator.CreateBlockBetween(predIndex, header); + impl.Blocks.Add(newBlock); + + // if pred is a back edge node, then now newBlock is the back edge node + if (backEdgeNodes.ContainsKey(pred)) + { + backEdgeNodes.Remove(pred); + backEdgeNodes.Add(newBlock, null); + } + + pred = newBlock; + } + + // Add the predicate commands + if (backEdgeNodes.ContainsKey(pred)) + { + pred.Cmds.AddRange(prefixOfPredicateCmdsMaintained); + } + else + { + pred.Cmds.AddRange(prefixOfPredicateCmdsInit); + } + } + + #endregion + + #region Cut the back edge + + foreach (Block backEdgeNode in cce.NonNull(backEdgeNodes.Keys)) + { + Contract.Assert(backEdgeNode != null); + Debug.Assert(backEdgeNode.TransferCmd is GotoCmd, + "An node was identified as the source for a backedge, but it does not have a goto command."); + GotoCmd gotoCmd = backEdgeNode.TransferCmd as GotoCmd; + if (gotoCmd is { LabelTargets.Count: > 1 }) + { + // then remove the backedge by removing the target block from the list of gotos + List remainingTargets = new List(); + List remainingLabels = new List(); + Contract.Assume(gotoCmd.LabelNames != null); + for (int i = 0, n = gotoCmd.LabelTargets.Count; i < n; i++) + { + if (gotoCmd.LabelTargets[i] != header) + { + remainingTargets.Add(gotoCmd.LabelTargets[i]); + remainingLabels.Add(gotoCmd.LabelNames[i]); + } + else + { + RecordCutEdge(edgesCut, backEdgeNode, header); + } + } + + gotoCmd.LabelTargets = remainingTargets; + gotoCmd.LabelNames = remainingLabels; + } + else + { + // This backedge is the only out-going edge from this node. + // Add an "assume false" statement to the end of the statements + // inside of the block and change the goto command to a return command. + var ac = new AssumeCmd(Token.NoToken, Expr.False); + backEdgeNode.Cmds.Add(ac); + backEdgeNode.TransferCmd = new ReturnCmd(backEdgeNode.TransferCmd.tok); + if (gotoCmd is { LabelTargets.Count: 1 }) + { + RecordCutEdge(edgesCut, backEdgeNode, gotoCmd.LabelTargets[0]); + } + } + + #region Remove the backedge node from the list of predecessor nodes in the header + + List newPreds = new List(); + foreach (Block p in header.Predecessors) + { + if (p != backEdgeNode) + { + newPreds.Add(p); + } + } + + header.Predecessors = newPreds; + + #endregion + } + + #endregion + + #region Collect all variables that are assigned to in all of the natural loops for which this is the header + + List varsToHavoc = VarsAssignedInLoop(g, header); + List havocExprs = new List(); + foreach (Variable v in varsToHavoc) + { + Contract.Assert(v != null); + IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); + if (!havocExprs.Contains(ie)) + { + havocExprs.Add(ie); + } + } + + // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct + // the source location for this later on + HavocCmd hc = new HavocCmd(header.tok, havocExprs); + List newCmds = new List(); + newCmds.Add(hc); + foreach (Cmd c in header.Cmds) + { + newCmds.Add(c); + } + + header.Cmds = newCmds; + + #endregion + } + + #endregion + + #endregion Convert program CFG into a DAG + } + + private void ConvertCfg2DagkInduction(Implementation impl, Dictionary> edgesCut, int taskID, + int inductionK) + { + // K-induction has not been adapted to be aware of these parameters which standard CFG to DAG transformation uses + Contract.Requires(edgesCut == null); + Contract.Requires(taskID == -1); + Contract.Requires(0 <= inductionK); + + bool contRuleApplication = true; + while (contRuleApplication) + { + contRuleApplication = false; + + #region Use the graph library to figure out where the (natural) loops are + + #region Create the graph by adding the source node and each edge + + Graph g = Program.GraphFromImpl(impl); + + #endregion + + g.ComputeLoops(); // this is the call that does all of the processing + if (!g.Reducible) + { + throw new VCGenException("Irreducible flow graphs are unsupported."); + } + + #endregion + + foreach (Block header in cce.NonNull(g.Headers)) + { + Contract.Assert(header != null); + + #region Debug Tracing + + if (generator.Options.TraceVerify) + { + generator.Options.OutputWriter.WriteLine("Applying k-induction rule with k=" + inductionK); + } + + #endregion + + #region generate the step case + + Block newHeader = DuplicateLoop(impl, g, header, null, + false, false, "_step_assertion"); + for (int i = 0; i < inductionK; ++i) + { + newHeader = DuplicateLoop(impl, g, header, newHeader, + true, true, + "_step_" + (inductionK - i)); + } + + #endregion + + #region havoc variables that can be assigned in the loop + + List varsToHavoc = VarsAssignedInLoop(g, header); + List havocExprs = new List(); + foreach (Variable v in varsToHavoc) + { + Contract.Assert(v != null); + IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); + if (!havocExprs.Contains(ie)) + { + havocExprs.Add(ie); + } + } + + // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct + // the source location for this later on + HavocCmd hc = new HavocCmd(newHeader.tok, havocExprs); + List havocCmds = new List(); + havocCmds.Add(hc); + + var havocBlock = new Block(newHeader.tok, newHeader.Label + "_havoc", havocCmds, + new GotoCmd(newHeader.tok, new List { newHeader })); + + impl.Blocks.Add(havocBlock); + newHeader.Predecessors.Add(havocBlock); + newHeader = havocBlock; + + #endregion + + #region generate the base case loop copies + + for (int i = 0; i < inductionK; ++i) + { + newHeader = DuplicateLoop(impl, g, header, newHeader, + false, false, + "_base_" + (inductionK - i)); + } + + #endregion + + #region redirect into the new loop copies and remove the original loop (but don't redirect back-edges) + + IDictionary backEdgeNodes = new Dictionary(); + foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(b != null); + backEdgeNodes.Add(b, null); + } + + for (int predIndex = 0, n = header.Predecessors.Count; predIndex < n; predIndex++) + { + Block pred = cce.NonNull(header.Predecessors[predIndex]); + if (!backEdgeNodes.ContainsKey(pred)) + { + GotoCmd gc = pred.TransferCmd as GotoCmd; + Contract.Assert(gc != null); + for (int i = 0; i < gc.LabelTargets.Count; ++i) + { + if (gc.LabelTargets[i] == header) + { + gc.LabelTargets[i] = newHeader; + gc.LabelNames[i] = newHeader.Label; + newHeader.Predecessors.Add(pred); + } + } + } + } + + impl.PruneUnreachableBlocks(generator.Options); + + #endregion + + contRuleApplication = true; + break; + } + } + + ConditionGeneration.ResetPredecessors(impl.Blocks); + impl.FreshenCaptureStates(); + } + + private static void RecordCutEdge(Dictionary> edgesCut, Block from, Block to) { + edgesCut?.GetOrCreate(from, () => new List()).Add(to); + } + + private static List VarsAssignedInLoop(Graph g, Block header) + { + List varsToHavoc = new List(); + foreach (var backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + foreach (var c in b.Cmds) + { + Contract.Assert(c != null); + varsToHavoc.AddRange(c.GetAssignedVariables()); + } + } + } + + return varsToHavoc; + } + + public static IEnumerable VarsReferencedInLoop(Graph g, Block header) + { + HashSet referencedVars = new HashSet(); + foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + foreach (Cmd c in b.Cmds) + { + Contract.Assert(c != null); + var Collector = new VariableCollector(); + Collector.Visit(c); + foreach (var v in Collector.usedVars) + { + referencedVars.Add(v); + } + } + } + } + + return referencedVars; + } + + private Block DuplicateLoop(Implementation impl, Graph g, + Block header, Block nextHeader, bool cutExits, + bool toAssumptions, string suffix) + { + var ori2CopiedBlocks = new Dictionary(); + var duplicator = new Duplicator(); + + #region create copies of all blocks in the loop + + foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) + { + Contract.Assert(backEdgeNode != null); + foreach (Block b in g.NaturalLoops(header, backEdgeNode)) + { + Contract.Assert(b != null); + if (ori2CopiedBlocks.ContainsKey(b)) { + continue; + } + + Block copy = (Block)duplicator.Visit(b); + copy.Cmds = new List(copy + .Cmds); // Philipp Ruemmer commented that this was necessary due to a bug in the Duplicator. That was a long time; worth checking whether this has been fixed + copy.Predecessors = new List(); + copy.Label += suffix; + + #region turn asserts into assumptions + + if (toAssumptions) + { + for (int i = 0; i < copy.Cmds.Count; ++i) + { + if (copy.Cmds[i] is AssertCmd ac) + { + copy.Cmds[i] = new AssumeCmd(ac.tok, ac.Expr); + } + } + } + + #endregion + + impl.Blocks.Add(copy); + ori2CopiedBlocks.Add(b, copy); + } + } + + #endregion + + #region adjust the transfer commands of the newly created blocks + + foreach (KeyValuePair pair in ori2CopiedBlocks) + { + var copy = pair.Value; + if (copy.TransferCmd is GotoCmd gc) + { + List newTargets = new List(); + List newLabels = new List(); + + for (int i = 0; i < gc.LabelTargets.Count; ++i) + { + if (gc.LabelTargets[i] == header) + { + if (nextHeader != null) + { + newTargets.Add(nextHeader); + newLabels.Add(nextHeader.Label); + nextHeader.Predecessors.Add(copy); + } + } + else if (ori2CopiedBlocks.TryGetValue(gc.LabelTargets[i], out var newTarget)) + { + newTargets.Add(newTarget); + newLabels.Add(newTarget.Label); + newTarget.Predecessors.Add(copy); + } + else if (!cutExits) + { + newTargets.Add(gc.LabelTargets[i]); + newLabels.Add(gc.LabelNames[i]); + gc.LabelTargets[i].Predecessors.Add(copy); + } + } + + if (newTargets.Count == 0) + { + // if no targets are left, we assume false and return + copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + copy.TransferCmd = new ReturnCmd(Token.NoToken); + } + else + { + copy.TransferCmd = new GotoCmd(gc.tok, newLabels, newTargets); + } + } + else if (cutExits && (copy.TransferCmd is ReturnCmd)) + { + // because return is a kind of exit from the loop, we + // assume false to cut this path + copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); + } + } + + #endregion + + return ori2CopiedBlocks[header]; + } +} \ No newline at end of file diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index 310e72224..06906367b 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -108,8 +108,8 @@ public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, List(); - vcgen.incarnationOriginMap = new Dictionary(); + vcgen.Variable2SequenceNumber = new Dictionary(); + vcgen.IncarnationOriginMap = new Dictionary(); vcgen.CurrentLocalVariables = codeExpr.LocVars; ResetPredecessors(codeExpr.Blocks); @@ -434,7 +434,7 @@ public void PrepareImplementation(ImplementationRun run, VerifierCallback callba if (!data.ConvertedToDAG) { data.ConvertedToDAG = true; - ConvertCFG2DAG(run); + new RemoveBackEdges(this).ConvertCfg2Dag(run); } smokeTester = null; @@ -633,632 +633,6 @@ public override void OnProverWarning(string msg) } } - private void RecordCutEdge(Dictionary> edgesCut, Block from, Block to) - { - if (edgesCut != null) - { - if (!edgesCut.ContainsKey(from)) - { - edgesCut.Add(from, new List()); - } - - edgesCut[from].Add(to); - } - } - - public void ConvertCFG2DAG(ImplementationRun run, Dictionary> edgesCut = null, int taskID = -1) - { - var impl = run.Implementation; - Contract.Requires(impl != null); - impl.PruneUnreachableBlocks( - Options); // This is needed for VCVariety.BlockNested, and is otherwise just an optimization - - CurrentLocalVariables = impl.LocVars; - variable2SequenceNumber = new Dictionary(); - incarnationOriginMap = new Dictionary(); - - #region Debug Tracing - - if (Options.TraceVerify) - { - run.OutputWriter.WriteLine("original implementation"); - EmitImpl(Options, run, false); - } - - #endregion - - #region Debug Tracing - - if (Options.TraceVerify) - { - run.OutputWriter.WriteLine("after desugaring sugared commands like procedure calls"); - EmitImpl(Options, run, true); - } - - #endregion - - // Recompute the predecessors, but first insert a dummy start node that is sure not to be the target of any goto (because the cutting of back edges - // below assumes that the start node has no predecessor) - impl.Blocks.Insert(0, - new Block(Token.NoToken, "0", new List(), - new GotoCmd(Token.NoToken, new List { impl.Blocks[0].Label }, new List { impl.Blocks[0] }))); - ResetPredecessors(impl.Blocks); - - var k = Math.Max(Options.KInductionDepth, - QKeyValue.FindIntAttribute(impl.Attributes, "kInductionDepth", -1)); - if (k < 0) - { - ConvertCFG2DAGStandard(impl, edgesCut, taskID); - } - else - { - ConvertCFG2DAGKInduction(impl, edgesCut, taskID, k); - } - - #region Debug Tracing - - if (Options.TraceVerify) - { - run.OutputWriter.WriteLine("after conversion into a DAG"); - EmitImpl(Options, run, true); - } - - #endregion - } - - private void ConvertCFG2DAGStandard(Implementation impl, Dictionary> edgesCut, int taskID) - { - #region Convert program CFG into a DAG - - #region Use the graph library to figure out where the (natural) loops are - - #region Create the graph by adding the source node and each edge - - Graph g = Program.GraphFromImpl(impl); - - #endregion - - //Graph g = program.ProcessLoops(impl); - - g.ComputeLoops(); // this is the call that does all of the processing - if (!g.Reducible) - { - throw new VCGenException("Irreducible flow graphs are unsupported."); - } - - #endregion - - #region Cut the backedges, push assert/assume statements from loop header into predecessors, change them all into assume statements at top of loop, introduce havoc statements - - foreach (Block header in cce.NonNull(g.Headers)) - { - Contract.Assert(header != null); - IDictionary backEdgeNodes = new Dictionary(); - foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(b != null); - backEdgeNodes.Add(b, null); - } - - #region Find the (possibly empty) prefix of assert commands in the header, replace each assert with an assume of the same condition - - List prefixOfPredicateCmdsInit = new List(); - List prefixOfPredicateCmdsMaintained = new List(); - for (int i = 0, n = header.Cmds.Count; i < n; i++) - { - PredicateCmd predicateCmd = header.Cmds[i] as PredicateCmd; - if (predicateCmd != null) - { - if (predicateCmd is AssertCmd) - { - AssertCmd assertCmd = (AssertCmd)predicateCmd; - AssertCmd initAssertCmd = null; - - if (Options.ConcurrentHoudini) - { - Contract.Assert(taskID >= 0); - if (Options.Cho[taskID].DisableLoopInvEntryAssert) - { - initAssertCmd = new LoopInitAssertCmd(assertCmd.tok, Expr.True, assertCmd); - } - else - { - initAssertCmd = new LoopInitAssertCmd(assertCmd.tok, assertCmd.Expr, assertCmd); - } - } - else - { - initAssertCmd = new LoopInitAssertCmd(assertCmd.tok, assertCmd.Expr, assertCmd); - } - - initAssertCmd.Attributes = (QKeyValue)assertCmd.Attributes?.Clone(); - // Copy any {:id ...} from the invariant to the assertion that it's established, so - // we can track it while analyzing verification coverage. - (initAssertCmd as ICarriesAttributes).CopyIdWithModificationsFrom(assertCmd.tok, assertCmd, - id => new TrackedInvariantEstablished(id)); - - prefixOfPredicateCmdsInit.Add(initAssertCmd); - - LoopInvMaintainedAssertCmd maintainedAssertCmd; - if (Options.ConcurrentHoudini) - { - Contract.Assert(taskID >= 0); - if (Options.Cho[taskID].DisableLoopInvMaintainedAssert) - { - maintainedAssertCmd = new Bpl.LoopInvMaintainedAssertCmd(assertCmd.tok, Expr.True, assertCmd); - } - else - { - maintainedAssertCmd = new Bpl.LoopInvMaintainedAssertCmd(assertCmd.tok, assertCmd.Expr, assertCmd); - } - } - else - { - maintainedAssertCmd = new Bpl.LoopInvMaintainedAssertCmd(assertCmd.tok, assertCmd.Expr, assertCmd); - } - - maintainedAssertCmd.Attributes = (QKeyValue)assertCmd.Attributes?.Clone(); - // Copy any {:id ...} from the invariant to the assertion that it's maintained, so - // we can track it while analyzing verification coverage. - (maintainedAssertCmd as ICarriesAttributes).CopyIdWithModificationsFrom(assertCmd.tok, assertCmd, - id => new TrackedInvariantMaintained(id)); - - prefixOfPredicateCmdsMaintained.Add(maintainedAssertCmd); - AssumeCmd assume = new AssumeCmd(assertCmd.tok, assertCmd.Expr); - // Copy any {:id ...} from the invariant to the assumption used within the body, so - // we can track it while analyzing verification coverage. - (assume as ICarriesAttributes).CopyIdWithModificationsFrom(assertCmd.tok, assertCmd, - id => new TrackedInvariantAssumed(id)); - - header.Cmds[i] = assume; - } - else - { - Contract.Assert(predicateCmd is AssumeCmd); - if (Options.AlwaysAssumeFreeLoopInvariants) - { - // Usually, "free" stuff, like free loop invariants (and the assume statements - // that stand for such loop invariants) are ignored on the checking side. This - // command-line option changes that behavior to always assume the conditions. - prefixOfPredicateCmdsInit.Add(predicateCmd); - prefixOfPredicateCmdsMaintained.Add(predicateCmd); - } - } - } - else if (header.Cmds[i] is CommentCmd) - { - // ignore - } - else - { - break; // stop when an assignment statement (or any other non-predicate cmd) is encountered - } - } - - #endregion - - #region Copy the prefix of predicate commands into each predecessor. Do this *before* cutting the backedge!! - - for (int predIndex = 0, n = header.Predecessors.Count; predIndex < n; predIndex++) - { - Block pred = cce.NonNull(header.Predecessors[predIndex]); - - // Create a block between header and pred for the predicate commands if pred has more than one successor - GotoCmd gotocmd = cce.NonNull((GotoCmd)pred.TransferCmd); - Contract.Assert(gotocmd.LabelNames != - null); // if "pred" is really a predecessor, it may be a GotoCmd with at least one label - if (gotocmd.LabelNames.Count > 1) - { - Block newBlock = CreateBlockBetween(predIndex, header); - impl.Blocks.Add(newBlock); - - // if pred is a back edge node, then now newBlock is the back edge node - if (backEdgeNodes.ContainsKey(pred)) - { - backEdgeNodes.Remove(pred); - backEdgeNodes.Add(newBlock, null); - } - - pred = newBlock; - } - - // Add the predicate commands - if (backEdgeNodes.ContainsKey(pred)) - { - pred.Cmds.AddRange(prefixOfPredicateCmdsMaintained); - } - else - { - pred.Cmds.AddRange(prefixOfPredicateCmdsInit); - } - } - - #endregion - - #region Cut the back edge - - foreach (Block backEdgeNode in cce.NonNull(backEdgeNodes.Keys)) - { - Contract.Assert(backEdgeNode != null); - Debug.Assert(backEdgeNode.TransferCmd is GotoCmd, - "An node was identified as the source for a backedge, but it does not have a goto command."); - GotoCmd gotoCmd = backEdgeNode.TransferCmd as GotoCmd; - if (gotoCmd is { LabelTargets.Count: > 1 }) - { - // then remove the backedge by removing the target block from the list of gotos - List remainingTargets = new List(); - List remainingLabels = new List(); - Contract.Assume(gotoCmd.LabelNames != null); - for (int i = 0, n = gotoCmd.LabelTargets.Count; i < n; i++) - { - if (gotoCmd.LabelTargets[i] != header) - { - remainingTargets.Add(gotoCmd.LabelTargets[i]); - remainingLabels.Add(gotoCmd.LabelNames[i]); - } - else - { - RecordCutEdge(edgesCut, backEdgeNode, header); - } - } - - gotoCmd.LabelTargets = remainingTargets; - gotoCmd.LabelNames = remainingLabels; - } - else - { - // This backedge is the only out-going edge from this node. - // Add an "assume false" statement to the end of the statements - // inside of the block and change the goto command to a return command. - var ac = new AssumeCmd(Token.NoToken, Expr.False); - backEdgeNode.Cmds.Add(ac); - backEdgeNode.TransferCmd = new ReturnCmd(backEdgeNode.TransferCmd.tok); - if (gotoCmd is { LabelTargets.Count: 1 }) - { - RecordCutEdge(edgesCut, backEdgeNode, gotoCmd.LabelTargets[0]); - } - } - - #region Remove the backedge node from the list of predecessor nodes in the header - - List newPreds = new List(); - foreach (Block p in header.Predecessors) - { - if (p != backEdgeNode) - { - newPreds.Add(p); - } - } - - header.Predecessors = newPreds; - - #endregion - } - - #endregion - - #region Collect all variables that are assigned to in all of the natural loops for which this is the header - - List varsToHavoc = VarsAssignedInLoop(g, header); - List havocExprs = new List(); - foreach (Variable v in varsToHavoc) - { - Contract.Assert(v != null); - IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); - if (!havocExprs.Contains(ie)) - { - havocExprs.Add(ie); - } - } - - // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct - // the source location for this later on - HavocCmd hc = new HavocCmd(header.tok, havocExprs); - List newCmds = new List(); - newCmds.Add(hc); - foreach (Cmd c in header.Cmds) - { - newCmds.Add(c); - } - - header.Cmds = newCmds; - - #endregion - } - - #endregion - - #endregion Convert program CFG into a DAG - } - - public static List VarsAssignedInLoop(Graph g, Block header) - { - List varsToHavoc = new List(); - foreach (var backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(backEdgeNode != null); - foreach (Block b in g.NaturalLoops(header, backEdgeNode)) - { - Contract.Assert(b != null); - foreach (var c in b.Cmds) - { - Contract.Assert(c != null); - varsToHavoc.AddRange(c.GetAssignedVariables()); - } - } - } - - return varsToHavoc; - } - - public static IEnumerable VarsReferencedInLoop(Graph g, Block header) - { - HashSet referencedVars = new HashSet(); - foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(backEdgeNode != null); - foreach (Block b in g.NaturalLoops(header, backEdgeNode)) - { - Contract.Assert(b != null); - foreach (Cmd c in b.Cmds) - { - Contract.Assert(c != null); - var Collector = new VariableCollector(); - Collector.Visit(c); - foreach (var v in Collector.usedVars) - { - referencedVars.Add(v); - } - } - } - } - - return referencedVars; - } - - private void ConvertCFG2DAGKInduction(Implementation impl, Dictionary> edgesCut, int taskID, - int inductionK) - { - // K-induction has not been adapted to be aware of these parameters which standard CFG to DAG transformation uses - Contract.Requires(edgesCut == null); - Contract.Requires(taskID == -1); - Contract.Requires(0 <= inductionK); - - bool contRuleApplication = true; - while (contRuleApplication) - { - contRuleApplication = false; - - #region Use the graph library to figure out where the (natural) loops are - - #region Create the graph by adding the source node and each edge - - Graph g = Program.GraphFromImpl(impl); - - #endregion - - g.ComputeLoops(); // this is the call that does all of the processing - if (!g.Reducible) - { - throw new VCGenException("Irreducible flow graphs are unsupported."); - } - - #endregion - - foreach (Block header in cce.NonNull(g.Headers)) - { - Contract.Assert(header != null); - - #region Debug Tracing - - if (Options.TraceVerify) - { - Options.OutputWriter.WriteLine("Applying k-induction rule with k=" + inductionK); - } - - #endregion - - #region generate the step case - - Block newHeader = DuplicateLoop(impl, g, header, null, - false, false, "_step_assertion"); - for (int i = 0; i < inductionK; ++i) - { - newHeader = DuplicateLoop(impl, g, header, newHeader, - true, true, - "_step_" + (inductionK - i)); - } - - #endregion - - #region havoc variables that can be assigned in the loop - - List varsToHavoc = VarsAssignedInLoop(g, header); - List havocExprs = new List(); - foreach (Variable v in varsToHavoc) - { - Contract.Assert(v != null); - IdentifierExpr ie = new IdentifierExpr(Token.NoToken, v); - if (!havocExprs.Contains(ie)) - { - havocExprs.Add(ie); - } - } - - // pass the token of the enclosing loop header to the HavocCmd so we can reconstruct - // the source location for this later on - HavocCmd hc = new HavocCmd(newHeader.tok, havocExprs); - List havocCmds = new List(); - havocCmds.Add(hc); - - Block havocBlock = new Block(newHeader.tok, newHeader.Label + "_havoc", havocCmds, - new GotoCmd(newHeader.tok, new List { newHeader })); - - impl.Blocks.Add(havocBlock); - newHeader.Predecessors.Add(havocBlock); - newHeader = havocBlock; - - #endregion - - #region generate the base case loop copies - - for (int i = 0; i < inductionK; ++i) - { - newHeader = DuplicateLoop(impl, g, header, newHeader, - false, false, - "_base_" + (inductionK - i)); - } - - #endregion - - #region redirect into the new loop copies and remove the original loop (but don't redirect back-edges) - - IDictionary backEdgeNodes = new Dictionary(); - foreach (Block b in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(b != null); - backEdgeNodes.Add(b, null); - } - - for (int predIndex = 0, n = header.Predecessors.Count(); predIndex < n; predIndex++) - { - Block pred = cce.NonNull(header.Predecessors[predIndex]); - if (!backEdgeNodes.ContainsKey(pred)) - { - GotoCmd gc = pred.TransferCmd as GotoCmd; - Contract.Assert(gc != null); - for (int i = 0; i < gc.LabelTargets.Count(); ++i) - { - if (gc.LabelTargets[i] == header) - { - gc.LabelTargets[i] = newHeader; - gc.LabelNames[i] = newHeader.Label; - newHeader.Predecessors.Add(pred); - } - } - } - } - - impl.PruneUnreachableBlocks(Options); - - #endregion - - contRuleApplication = true; - break; - } - } - - ResetPredecessors(impl.Blocks); - impl.FreshenCaptureStates(); - } - - private Block DuplicateLoop(Implementation impl, Graph g, - Block header, Block nextHeader, bool cutExits, - bool toAssumptions, string suffix) - { - IDictionary ori2CopiedBlocks = new Dictionary(); - Duplicator duplicator = new Duplicator(); - - #region create copies of all blocks in the loop - - foreach (Block backEdgeNode in cce.NonNull(g.BackEdgeNodes(header))) - { - Contract.Assert(backEdgeNode != null); - foreach (Block b in g.NaturalLoops(header, backEdgeNode)) - { - Contract.Assert(b != null); - if (!ori2CopiedBlocks.ContainsKey(b)) - { - Block copy = (Block)duplicator.Visit(b); - copy.Cmds = new List(copy - .Cmds); // Philipp Ruemmer commented that this was necessary due to a bug in the Duplicator. That was a long time; worth checking whether this has been fixed - copy.Predecessors = new List(); - copy.Label = copy.Label + suffix; - - #region turn asserts into assumptions - - if (toAssumptions) - { - for (int i = 0; i < copy.Cmds.Count(); ++i) - { - AssertCmd ac = copy.Cmds[i] as AssertCmd; - if (ac != null) - { - copy.Cmds[i] = new AssumeCmd(ac.tok, ac.Expr); - } - } - } - - #endregion - - impl.Blocks.Add(copy); - ori2CopiedBlocks.Add(b, copy); - } - } - } - - #endregion - - #region adjust the transfer commands of the newly created blocks - - foreach (KeyValuePair pair in ori2CopiedBlocks) - { - Block copy = pair.Value; - GotoCmd gc = copy.TransferCmd as GotoCmd; - if (gc != null) - { - List newTargets = new List(); - List newLabels = new List(); - - for (int i = 0; i < gc.LabelTargets.Count(); ++i) - { - if (gc.LabelTargets[i] == header) - { - if (nextHeader != null) - { - newTargets.Add(nextHeader); - newLabels.Add(nextHeader.Label); - nextHeader.Predecessors.Add(copy); - } - } - else if (ori2CopiedBlocks.TryGetValue(gc.LabelTargets[i], out var newTarget)) - { - newTargets.Add(newTarget); - newLabels.Add(newTarget.Label); - newTarget.Predecessors.Add(copy); - } - else if (!cutExits) - { - newTargets.Add(gc.LabelTargets[i]); - newLabels.Add(gc.LabelNames[i]); - gc.LabelTargets[i].Predecessors.Add(copy); - } - } - - if (newTargets.Count() == 0) - { - // if no targets are left, we assume false and return - copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); - copy.TransferCmd = new ReturnCmd(Token.NoToken); - } - else - { - copy.TransferCmd = new GotoCmd(gc.tok, newLabels, newTargets); - } - } - else if (cutExits && (copy.TransferCmd is ReturnCmd)) - { - // because return is a kind of exit from the loop, we - // assume false to cut this path - copy.Cmds.Add(new AssumeCmd(Token.NoToken, Expr.False)); - } - } - - #endregion - - return ori2CopiedBlocks[header]; - } - public void DesugarCalls(Implementation impl) { foreach (Block block in impl.Blocks) From cc96494acb2faed7b0b5640dd53daf1488075079 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 15:48:06 +0200 Subject: [PATCH 41/70] Remove need for gotoCmdOrigins map --- .../Transformations/DesugarReturns.cs | 96 ++++++++++--------- .../Transformations/RemoveBackEdges.cs | 11 +-- .../VerificationConditionGenerator.cs | 12 +-- Test/aitest0/Issue25.bpl.expect | 8 +- 4 files changed, 62 insertions(+), 65 deletions(-) diff --git a/Source/VCGeneration/Transformations/DesugarReturns.cs b/Source/VCGeneration/Transformations/DesugarReturns.cs index e47d62dd7..d4e2fc4d2 100644 --- a/Source/VCGeneration/Transformations/DesugarReturns.cs +++ b/Source/VCGeneration/Transformations/DesugarReturns.cs @@ -38,7 +38,7 @@ public static Block GenerateUnifiedExit(Implementation impl, out Dictionary { unifiedExitLabel }; var gotoTargets = new List { unifiedExit }; - var gotoCmd = new GotoCmd(returnCmd.tok, gotoLabels, gotoTargets) { + var gotoCmd = new GotoCmd(new GotoFromReturn(returnCmd), gotoLabels, gotoTargets) { Attributes = returnCmd.Attributes }; gotoCmdOrigins[gotoCmd] = returnCmd; @@ -65,58 +65,66 @@ public static Block GenerateUnifiedExit(Implementation impl, out Dictionary public static void InjectPostConditions(VCGenOptions options, ImplementationRun run, Block unifiedExitBlock, Dictionary gotoCmdOrigins) + { + var impl = run.Implementation; + Contract.Requires(impl != null); + Contract.Requires(unifiedExitBlock != null); + Contract.Requires(gotoCmdOrigins != null); + Contract.Requires(impl.Proc != null); + Contract.Requires(unifiedExitBlock.TransferCmd is ReturnCmd); + + TokenTextWriter debugWriter = null; + if (options.PrintWithUniqueASTIds) { - var impl = run.Implementation; - Contract.Requires(impl != null); - Contract.Requires(unifiedExitBlock != null); - Contract.Requires(gotoCmdOrigins != null); - Contract.Requires(impl.Proc != null); - Contract.Requires(unifiedExitBlock.TransferCmd is ReturnCmd); + debugWriter = new TokenTextWriter("", run.OutputWriter, /*setTokens=*/ false, /*pretty=*/ false, options); + debugWriter.WriteLine("Effective postcondition:"); + } - TokenTextWriter debugWriter = null; - if (options.PrintWithUniqueASTIds) - { - debugWriter = new TokenTextWriter("", run.OutputWriter, /*setTokens=*/ false, /*pretty=*/ false, options); - debugWriter.WriteLine("Effective postcondition:"); - } + var formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); - var formalProcImplSubst = Substituter.SubstitutionFromDictionary(impl.GetImplFormalMap(options)); + // (free and checked) ensures clauses + foreach (Ensures ens in impl.Proc.Ensures) + { + Contract.Assert(ens != null); - // (free and checked) ensures clauses - foreach (Ensures ens in impl.Proc.Ensures) + if (!ens.Free) { - Contract.Assert(ens != null); - - if (!ens.Free) - { - var ensuresCopy = (Ensures) cce.NonNull(ens.Clone()); - ensuresCopy.Condition = Substituter.Apply(formalProcImplSubst, ens.Condition); - AssertEnsuresCmd assert = new AssertEnsuresCmd(ensuresCopy) { - ErrorDataEnhanced = ensuresCopy.ErrorDataEnhanced - }; - // Copy any {:id ...} from the postcondition to the assumption, so - // we can track it while analyzing verification coverage. - assert.CopyIdFrom(ens.tok, ens); - unifiedExitBlock.Cmds.Add(assert); - if (debugWriter != null) - { - assert.Emit(debugWriter, 1); - } - } - else if (ens.CanAlwaysAssume()) - { - Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); - unifiedExitBlock.Cmds.Add(new AssumeCmd(ens.tok, e)); - } - else + var ensuresCopy = (Ensures) cce.NonNull(ens.Clone()); + ensuresCopy.Condition = Substituter.Apply(formalProcImplSubst, ens.Condition); + AssertEnsuresCmd assert = new AssertEnsuresCmd(ensuresCopy) { + ErrorDataEnhanced = ensuresCopy.ErrorDataEnhanced + }; + // Copy any {:id ...} from the postcondition to the assumption, so + // we can track it while analyzing verification coverage. + assert.CopyIdFrom(ens.tok, ens); + unifiedExitBlock.Cmds.Add(assert); + if (debugWriter != null) { - // skip free ensures if it doesn't have the :always_assume attr + assert.Emit(debugWriter, 1); } } - - if (debugWriter != null) + else if (ens.CanAlwaysAssume()) { - debugWriter.WriteLine(); + Expr e = Substituter.Apply(formalProcImplSubst, ens.Condition); + unifiedExitBlock.Cmds.Add(new AssumeCmd(ens.tok, e)); } + else + { + // skip free ensures if it doesn't have the :always_assume attr + } + } + + if (debugWriter != null) + { + debugWriter.WriteLine(); } + } +} + +class GotoFromReturn : TokenWrapper { + public ReturnCmd Origin { get; } + + public GotoFromReturn(ReturnCmd origin) : base(origin.tok) { + this.Origin = origin; + } } \ No newline at end of file diff --git a/Source/VCGeneration/Transformations/RemoveBackEdges.cs b/Source/VCGeneration/Transformations/RemoveBackEdges.cs index d8a43e66b..f9233af03 100644 --- a/Source/VCGeneration/Transformations/RemoveBackEdges.cs +++ b/Source/VCGeneration/Transformations/RemoveBackEdges.cs @@ -9,7 +9,7 @@ namespace VCGeneration.Transformations; public class RemoveBackEdges { - private VerificationConditionGenerator generator; + private readonly VerificationConditionGenerator generator; public RemoveBackEdges(VerificationConditionGenerator generator) { this.generator = generator; @@ -206,14 +206,7 @@ private void ConvertCfg2DagStandard(Implementation impl, Dictionary labels /*!*/ /*!*/, Model model, #region Map passive program errors back to original program errors - ReturnCounterexample returnExample = newCounterexample as ReturnCounterexample; - if (returnExample != null) + if (newCounterexample is ReturnCounterexample returnExample) { - foreach (Block b in returnExample.Trace) + foreach (var b in returnExample.Trace) { Contract.Assert(b != null); Contract.Assume(b.TransferCmd != null); - ReturnCmd cmd = gotoCmdOrigins.ContainsKey(b.TransferCmd) ? gotoCmdOrigins[b.TransferCmd] : null; - if (cmd != null) - { - returnExample.FailingReturn = cmd; - break; + if (b.TransferCmd.tok is GotoFromReturn gotoFromReturn) { + returnExample.FailingReturn = gotoFromReturn.Origin; } } } diff --git a/Test/aitest0/Issue25.bpl.expect b/Test/aitest0/Issue25.bpl.expect index de1be84d4..650e46069 100644 --- a/Test/aitest0/Issue25.bpl.expect +++ b/Test/aitest0/Issue25.bpl.expect @@ -1,8 +1,8 @@ -Issue25.bpl(12,1): Error: a postcondition could not be proved on this return path +Issue25.bpl(12,3): Error: a postcondition could not be proved on this return path Issue25.bpl(8,1): Related location: this is the postcondition that could not be proved Execution trace: - Issue25.bpl(11,3): anon0 - Issue25.bpl(12,1): anon2_LoopHead - Issue25.bpl(12,1): anon2_LoopDone + Issue25.bpl(11,5): anon0 + Issue25.bpl(12,3): anon2_LoopHead + Issue25.bpl(12,3): anon2_LoopDone Boogie program verifier finished with 0 verified, 1 error From 679724913f8b7e8e718538c6cfec9efd165c8978 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 15:56:32 +0200 Subject: [PATCH 42/70] Remove occurrences of gotoCmd dictionary --- Source/ExecutionEngine/CommandLineOptions.cs | 2 +- Source/ExecutionEngine/ExecutionEngine.cs | 5 +- Source/ExecutionEngine/VerificationTask.cs | 2 +- Source/Houdini/HoudiniSession.cs | 4 +- Source/VCGeneration/ManualSplit.cs | 3 +- .../Splits/IsolateAttributeOnJumpsHandler.cs | 11 ++-- .../VCGeneration/Splits/ManualSplitFinder.cs | 5 +- Source/VCGeneration/Splits/Split.cs | 14 +---- .../Splits/SplitAndVerifyWorker.cs | 7 ++- .../Splits/SplitAttributeHandler.cs | 2 +- .../Transformations/DesugarReturns.cs | 9 +--- .../VerificationConditionGenerator.cs | 51 +++++-------------- 12 files changed, 37 insertions(+), 78 deletions(-) diff --git a/Source/ExecutionEngine/CommandLineOptions.cs b/Source/ExecutionEngine/CommandLineOptions.cs index 171797990..764ea17a4 100644 --- a/Source/ExecutionEngine/CommandLineOptions.cs +++ b/Source/ExecutionEngine/CommandLineOptions.cs @@ -105,7 +105,7 @@ static void PassifyAllImplementations(ExecutionEngineOptions options, ProcessedP foreach(var implementation in processedProgram.Program.Implementations) { vcGenerator.PrepareImplementation(new ImplementationRun(implementation, options.OutputWriter), - callback, out _, out _, out _); + callback, out _, out _); } } diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index e75bca69d..7cc6abc0c 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -740,12 +740,11 @@ public async Task> GetVerificationTasks(Program var run = new ImplementationRun(implementation, writer); var collector = new VerificationResultCollector(Options); vcGenerator.PrepareImplementation(run, collector, out _, - out var gotoCmdOrigins, out var modelViewInfo); ConditionGeneration.ResetPredecessors(run.Implementation.Blocks); - var splits = ManualSplitFinder.GetParts(Options, run, gotoCmdOrigins, - (token, blocks) => new ManualSplit(Options, () => blocks, gotoCmdOrigins, vcGenerator, run, token)).ToList(); + var splits = ManualSplitFinder.GetParts(Options, run, + (token, blocks) => new ManualSplit(Options, () => blocks, vcGenerator, run, token)).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 6b0fb5f3e..3571dac41 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.parent, Split.Run, Split.Origin, newSeed); split.SplitIndex = Split.SplitIndex; return new VerificationTask(engine, ProcessedProgram, split, modelViewInfo); diff --git a/Source/Houdini/HoudiniSession.cs b/Source/Houdini/HoudiniSession.cs index ee24e7bfa..a787a8a65 100644 --- a/Source/Houdini/HoudiniSession.cs +++ b/Source/Houdini/HoudiniSession.cs @@ -168,7 +168,7 @@ public HoudiniSession(Houdini houdini, VerificationConditionGenerator vcgen, Pro collector.OnProgress?.Invoke("HdnVCGen", 0, 0, 0.0); new RemoveBackEdges(vcgen).ConvertCfg2Dag(run, taskID: taskId); - var gotoCmdOrigins = vcgen.PassifyImpl(run, out var mvInfo); + vcgen.PassifyImpl(run, out var mvInfo); ExistentialConstantCollector.CollectHoudiniConstants(houdini, impl, out var ecollector); this.houdiniAssertConstants = ecollector.houdiniAssertConstants; @@ -196,7 +196,7 @@ public HoudiniSession(Houdini houdini, VerificationConditionGenerator vcgen, Pro new Formal(Token.NoToken, new TypedIdent(Token.NoToken, "", Type.Bool), false)); proverInterface.DefineMacro(macro, conjecture); conjecture = exprGen.Function(macro); - handler = new VerificationConditionGenerator.ErrorReporter(this.houdini.Options, gotoCmdOrigins, absyIds, impl.Blocks, impl.debugInfos, collector, + handler = new VerificationConditionGenerator.ErrorReporter(this.houdini.Options, absyIds, impl.Blocks, impl.debugInfos, collector, mvInfo, proverInterface.Context, program, this); } diff --git a/Source/VCGeneration/ManualSplit.cs b/Source/VCGeneration/ManualSplit.cs index b2cf6482f..701732e10 100644 --- a/Source/VCGeneration/ManualSplit.cs +++ b/Source/VCGeneration/ManualSplit.cs @@ -12,11 +12,10 @@ public class ManualSplit : Split public ManualSplit(VCGenOptions options, Func> blocks, - Dictionary gotoCmdOrigins, VerificationConditionGenerator par, ImplementationRun run, ImplementationPartOrigin origin, int? randomSeed = null) - : base(options, blocks, gotoCmdOrigins, par, run, randomSeed) + : base(options, blocks, par, run, randomSeed) { Origin = origin; } diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs index bf13a5f81..44e73b9c0 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs @@ -7,6 +7,7 @@ using Microsoft.Boogie; using VC; using VCGeneration.Splits; +using VCGeneration.Transformations; namespace VCGeneration; @@ -17,8 +18,7 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { this.rewriter = rewriter; } - public (List Isolated, ManualSplit Remainder) GetParts( - Dictionary gotoToOriginalReturn, + public (List Isolated, ManualSplit Remainder) GetParts( ManualSplit partToDivide) { var results = new List(); @@ -37,7 +37,7 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { continue; } - var isTypeOfAssert = gotoToOriginalReturn.ContainsKey(gotoCmd); + var isTypeOfAssert = gotoCmd.tok is GotoFromReturn; var isolateAttribute = QKeyValue.FindAttribute(gotoCmd.Attributes, p => p.Key == "isolate"); var isolate = ShouldIsolate(isTypeOfAssert && splitOnEveryAssert, isolateAttribute); if (!isolate) { @@ -49,15 +49,16 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { var descendants = dag.ComputeReachability(block, true); var blocksToInclude = ancestors.Union(descendants).ToHashSet(); + var originalReturn = ((GotoFromReturn)gotoCmd.tok).Origin; if (isolateAttribute != null && isolateAttribute.Params.OfType().Any(p => Equals(p, "paths"))) { // These conditions hold if the goto was originally a return Debug.Assert(gotoCmd.LabelTargets.Count == 1); Debug.Assert(gotoCmd.LabelTargets[0].TransferCmd is not GotoCmd); - results.AddRange(rewriter.GetSplitsForIsolatedPaths(gotoCmd.LabelTargets[0], blocksToInclude, gotoToOriginalReturn[gotoCmd].tok)); + results.AddRange(rewriter.GetSplitsForIsolatedPaths(gotoCmd.LabelTargets[0], blocksToInclude, originalReturn.tok)); } else { var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, reversedBlocks, ancestors.ToHashSet()); - results.Add(rewriter.CreateSplit(new ReturnOrigin(gotoToOriginalReturn[gotoCmd]), newBlocks)); + results.Add(rewriter.CreateSplit(new ReturnOrigin(originalReturn), newBlocks)); } } diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 2843b660d..af8e05be1 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -12,15 +12,14 @@ namespace VCGeneration; public static class ManualSplitFinder { - public static IEnumerable GetParts(VCGenOptions options, ImplementationRun run, - Dictionary gotoToOriginalReturn, + public static IEnumerable GetParts(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createPart) { var blockRewriter = new BlockRewriter(options, run.Implementation.Blocks, createPart); var focussedParts = new FocusAttributeHandler(blockRewriter).GetParts(run); return focussedParts.SelectMany(focussedPart => { var (isolatedJumps, withoutIsolatedJumps) = - new IsolateAttributeOnJumpsHandler(blockRewriter).GetParts(gotoToOriginalReturn, focussedPart); + new IsolateAttributeOnJumpsHandler(blockRewriter).GetParts(focussedPart); var (isolatedAssertions, withoutIsolatedAssertions) = new IsolateAttributeOnAssertsHandler(new BlockRewriter(options, withoutIsolatedJumps.Blocks, createPart)).GetParts(withoutIsolatedJumps); diff --git a/Source/VCGeneration/Splits/Split.cs b/Source/VCGeneration/Splits/Split.cs index ecc88fcc7..f9c7292e7 100644 --- a/Source/VCGeneration/Splits/Split.cs +++ b/Source/VCGeneration/Splits/Split.cs @@ -57,8 +57,6 @@ public IReadOnlyList PrunedDeclarations { int assertionCount; double assertionCost; // without multiplication by paths - public Dictionary GotoCmdOrigins { get; } - public readonly VerificationConditionGenerator /*!*/ parent; @@ -78,13 +76,10 @@ public readonly VerificationConditionGenerator /*!*/ public VerificationConditionGenerator.ErrorReporter reporter; public Split(VCGenOptions options, Func> /*!*/ getBlocks, - Dictionary /*!*/ gotoCmdOrigins, VerificationConditionGenerator /*!*/ par, ImplementationRun run, int? randomSeed = null) { - Contract.Requires(gotoCmdOrigins != null); Contract.Requires(par != null); this.getBlocks = getBlocks; - this.GotoCmdOrigins = gotoCmdOrigins; parent = par; this.Run = run; this.Options = options; @@ -667,7 +662,6 @@ private Split DoSplit() copies.Clear(); CloneBlock(Blocks[0]); var newBlocks = new List(); - var newGotoCmdOrigins = new Dictionary(); foreach (var block in Blocks) { Contract.Assert(block != null); @@ -677,10 +671,6 @@ private Split DoSplit() } newBlocks.Add(cce.NonNull(tmp)); - if (GotoCmdOrigins.TryGetValue(block.TransferCmd, out var origin)) - { - newGotoCmdOrigins[tmp.TransferCmd] = origin; - } foreach (var predecessor in block.Predecessors) { @@ -692,7 +682,7 @@ private Split DoSplit() } } - return new Split(Options, () => newBlocks, newGotoCmdOrigins, parent, Run); + return new Split(Options, () => newBlocks, parent, Run); } private Split SplitAt(int idx) @@ -962,7 +952,7 @@ public async Task BeginCheck(TextWriter traceWriter, Checker checker, VerifierCa VCExpr eqExpr = exprGen.Eq(controlFlowFunctionAppl, exprGen.Integer(BigNum.FromInt(absyIds.GetId(Implementation.Blocks[0])))); vc = exprGen.Implies(eqExpr, vc); - reporter = new VerificationConditionGenerator.ErrorReporter(Options, GotoCmdOrigins, absyIds, + reporter = new VerificationConditionGenerator.ErrorReporter(Options, absyIds, Implementation.Blocks, Implementation.debugInfos, callback, mvInfo, checker.TheoremProver.Context, parent.program, this); } diff --git a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs index ca477fe2c..a5d262108 100644 --- a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs @@ -39,8 +39,7 @@ public SplitAndVerifyWorker( Program program, VCGenOptions options, VerificationConditionGenerator verificationConditionGenerator, - ImplementationRun run, - Dictionary gotoCmdOrigins, + ImplementationRun run, VerifierCallback callback, ModelViewInfo mvInfo, VcOutcome vcOutcome) @@ -66,8 +65,8 @@ public SplitAndVerifyWorker( ResetPredecessors(Implementation.Blocks); - ManualSplits = ManualSplitFinder.GetParts(options, run, gotoCmdOrigins, - (token, blocks) => new ManualSplit(options, () => blocks, gotoCmdOrigins, verificationConditionGenerator, run, token)).ToList(); + ManualSplits = ManualSplitFinder.GetParts(options, run, + (token, blocks) => new ManualSplit(options, () => blocks, verificationConditionGenerator, run, token)).ToList(); if (ManualSplits.Count == 1 && maxSplits > 1) { diff --git a/Source/VCGeneration/Splits/SplitAttributeHandler.cs b/Source/VCGeneration/Splits/SplitAttributeHandler.cs index 34a1fa37e..12644a937 100644 --- a/Source/VCGeneration/Splits/SplitAttributeHandler.cs +++ b/Source/VCGeneration/Splits/SplitAttributeHandler.cs @@ -71,7 +71,7 @@ ManualSplit CreateVc(ImplementationPartOrigin token, List blocks) { return new ManualSplit(partToSplit.Options, () => { BlockTransformations.Optimize(blocks); return blocks; - }, partToSplit.GotoCmdOrigins, partToSplit.parent, partToSplit.Run, token); + }, partToSplit.parent, partToSplit.Run, token); } } diff --git a/Source/VCGeneration/Transformations/DesugarReturns.cs b/Source/VCGeneration/Transformations/DesugarReturns.cs index d4e2fc4d2..90ab89089 100644 --- a/Source/VCGeneration/Transformations/DesugarReturns.cs +++ b/Source/VCGeneration/Transformations/DesugarReturns.cs @@ -8,13 +8,11 @@ namespace VCGeneration.Transformations; public static class DesugarReturns { - public static Block GenerateUnifiedExit(Implementation impl, out Dictionary gotoCmdOrigins) + public static Block GenerateUnifiedExit(Implementation impl) { Contract.Requires(impl != null); - Contract.Requires(gotoCmdOrigins != null); Contract.Ensures(Contract.Result() != null); - gotoCmdOrigins = new(); Contract.Ensures(Contract.Result().TransferCmd is ReturnCmd); Block exitBlock = null; @@ -41,7 +39,6 @@ public static Block GenerateUnifiedExit(Implementation impl, out Dictionary - public static void InjectPostConditions(VCGenOptions options, ImplementationRun run, Block unifiedExitBlock, - Dictionary gotoCmdOrigins) + public static void InjectPostConditions(VCGenOptions options, ImplementationRun run, Block unifiedExitBlock) { var impl = run.Implementation; Contract.Requires(impl != null); Contract.Requires(unifiedExitBlock != null); - Contract.Requires(gotoCmdOrigins != null); Contract.Requires(impl.Proc != null); Contract.Requires(unifiedExitBlock.TransferCmd is ReturnCmd); diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index 4bf939fea..315a6b047 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -25,7 +25,6 @@ record ImplementationTransformationData public bool Passified { get; set; } = false; public bool ConvertedToDAG { get; set; } = false; - public Dictionary GotoCmdOrigins { get; set; } public ModelViewInfo ModelViewInfo { get; set; } } @@ -114,7 +113,7 @@ public VCExpr CodeExprToVerificationCondition(CodeExpr codeExpr, List gotoCmdOrigins = vcgen.ConvertBlocks2PassiveCmd(traceWriter, codeExpr.Blocks, + vcgen.ConvertBlocks2PassiveCmd(traceWriter, codeExpr.Blocks, new List(), new ModelViewInfo(codeExpr), debugInfos); VCExpr startCorrect = vcgen.LetVC(codeExpr.Blocks, null, absyIds, ctx, out var ac, isPositiveContext); VCExpr vce = ctx.ExprGen.Let(bindings, startCorrect); @@ -380,7 +379,7 @@ public override async Task VerifyImplementation(ImplementationRun run callback.OnProgress?.Invoke("VCgen", 0, 0, 0.0); - PrepareImplementation(run, callback, out var smokeTester, out var dataGotoCmdOrigins, out var dataModelViewInfo); + PrepareImplementation(run, callback, out var smokeTester, out var dataModelViewInfo); VcOutcome vcOutcome = VcOutcome.Correct; @@ -400,15 +399,14 @@ public override async Task VerifyImplementation(ImplementationRun run else { // If possible, we use the old counterexample, but with the location information of "a" - var cex = AssertCmdToCloneCounterexample(CheckerPool.Options, a, oldCex, impl.Blocks[0], - dataGotoCmdOrigins); + var cex = AssertCmdToCloneCounterexample(CheckerPool.Options, a, oldCex, impl.Blocks[0]); callback.OnCounterexample(cex, null); } } } } - var worker = new SplitAndVerifyWorker(program, Options, this, run, dataGotoCmdOrigins, callback, + var worker = new SplitAndVerifyWorker(program, Options, this, run, callback, dataModelViewInfo, vcOutcome); vcOutcome = await worker.WorkUntilDone(cancellationToken); @@ -427,7 +425,6 @@ public override async Task VerifyImplementation(ImplementationRun run public void PrepareImplementation(ImplementationRun run, VerifierCallback callback, out SmokeTester smokeTester, - out Dictionary gotoCmdOrigins, out ModelViewInfo modelViewInfo) { var data = implementationData.GetOrCreateValue(run.Implementation)!; @@ -447,7 +444,7 @@ public void PrepareImplementation(ImplementationRun run, VerifierCallback callba if (!data.Passified) { data.Passified = true; - data.GotoCmdOrigins = gotoCmdOrigins = PassifyImpl(run, out modelViewInfo); + PassifyImpl(run, out modelViewInfo); data.ModelViewInfo = modelViewInfo; ExpandAsserts(run.Implementation); @@ -466,7 +463,6 @@ public void PrepareImplementation(ImplementationRun run, VerifierCallback callba else { modelViewInfo = data.ModelViewInfo; - gotoCmdOrigins = data.GotoCmdOrigins; } } @@ -475,7 +471,6 @@ public class ErrorReporter : ProverInterface.ErrorHandler { private ProofRun split; private new VCGenOptions options; - Dictionary gotoCmdOrigins; ControlFlowIdMap absyIds; @@ -491,7 +486,6 @@ public class ErrorReporter : ProverInterface.ErrorHandler [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(gotoCmdOrigins != null); Contract.Invariant(absyIds != null); Contract.Invariant(cce.NonNullElements(blocks)); Contract.Invariant(callback != null); @@ -510,7 +504,6 @@ public override void AddCoveredElement(TrackedNodeComponent elt) } public ErrorReporter(VCGenOptions options, - Dictionary /*!*/ gotoCmdOrigins, ControlFlowIdMap /*!*/ absyIds, List /*!*/ blocks, Dictionary> debugInfos, @@ -519,13 +512,11 @@ public ErrorReporter(VCGenOptions options, ProverContext /*!*/ context, Program /*!*/ program, ProofRun split) : base(options) { - Contract.Requires(gotoCmdOrigins != null); Contract.Requires(absyIds != null); Contract.Requires(cce.NonNullElements(blocks)); Contract.Requires(callback != null); Contract.Requires(context != null); Contract.Requires(program != null); - this.gotoCmdOrigins = gotoCmdOrigins; this.absyIds = absyIds; this.blocks = blocks; this.debugInfos = debugInfos; @@ -657,14 +648,14 @@ public void DesugarCalls(Implementation impl) } } - public Dictionary PassifyImpl(ImplementationRun run, out ModelViewInfo modelViewInfo) + public void PassifyImpl(ImplementationRun run, out ModelViewInfo modelViewInfo) { Contract.Requires(run != null); Contract.Requires(program != null); Contract.Ensures(Contract.Result>() != null); var impl = run.Implementation; - var exitBlock = DesugarReturns.GenerateUnifiedExit(impl, out var gotoCmdOrigins); + var exitBlock = DesugarReturns.GenerateUnifiedExit(impl); #region Debug Tracing @@ -703,7 +694,7 @@ public Dictionary PassifyImpl(ImplementationRun run, out if (lvar.TypedIdent.WhereExpr != null) { var exp = Expr.Binary(lvar.tok, BinaryOperator.Opcode.And, lvar.TypedIdent.WhereExpr, - LiteralExpr.Literal(true)); + Expr.Literal(true)); Cmd c = new AssumeCmd(lvar.tok, exp, new QKeyValue(lvar.tok, "where", new List(new object[] { idExp }), null)); cc.Add(c); @@ -719,7 +710,7 @@ public Dictionary PassifyImpl(ImplementationRun run, out InjectPreconditions(Options, run, cc); // append postconditions, starting in exitBlock and continuing into other blocks, if needed - DesugarReturns.InjectPostConditions(Options, run, exitBlock, gotoCmdOrigins); + DesugarReturns.InjectPostConditions(Options, run, exitBlock); } #endregion @@ -793,19 +784,6 @@ public Dictionary PassifyImpl(ImplementationRun run, out #endregion Peep-hole optimizations HandleSelectiveChecking(impl); - - -// #region Constant Folding -// #endregion -// #region Debug Tracing -// if (CommandLineOptions.Clo.TraceVerify) -// { -// Console.WriteLine("after constant folding"); -// EmitImpl(impl, true); -// } -// #endregion - - return gotoCmdOrigins; } #region Simplified May-Unverified Analysis and Instrumentation @@ -1375,12 +1353,11 @@ public static Counterexample AssertCmdToCounterexample(VCGenOptions options, Ass /// public static Counterexample AssertCmdToCloneCounterexample(VCGenOptions options, AssertCmd assrt, Counterexample cex, - Block implEntryBlock, Dictionary gotoCmdOrigins) + Block implEntryBlock) { Contract.Requires(assrt != null); Contract.Requires(cex != null); Contract.Requires(implEntryBlock != null); - Contract.Requires(gotoCmdOrigins != null); Contract.Ensures(Contract.Result() != null); Counterexample cc; @@ -1449,11 +1426,11 @@ public static Counterexample AssertCmdToCloneCounterexample(VCGenOptions options if (reconstructedTrace != null) { // The reconstructed trace ends with a "return;" in the passive command, so we now try to convert it to the original (non-passive) "return;" - foreach (Block b in reconstructedTrace) + foreach (var block in reconstructedTrace) { - Contract.Assert(b != null); - Contract.Assume(b.TransferCmd != null); - returnCmd = gotoCmdOrigins.ContainsKey(b.TransferCmd) ? gotoCmdOrigins[b.TransferCmd] : null; + Contract.Assert(block != null); + Contract.Assume(block.TransferCmd != null); + returnCmd = block.TransferCmd.tok is GotoFromReturn gotoFromReturn ? gotoFromReturn.Origin : null; if (returnCmd != null) { break; From d3a9015ea26657f2151421239beb41d03417223c Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 16:08:54 +0200 Subject: [PATCH 43/70] Simple fail now passes --- Source/Core/AST/Block.cs | 6 +- Source/Core/VariableDependenceAnalyser.cs | 22 ++--- Source/VCGeneration/LoopExtractor.cs | 89 +++++++++---------- Source/VCGeneration/Splits/BlockRewriter.cs | 36 +++----- .../Splits/SplitAttributeHandler.cs | 5 +- 5 files changed, 68 insertions(+), 90 deletions(-) diff --git a/Source/Core/AST/Block.cs b/Source/Core/AST/Block.cs index c6ac50740..4cb78df21 100644 --- a/Source/Core/AST/Block.cs +++ b/Source/Core/AST/Block.cs @@ -128,7 +128,11 @@ public bool IsLive(Variable v) return liveVarsBefore.Contains(v); } - public Block(IToken tok) + public static Block ShallowClone(Block block) { + return new Block(block.tok, block.Label, block.Cmds, block.TransferCmd); + } + + public Block(IToken tok, TransferCmd cmd) : this(tok, "", new List(), new ReturnCmd(tok)) { } diff --git a/Source/Core/VariableDependenceAnalyser.cs b/Source/Core/VariableDependenceAnalyser.cs index 105b7811a..09fba1808 100644 --- a/Source/Core/VariableDependenceAnalyser.cs +++ b/Source/Core/VariableDependenceAnalyser.cs @@ -515,18 +515,18 @@ private void MakeIgnoreList() private Dictionary> ComputeGlobalControlDependences() { - Dictionary> GlobalCtrlDep = new Dictionary>(); - Dictionary>> LocalCtrlDeps = + var globalCtrlDep = new Dictionary>(); + var localCtrlDeps = new Dictionary>>(); // Work out and union together local control dependences - foreach (var Impl in prog.NonInlinedImplementations()) + foreach (var impl in prog.NonInlinedImplementations()) { - Graph blockGraph = prog.ProcessLoops(options, Impl); - LocalCtrlDeps[Impl] = blockGraph.ControlDependence(new Block(prog.tok)); - foreach (var KeyValue in LocalCtrlDeps[Impl]) + var blockGraph = prog.ProcessLoops(options, impl); + localCtrlDeps[impl] = blockGraph.ControlDependence(new Block(prog.tok, new ReturnCmd(prog.tok))); + foreach (var keyValue in localCtrlDeps[impl]) { - GlobalCtrlDep.Add(KeyValue.Key, KeyValue.Value); + globalCtrlDep.Add(keyValue.Key, keyValue.Value); } } @@ -543,11 +543,11 @@ private Dictionary> ComputeGlobalControlDependences() if (DirectCallee != null) { HashSet IndirectCallees = ComputeIndirectCallees(callGraph, DirectCallee); - foreach (var control in GetControllingBlocks(b, LocalCtrlDeps[Impl])) + foreach (var control in GetControllingBlocks(b, localCtrlDeps[Impl])) { foreach (var c in IndirectCallees.Select(Item => Item.Blocks).SelectMany(Item => Item)) { - GlobalCtrlDep[control].Add(c); + globalCtrlDep[control].Add(c); } } } @@ -556,13 +556,13 @@ private Dictionary> ComputeGlobalControlDependences() } // Compute transitive closure - GlobalCtrlDep.TransitiveClosure(); + globalCtrlDep.TransitiveClosure(); // Finally reverse the dependences Dictionary> result = new Dictionary>(); - foreach (var KeyValue in GlobalCtrlDep) + foreach (var KeyValue in globalCtrlDep) { foreach (var v in KeyValue.Value) { diff --git a/Source/VCGeneration/LoopExtractor.cs b/Source/VCGeneration/LoopExtractor.cs index 0aa72bf9d..e0f40524b 100644 --- a/Source/VCGeneration/LoopExtractor.cs +++ b/Source/VCGeneration/LoopExtractor.cs @@ -313,10 +313,7 @@ static void CreateProceduresForLoops(CoreOptions options, Implementation impl, G continue; } - Block newBlock = new Block(block.tok) - { - Label = block.Label - }; + var newBlock = Block.ShallowClone(block); if (headRecursion && block == header) { CallCmd callCmd = (CallCmd) (loopHeaderToCallCmd2[header]).Clone(); @@ -332,14 +329,10 @@ static void CreateProceduresForLoops(CoreOptions options, Implementation impl, G } blockMap[block] = newBlock; - if (newBlocksCreated.ContainsKey(block)) + if (newBlocksCreated.TryGetValue(block, out var original)) { - var original = newBlocksCreated[block]; - var newBlock2 = new Block(original.tok) - { - Label = original.Label, - Cmds = Substituter.Apply(subst, original.Cmds) - }; + var newBlock2 = Block.ShallowClone(original); + newBlock2.Cmds = Substituter.Apply(subst, original.Cmds); blockMap[original] = newBlock2; } @@ -351,46 +344,44 @@ static void CreateProceduresForLoops(CoreOptions options, Implementation impl, G auxGotoCmd.LabelTargets != null && auxGotoCmd.LabelTargets.Count >= 1); //BUGFIX on 10/26/15: this contains nodes present in NaturalLoops for a different backedgenode var loopNodes = GetBlocksInAllNaturalLoops(options, header, g); //var loopNodes = g.NaturalLoops(header, source); - foreach (var bl in auxGotoCmd.LabelTargets) - { - if (g.Nodes.Contains(bl) && //newly created blocks are not present in NaturalLoop(header, xx, g) - !loopNodes.Contains(bl)) + foreach (var bl in auxGotoCmd.LabelTargets) { + if (!g.Nodes.Contains(bl) || // newly created blocks are not present in NaturalLoop(header, xx, g) + loopNodes.Contains(bl)) { + continue; + } + + var auxNewBlock = Block.ShallowClone(bl); + // these blocks may have read/write locals that are not present in naturalLoops + // we need to capture these variables + auxNewBlock.Cmds = Substituter.Apply(subst, bl.Cmds); + + // add restoration code for such blocks + if (loopHeaderToAssignCmd.TryGetValue(header, out var assignCmd)) + { + auxNewBlock.Cmds.Add(assignCmd); + } + + List lhsg = new List(); + List /*!*/ + globalsMods = loopHeaderToLoopProc[header].Modifies; + foreach (IdentifierExpr gl in globalsMods) { - var auxNewBlock = new Block(bl.tok) - { - Label = bl.Label, - //these blocks may have read/write locals that are not present in naturalLoops - //we need to capture these variables - Cmds = Substituter.Apply(subst, bl.Cmds) - }; - //add restoration code for such blocks - if (loopHeaderToAssignCmd.TryGetValue(header, out var assignCmd)) - { - auxNewBlock.Cmds.Add(assignCmd); - } - - List lhsg = new List(); - List /*!*/ - globalsMods = loopHeaderToLoopProc[header].Modifies; - foreach (IdentifierExpr gl in globalsMods) - { - lhsg.Add(new SimpleAssignLhs(Token.NoToken, gl)); - } - - List rhsg = new List(); - foreach (IdentifierExpr gl in globalsMods) - { - rhsg.Add(new OldExpr(Token.NoToken, gl)); - } - - if (lhsg.Count != 0) - { - AssignCmd globalAssignCmd = new AssignCmd(Token.NoToken, lhsg, rhsg); - auxNewBlock.Cmds.Add(globalAssignCmd); - } - - blockMap[(Block) bl] = auxNewBlock; + lhsg.Add(new SimpleAssignLhs(Token.NoToken, gl)); } + + List rhsg = new List(); + foreach (IdentifierExpr gl in globalsMods) + { + rhsg.Add(new OldExpr(Token.NoToken, gl)); + } + + if (lhsg.Count != 0) + { + AssignCmd globalAssignCmd = new AssignCmd(Token.NoToken, lhsg, rhsg); + auxNewBlock.Cmds.Add(globalAssignCmd); + } + + blockMap[bl] = auxNewBlock; } } } diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index 15c79c4ba..1a0e990fa 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -32,13 +32,8 @@ public Cmd TransformAssertCmd(Cmd cmd) { public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IReadOnlySet? blocksToInclude, IToken origin) { var blockToVisit = new Stack>(); var newToOldBlocks = new Dictionary(); - var newLastBlock = new Block(lastBlock.tok) - { - Predecessors = lastBlock.Predecessors, - Label = lastBlock.Label, - TransferCmd = new ReturnCmd(Token.NoToken), - Cmds = lastBlock.Cmds - }; + var newLastBlock = Block.ShallowClone(lastBlock); + newLastBlock.Predecessors = lastBlock.Predecessors; blockToVisit.Push(ImmutableStack.Create(newLastBlock)); newToOldBlocks[newLastBlock] = lastBlock; @@ -53,13 +48,10 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead var hadPredecessors = false; foreach (var oldPrevious in predecessors) { hadPredecessors = true; - var newPrevious = new Block(oldPrevious.tok) - { - Predecessors = oldPrevious.Predecessors, - Label = oldPrevious.Label, - TransferCmd = oldPrevious.TransferCmd, - Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList() - }; + var newPrevious = Block.ShallowClone(oldPrevious); + newPrevious.Predecessors = oldPrevious.Predecessors; + newPrevious.Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList(); + newToOldBlocks[newPrevious] = oldPrevious; if (newPrevious.TransferCmd is GotoCmd gotoCmd) { newPrevious.TransferCmd = @@ -75,10 +67,9 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead var filteredDag = blocksToInclude == null ? dag : Program.GraphFromBlocksSubset(newToOldBlocks[path.Peek()], blocksToInclude); var nonDominatedBranches = path.Where(b => !filteredDag.DominatorMap.DominatedBy(lastBlock, newToOldBlocks[b])).ToList(); - yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), new List { new(firstBlock.tok) { - Label = firstBlock.Label, - Cmds = path.SelectMany(b => b.Cmds).ToList() - } }); + var newFirstBlock = Block.ShallowClone(firstBlock); + newFirstBlock.Cmds = path.SelectMany(b => b.Cmds).ToList(); + yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), new List { newFirstBlock }); } } } @@ -97,9 +88,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead continue; } - var newBlock = new Block(block.tok) { - Label = block.Label - }; + var newBlock = Block.ShallowClone(block); newBlocks.Add(newBlock); oldToNewBlockMap[block] = newBlock; // freeBlocks consist of the predecessors of the relevant foci. @@ -133,10 +122,7 @@ public static OrderedDictionary UpdateBlocks(Stack blocksTo continue; } - var newBlock = new Block(oldBlock.tok) - { - Label = oldBlock.Label - }; + var newBlock = Block.ShallowClone(oldBlock); oldToNewBlockMap.Add(oldBlock, newBlock); newBlock.Cmds = getCommands(oldBlock); foreach (var previous in oldBlock.Predecessors) { diff --git a/Source/VCGeneration/Splits/SplitAttributeHandler.cs b/Source/VCGeneration/Splits/SplitAttributeHandler.cs index 12644a937..0779bcef0 100644 --- a/Source/VCGeneration/Splits/SplitAttributeHandler.cs +++ b/Source/VCGeneration/Splits/SplitAttributeHandler.cs @@ -181,10 +181,7 @@ private static List UpdateBlocks(IReadOnlyList blocks, var newBlocks = new List(blocks.Count); var oldToNewBlockMap = new Dictionary(newBlocks.Count); foreach (var currentBlock in blocks) { - var newBlock = new Block(currentBlock.tok) - { - Label = currentBlock.Label - }; + var newBlock = Block.ShallowClone(currentBlock); oldToNewBlockMap[currentBlock] = newBlock; newBlocks.Add(newBlock); From c1db5a29940e41af685ec6d5c68c73ca837161c9 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 16:38:24 +0200 Subject: [PATCH 44/70] Update test files --- Source/VCGeneration/Splits/Split.cs | 10 ++++---- .../intro-nonblocking.bpl.expect | 1 - .../regression-tests/left-mover.bpl.expect | 1 - .../non-interference-1.bpl.expect | 1 - .../non-interference-2.bpl.expect | 2 -- .../pa-noninterference-check.bpl.expect | 1 - .../regression-tests/parallel1.bpl.expect | 1 - .../regression-tests/parallel6.bpl.expect | 1 - .../pending-async-1.bpl.expect | 1 - ...ding-async-noninterference-fail.bpl.expect | 1 - .../regression-tests/refinement.bpl.expect | 5 ---- .../regression-tests/refinement2.bpl.expect | 4 ---- .../yield-invariant-fails.bpl.expect | 1 - Test/commandline/SplitOnEveryAssert.bpl | 24 +++++++++---------- Test/test0/SplitOnEveryAssert.bpl | 24 +++++++++---------- Test/test2/Structured.bpl.expect | 1 - 16 files changed, 29 insertions(+), 50 deletions(-) diff --git a/Source/VCGeneration/Splits/Split.cs b/Source/VCGeneration/Splits/Split.cs index f9c7292e7..9930e6ecc 100644 --- a/Source/VCGeneration/Splits/Split.cs +++ b/Source/VCGeneration/Splits/Split.cs @@ -484,7 +484,7 @@ void ComputeBlockSetsHelper(Block b, bool allowSmall) } } - void ComputeBlockSets(bool allowSmall) + private void ComputeBlockSets(bool allowSmall) { protectedFromAssertToAssume.Clear(); keepAtAll.Clear(); @@ -494,12 +494,12 @@ void ComputeBlockSets(bool allowSmall) if (assertToAssume) { - foreach (Block b in allowSmall ? Blocks : bigBlocks) + foreach (var block in allowSmall ? Blocks : bigBlocks) { - Contract.Assert(b != null); - if (ComputeReachableNodes(b).Contains(cce.NonNull(splitBlock))) + Contract.Assert(block != null); + if (ComputeReachableNodes(block).Contains(cce.NonNull(splitBlock))) { - keepAtAll.Add(b); + keepAtAll.Add(block); } } diff --git a/Test/civl/regression-tests/intro-nonblocking.bpl.expect b/Test/civl/regression-tests/intro-nonblocking.bpl.expect index 1a747e1d9..08c401b27 100644 --- a/Test/civl/regression-tests/intro-nonblocking.bpl.expect +++ b/Test/civl/regression-tests/intro-nonblocking.bpl.expect @@ -1,5 +1,4 @@ intro-nonblocking.bpl(4,13): Error: Cooperation check for intro failed Execution trace: - (0,0): init Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/civl/regression-tests/left-mover.bpl.expect b/Test/civl/regression-tests/left-mover.bpl.expect index 44ada71cb..383a2cd10 100644 --- a/Test/civl/regression-tests/left-mover.bpl.expect +++ b/Test/civl/regression-tests/left-mover.bpl.expect @@ -10,6 +10,5 @@ Execution trace: left-mover.bpl(6,24): inline$inc$0$Return left-mover.bpl(26,24): Error: Cooperation check for block failed Execution trace: - (0,0): init Boogie program verifier finished with 3 verified, 3 errors diff --git a/Test/civl/regression-tests/non-interference-1.bpl.expect b/Test/civl/regression-tests/non-interference-1.bpl.expect index 9ed2f19b1..be24ba16a 100644 --- a/Test/civl/regression-tests/non-interference-1.bpl.expect +++ b/Test/civl/regression-tests/non-interference-1.bpl.expect @@ -2,6 +2,5 @@ non-interference-1.bpl(25,1): Error: Non-interference check failed Execution trace: non-interference-1.bpl(7,3): anon0 non-interference-1.bpl(12,5): inline$AtomicIncr$0$anon0 - (0,0): inline$Civl_NoninterferenceChecker_yield_Yield$0$L Boogie program verifier finished with 4 verified, 1 error diff --git a/Test/civl/regression-tests/non-interference-2.bpl.expect b/Test/civl/regression-tests/non-interference-2.bpl.expect index c1176c36d..4bbae5629 100644 --- a/Test/civl/regression-tests/non-interference-2.bpl.expect +++ b/Test/civl/regression-tests/non-interference-2.bpl.expect @@ -2,10 +2,8 @@ non-interference-2.bpl(25,1): Error: Non-interference check failed Execution trace: non-interference-2.bpl(7,3): anon0 non-interference-2.bpl(12,5): inline$AtomicIncr$0$anon0 - (0,0): inline$Civl_NoninterferenceChecker_yield_PC$0$L non-interference-2.bpl(25,1): Error: Non-interference check failed Execution trace: non-interference-2.bpl(34,3): anon0 - (0,0): inline$Civl_NoninterferenceChecker_yield_PC$0$L Boogie program verifier finished with 2 verified, 2 errors diff --git a/Test/civl/regression-tests/pa-noninterference-check.bpl.expect b/Test/civl/regression-tests/pa-noninterference-check.bpl.expect index b97bf307f..55715916a 100644 --- a/Test/civl/regression-tests/pa-noninterference-check.bpl.expect +++ b/Test/civl/regression-tests/pa-noninterference-check.bpl.expect @@ -1,6 +1,5 @@ pa-noninterference-check.bpl(26,1): Error: Non-interference check failed Execution trace: - (0,0): init pa-noninterference-check.bpl(19,7): inline$A_Incr$0$anon0 pa-noninterference-check.bpl(16,34): inline$A_Incr$0$Return diff --git a/Test/civl/regression-tests/parallel1.bpl.expect b/Test/civl/regression-tests/parallel1.bpl.expect index 4b52e2854..0bc33ffe1 100644 --- a/Test/civl/regression-tests/parallel1.bpl.expect +++ b/Test/civl/regression-tests/parallel1.bpl.expect @@ -2,6 +2,5 @@ parallel1.bpl(25,1): Error: Non-interference check failed Execution trace: parallel1.bpl(7,3): anon0 parallel1.bpl(12,5): inline$AtomicIncr$0$anon0 - (0,0): inline$Civl_NoninterferenceChecker_yield_Yield$0$L Boogie program verifier finished with 3 verified, 1 error diff --git a/Test/civl/regression-tests/parallel6.bpl.expect b/Test/civl/regression-tests/parallel6.bpl.expect index 0ff7beb24..9a6a6e2be 100644 --- a/Test/civl/regression-tests/parallel6.bpl.expect +++ b/Test/civl/regression-tests/parallel6.bpl.expect @@ -7,6 +7,5 @@ Execution trace: parallel6.bpl(11,5): anon0_0 parallel6.bpl(30,7): inline$atomic_incr$2$anon0 parallel6.bpl(30,7): inline$atomic_incr$3$anon0 - (0,0): Civl_ReturnChecker Boogie program verifier finished with 7 verified, 1 error diff --git a/Test/civl/regression-tests/pending-async-1.bpl.expect b/Test/civl/regression-tests/pending-async-1.bpl.expect index 86c6fbf94..5e0a35e06 100644 --- a/Test/civl/regression-tests/pending-async-1.bpl.expect +++ b/Test/civl/regression-tests/pending-async-1.bpl.expect @@ -5,7 +5,6 @@ Execution trace: pending-async-1.bpl(51,3): anon0$1 pending-async-1.bpl(11,9): inline$B$1$anon0 pending-async-1.bpl(51,3): anon0$2 - (0,0): Civl_ReturnChecker pending-async-1.bpl(67,3): Error: Pending asyncs to action A created by this call are not summarized Execution trace: pending-async-1.bpl(67,3): anon0 diff --git a/Test/civl/regression-tests/pending-async-noninterference-fail.bpl.expect b/Test/civl/regression-tests/pending-async-noninterference-fail.bpl.expect index 29a3c680e..d6951c016 100644 --- a/Test/civl/regression-tests/pending-async-noninterference-fail.bpl.expect +++ b/Test/civl/regression-tests/pending-async-noninterference-fail.bpl.expect @@ -1,6 +1,5 @@ pending-async-noninterference-fail.bpl(7,1): Error: Non-interference check failed Execution trace: - (0,0): init pending-async-noninterference-fail.bpl(12,5): inline$A$0$anon0 pending-async-noninterference-fail.bpl(9,32): inline$A$0$Return diff --git a/Test/civl/regression-tests/refinement.bpl.expect b/Test/civl/regression-tests/refinement.bpl.expect index 4974958b5..7203ec2a4 100644 --- a/Test/civl/regression-tests/refinement.bpl.expect +++ b/Test/civl/regression-tests/refinement.bpl.expect @@ -5,13 +5,11 @@ Execution trace: refinement.bpl(10,3): anon0$1 refinement.bpl(10,3): anon0_0 refinement.bpl(58,5): inline$INCR$1$anon0 - (0,0): Civl_ReturnChecker refinement.bpl(15,28): Error: A yield-to-yield fragment modifies layer-2 state in a way that does not match the refined atomic action Execution trace: refinement.bpl(18,3): anon0 refinement.bpl(64,5): inline$DECR$0$anon0 refinement.bpl(18,3): anon0$1 - (0,0): Civl_RefinementChecker refinement.bpl(15,28): Error: A yield-to-yield fragment modifies layer-2 state subsequent to a yield-to-yield fragment that already modified layer-2 state Execution trace: refinement.bpl(18,3): anon0 @@ -19,11 +17,9 @@ Execution trace: refinement.bpl(18,3): anon0$1 refinement.bpl(18,3): anon0_0 refinement.bpl(58,5): inline$INCR$0$anon0 - (0,0): Civl_ReturnChecker refinement.bpl(37,28): Error: On some path no yield-to-yield fragment matched the refined atomic action Execution trace: refinement.bpl(41,1): anon0 - (0,0): Civl_ReturnChecker refinement.bpl(43,28): Error: A yield-to-yield fragment illegally modifies layer-2 globals Execution trace: refinement.bpl(46,3): anon0 @@ -32,6 +28,5 @@ Execution trace: refinement.bpl(46,3): anon0_0 refinement.bpl(48,3): anon2_LoopHead refinement.bpl(58,5): inline$INCR$1$anon0 - (0,0): Civl_UnchangedChecker Boogie program verifier finished with 3 verified, 5 errors diff --git a/Test/civl/regression-tests/refinement2.bpl.expect b/Test/civl/regression-tests/refinement2.bpl.expect index 3005f0993..24cb32f73 100644 --- a/Test/civl/regression-tests/refinement2.bpl.expect +++ b/Test/civl/regression-tests/refinement2.bpl.expect @@ -1,15 +1,11 @@ refinement2.bpl(13,28): Error: On some path no yield-to-yield fragment matched the refined atomic action Execution trace: refinement2.bpl(16,5): anon0 - (0,0): Civl_call_refinement_4 refinement2.bpl(27,28): inline$atomic_nop$0$Return - (0,0): Civl_ReturnChecker refinement2.bpl(13,28): Error: A yield-to-yield fragment illegally modifies layer-2 globals Execution trace: refinement2.bpl(16,5): anon0 - (0,0): Civl_call_refinement_3 refinement2.bpl(22,7): inline$atomic_incr$0$anon0 refinement2.bpl(19,28): inline$atomic_incr$0$Return - (0,0): Civl_UnchangedChecker Boogie program verifier finished with 1 verified, 2 errors diff --git a/Test/civl/regression-tests/yield-invariant-fails.bpl.expect b/Test/civl/regression-tests/yield-invariant-fails.bpl.expect index 093836a59..8bab02145 100644 --- a/Test/civl/regression-tests/yield-invariant-fails.bpl.expect +++ b/Test/civl/regression-tests/yield-invariant-fails.bpl.expect @@ -2,6 +2,5 @@ yield-invariant-fails.bpl(7,1): Error: Non-interference check failed Execution trace: yield-invariant-fails.bpl(11,5): anon0 yield-invariant-fails.bpl(19,7): inline$atomic_A$0$anon0 - (0,0): inline$Civl_NoninterferenceChecker_yield_Inv$0$L Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/commandline/SplitOnEveryAssert.bpl b/Test/commandline/SplitOnEveryAssert.bpl index 2e3878802..f65fd1203 100644 --- a/Test/commandline/SplitOnEveryAssert.bpl +++ b/Test/commandline/SplitOnEveryAssert.bpl @@ -3,18 +3,18 @@ // RUN: %OutputCheck --file-to-check "%t" "%s" // CHECK: Verifying Ex ... -// 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: 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-L: SplitOnEveryAssert.bpl(31,5): Error: this assertion could not be proved procedure Ex() returns (y: int) diff --git a/Test/test0/SplitOnEveryAssert.bpl b/Test/test0/SplitOnEveryAssert.bpl index c883fb50c..d1313c7c9 100644 --- a/Test/test0/SplitOnEveryAssert.bpl +++ b/Test/test0/SplitOnEveryAssert.bpl @@ -3,18 +3,18 @@ // RUN: %OutputCheck --file-to-check "%t" "%s" // CHECK: Verifying DoTheSplitting ... -// 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: 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-L: SplitOnEveryAssert.bpl(36,5): Error: this assertion could not be proved // Verify the second procedure is NOT split. .* is necessary to match the blank line in-between. diff --git a/Test/test2/Structured.bpl.expect b/Test/test2/Structured.bpl.expect index a349c0d87..22c885b42 100644 --- a/Test/test2/Structured.bpl.expect +++ b/Test/test2/Structured.bpl.expect @@ -22,6 +22,5 @@ Execution trace: Structured.bpl(355,3): anon4_LoopHead Structured.bpl(358,7): anon4_LoopBody Structured.bpl(361,7): anon5_LoopBody - (0,0): anon3 Boogie program verifier finished with 16 verified, 4 errors From 064b44a3bf765e65f75067dfd42daeec7920fc81 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 16:43:27 +0200 Subject: [PATCH 45/70] Fixes --- Source/VCGeneration/Splits/BlockRewriter.cs | 1 + .../focus/multiFocus.bpl | 63 ------ .../focus/multiFocus.bpl.expect | 5 - .../isolateAssertion.bpl.expect | 182 +++++++++++++++++- 4 files changed, 181 insertions(+), 70 deletions(-) delete mode 100644 Test/implementationDivision/focus/multiFocus.bpl delete mode 100644 Test/implementationDivision/focus/multiFocus.bpl.expect diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index 1a0e990fa..afc220a84 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -68,6 +68,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead var nonDominatedBranches = path.Where(b => !filteredDag.DominatorMap.DominatedBy(lastBlock, newToOldBlocks[b])).ToList(); var newFirstBlock = Block.ShallowClone(firstBlock); + newFirstBlock.TransferCmd = new ReturnCmd(firstBlock.TransferCmd.tok); newFirstBlock.Cmds = path.SelectMany(b => b.Cmds).ToList(); yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), new List { newFirstBlock }); } diff --git a/Test/implementationDivision/focus/multiFocus.bpl b/Test/implementationDivision/focus/multiFocus.bpl deleted file mode 100644 index 6d64c1440..000000000 --- a/Test/implementationDivision/focus/multiFocus.bpl +++ /dev/null @@ -1,63 +0,0 @@ -// RUN: %parallel-boogie /printSplit:%t /vcsSplitOnEveryAssert /errorTrace:0 "%s" > "%t" -// RUN: %diff "%s.expect" "%t" - -procedure GuardWithoutFocus(x: int) returns (r: int) - ensures r > 0; -{ - if (x > 0) { - assert x > 2; - r := 3; - return; - } - - r := 2; -} - -procedure GuardWithFocus(x: int) returns (r: int) - ensures r > 0; -{ - if (x > 0) { - assert {:focus} x > 2; - r := 3; - return; - } - - r := 2; - return; -} - -procedure SubSequentAndNestedFocus(x: int) -{ - if (*) { - if (*) { - assume {:focus} true; - assert x > 1; - } else { - assert x > 2; - } - } else if (*) { - assume {:focus} true; - assert x > 3; - } else { - assert x > 4; - } - assert x > 5; - if (*) { - assume {:focus} true; - assert x > 6; - } else { - assert x > 7; - } -} - -procedure {:vcs_split_on_every_assert} EarlyReturn(x: int) { - if (*) { - assume {:focus} true; - assert x > 1; - return; - } - - assert x > 2; - assert x > 3; - assert x > 4; -} \ No newline at end of file diff --git a/Test/implementationDivision/focus/multiFocus.bpl.expect b/Test/implementationDivision/focus/multiFocus.bpl.expect deleted file mode 100644 index 4250cfcbc..000000000 --- a/Test/implementationDivision/focus/multiFocus.bpl.expect +++ /dev/null @@ -1,5 +0,0 @@ -MultiFocus.bpl(22,3): Error: this assertion could not be proved -MultiFocus.bpl(22,3): Error: this assertion could not be proved -MultiFocus.bpl(22,3): Error: this assertion could not be proved - -Boogie program verifier finished with 0 verified, 3 errors diff --git a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect index d66944447..e832a09f9 100644 --- a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect @@ -1,3 +1,181 @@ -Focus.bpl(15,5): Error: this assertion could not be proved +implementation IsolateAssertion/assert@20(x: int, y: int) +{ -Boogie program verifier finished with 2 verified, 1 error + anon0: + goto anon7_Then, anon7_Else; + + anon7_Then: + assume {:partition} x > 0; + assume z#AT#0 == 0 + 1; + assume z#AT#2 == z#AT#0; + goto anon8_Then, anon8_Else; + + anon7_Else: + assume {:partition} 0 >= x; + assume z#AT#1 == 0 + 2; + assume z#AT#2 == z#AT#1; + goto anon8_Then, anon8_Else; + + anon8_Then: + assume {:partition} y > 0; + assume z#AT#3 == z#AT#2 + 3; + assume z#AT#5 == z#AT#3; + goto anon6; + + anon8_Else: + assume {:partition} 0 >= y; + assume z#AT#4 == z#AT#2 + 4; + assume z#AT#5 == z#AT#4; + goto anon6; + + anon6: + assume z#AT#5 > 1; + assert {:isolate} z#AT#5 > 2; + return; +} + + +implementation IsolateAssertion(x: int, y: int) +{ + + anon0: + goto anon7_Then, anon7_Else; + + anon7_Then: + assume {:partition} x > 0; + assume z#AT#0 == 0 + 1; + assume z#AT#2 == z#AT#0; + goto anon8_Then, anon8_Else; + + anon7_Else: + assume {:partition} 0 >= x; + assume z#AT#1 == 0 + 2; + assume z#AT#2 == z#AT#1; + goto anon8_Then, anon8_Else; + + anon8_Then: + assume {:partition} y > 0; + assume z#AT#3 == z#AT#2 + 3; + assume z#AT#5 == z#AT#3; + goto anon6; + + anon8_Else: + assume {:partition} 0 >= y; + assume z#AT#4 == z#AT#2 + 4; + assume z#AT#5 == z#AT#4; + goto anon6; + + anon6: + assert z#AT#5 > 1; + assume z#AT#5 > 2; + assert z#AT#5 > 3; + return; +} + + +implementation IsolatePathsAssertion/assert@40[31,37](x: int, y: int) +{ + + anon0: + assume {:partition} 0 >= x; + assume z#AT#1 == 0 + 2; + assume z#AT#2 == z#AT#1; + assume {:partition} 0 >= y; + assume z#AT#4 == z#AT#2 + 4; + assume z#AT#5 == z#AT#4; + assume z#AT#5 > 1; + assert {:isolate "paths"} z#AT#5 > 5; + return; +} + + +implementation IsolatePathsAssertion/assert@40[29,37](x: int, y: int) +{ + + anon0: + assume {:partition} x > 0; + assume z#AT#0 == 0 + 1; + assume z#AT#2 == z#AT#0; + assume {:partition} 0 >= y; + assume z#AT#4 == z#AT#2 + 4; + assume z#AT#5 == z#AT#4; + assume z#AT#5 > 1; + assert {:isolate "paths"} z#AT#5 > 5; + return; +} + + +implementation IsolatePathsAssertion/assert@40[31,35](x: int, y: int) +{ + + anon0: + assume {:partition} 0 >= x; + assume z#AT#1 == 0 + 2; + assume z#AT#2 == z#AT#1; + assume {:partition} y > 0; + assume z#AT#3 == z#AT#2 + 3; + assume z#AT#5 == z#AT#3; + assume z#AT#5 > 1; + assert {:isolate "paths"} z#AT#5 > 5; + return; +} + + +implementation IsolatePathsAssertion/assert@40[29,35](x: int, y: int) +{ + + anon0: + assume {:partition} x > 0; + assume z#AT#0 == 0 + 1; + assume z#AT#2 == z#AT#0; + assume {:partition} y > 0; + assume z#AT#3 == z#AT#2 + 3; + assume z#AT#5 == z#AT#3; + assume z#AT#5 > 1; + assert {:isolate "paths"} z#AT#5 > 5; + return; +} + + +implementation IsolatePathsAssertion(x: int, y: int) +{ + + anon0: + goto anon7_Then, anon7_Else; + + anon7_Then: + assume {:partition} x > 0; + assume z#AT#0 == 0 + 1; + assume z#AT#2 == z#AT#0; + goto anon8_Then, anon8_Else; + + anon7_Else: + assume {:partition} 0 >= x; + assume z#AT#1 == 0 + 2; + assume z#AT#2 == z#AT#1; + goto anon8_Then, anon8_Else; + + anon8_Then: + assume {:partition} y > 0; + assume z#AT#3 == z#AT#2 + 3; + assume z#AT#5 == z#AT#3; + goto anon6; + + anon8_Else: + assume {:partition} 0 >= y; + assume z#AT#4 == z#AT#2 + 4; + assume z#AT#5 == z#AT#4; + goto anon6; + + anon6: + assert z#AT#5 > 1; + assume z#AT#5 > 5; + assert z#AT#5 > 6; + return; +} + + +isolateAssertion.bpl(40,3): Error: this assertion could not be proved +isolateAssertion.bpl(41,3): Error: this assertion could not be proved + +Boogie program verifier finished with 1 verified, 4 errors From a7ce5e06dd91bd5fe947391dcbaa82c6ba581829 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Wed, 2 Oct 2024 16:52:08 +0200 Subject: [PATCH 46/70] Test file fixes --- .../AssumeFalseSplit.bpl | 5 +- .../split/AssumeFalseSplit.bpl.expect | 36 ++++++ .../split/{originalSplit => }/Split.bpl | 6 +- .../split/Split.bpl.expect | 112 ++++++++++++++++++ .../AssumeFalseSplit.bpl.expect | 2 - .../assumeFalseSplit/Foo.split.0.bpl.expect | 10 -- .../assumeFalseSplit/Foo.split.1.bpl.expect | 12 -- .../assumeFalseSplit/Foo.split.2.bpl.expect | 12 -- .../originalSplit/Foo.split.0.bpl.expect | 15 --- .../originalSplit/Foo.split.1.bpl.expect | 19 --- .../originalSplit/Foo.split.2.bpl.expect | 19 --- .../originalSplit/Foo.split.3.bpl.expect | 35 ------ .../originalSplit/Foo.split.4.bpl.expect | 31 ----- .../split/originalSplit/Split.bpl.expect | 5 - Test/pruning/MonomorphicSplit.bpl | 2 +- 15 files changed, 151 insertions(+), 170 deletions(-) rename Test/implementationDivision/split/{assumeFalseSplit => }/AssumeFalseSplit.bpl (50%) create mode 100644 Test/implementationDivision/split/AssumeFalseSplit.bpl.expect rename Test/implementationDivision/split/{originalSplit => }/Split.bpl (64%) create mode 100644 Test/implementationDivision/split/Split.bpl.expect delete mode 100644 Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl.expect delete mode 100644 Test/implementationDivision/split/assumeFalseSplit/Foo.split.0.bpl.expect delete mode 100644 Test/implementationDivision/split/assumeFalseSplit/Foo.split.1.bpl.expect delete mode 100644 Test/implementationDivision/split/assumeFalseSplit/Foo.split.2.bpl.expect delete mode 100644 Test/implementationDivision/split/originalSplit/Foo.split.0.bpl.expect delete mode 100644 Test/implementationDivision/split/originalSplit/Foo.split.1.bpl.expect delete mode 100644 Test/implementationDivision/split/originalSplit/Foo.split.2.bpl.expect delete mode 100644 Test/implementationDivision/split/originalSplit/Foo.split.3.bpl.expect delete mode 100644 Test/implementationDivision/split/originalSplit/Foo.split.4.bpl.expect delete mode 100644 Test/implementationDivision/split/originalSplit/Split.bpl.expect diff --git a/Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl b/Test/implementationDivision/split/AssumeFalseSplit.bpl similarity index 50% rename from Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl rename to Test/implementationDivision/split/AssumeFalseSplit.bpl index 148216280..92c6bb87f 100644 --- a/Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl +++ b/Test/implementationDivision/split/AssumeFalseSplit.bpl @@ -1,8 +1,5 @@ -// RUN: %parallel-boogie /printSplit:%t "%s" > "%t" +// RUN: %boogie /printSplit:- "%s" > "%t" // RUN: %diff "%s.expect" "%t" -// 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/implementationDivision/split/AssumeFalseSplit.bpl.expect b/Test/implementationDivision/split/AssumeFalseSplit.bpl.expect new file mode 100644 index 000000000..29725f4eb --- /dev/null +++ b/Test/implementationDivision/split/AssumeFalseSplit.bpl.expect @@ -0,0 +1,36 @@ +implementation Foo/split@4() +{ + + anon3_Then: + assert 2 == 1 + 1; + assume false; + return; +} + + +implementation Foo/split@11() +{ + + anon3_Else: + assume 2 == 1 + 1; + assert {:split_here} 2 == 2; + assume 3 == 2 + 1; + assume 1 == 1; + goto ; +} + + +implementation Foo/split@12() +{ + + anon3_Else: + assume 2 == 1 + 1; + assume 2 == 2; + assert {:split_here} 3 == 2 + 1; + assert 1 == 1; + goto ; +} + + + +Boogie program verifier finished with 1 verified, 0 errors diff --git a/Test/implementationDivision/split/originalSplit/Split.bpl b/Test/implementationDivision/split/Split.bpl similarity index 64% rename from Test/implementationDivision/split/originalSplit/Split.bpl rename to Test/implementationDivision/split/Split.bpl index e99d76351..4c7fdaf45 100644 --- a/Test/implementationDivision/split/originalSplit/Split.bpl +++ b/Test/implementationDivision/split/Split.bpl @@ -1,9 +1,5 @@ -// RUN: %parallel-boogie /printSplit:%t "%s" > "%t" +// RUN: %boogie /printSplit:- "%s" > "%t" // RUN: %diff "%s.expect" "%t" -// 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; diff --git a/Test/implementationDivision/split/Split.bpl.expect b/Test/implementationDivision/split/Split.bpl.expect new file mode 100644 index 000000000..c8b6615c9 --- /dev/null +++ b/Test/implementationDivision/split/Split.bpl.expect @@ -0,0 +1,112 @@ +implementation Foo/split@4() returns (y: int) +{ + + anon0: + assert 5 + 0 == 5; + assert 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + assume {:partition} 0 >= x#AT#0; + assume y#AT#2 == y#AT#0; + assert y#AT#2 >= 0; + return; +} + + +implementation Foo/split@15() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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; + assume {:partition} x#AT#1 < 3; + assert 2 < 2; + assume y#AT#1 * y#AT#1 > 4; + goto ; +} + + +implementation Foo/split@19() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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; + assume {:partition} x#AT#1 < 3; + assume 2 < 2; + assert {:split_here} y#AT#1 * y#AT#1 > 4; + goto ; +} + + +implementation Foo/split@22() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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; + assume {:partition} 3 <= x#AT#1; + assert {:split_here} y#AT#1 * y#AT#1 * y#AT#1 < 8; + assert 2 < 2; + goto ; +} + + +implementation Foo/split@25() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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_Then, anon6_Else; + + anon6_Then: + assume {:partition} x#AT#1 < 3; + assume 2 < 2; + assume y#AT#1 * y#AT#1 > 4; + goto anon4; + + anon6_Else: + assume {:partition} 3 <= x#AT#1; + assume y#AT#1 * y#AT#1 * y#AT#1 < 8; + assume 2 < 2; + goto anon4; + + anon4: + assert {:split_here} (x#AT#1 + y#AT#1) * (x#AT#1 + y#AT#1) == 25; + assert x#AT#1 + y#AT#1 == 5; + assert x#AT#1 * x#AT#1 <= 25; + assume false; + return; +} + + +Split.bpl(15,5): Error: this assertion could not be proved +Execution trace: + Split.bpl(8,5): anon0 + +Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl.expect b/Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl.expect deleted file mode 100644 index 37fad75c9..000000000 --- a/Test/implementationDivision/split/assumeFalseSplit/AssumeFalseSplit.bpl.expect +++ /dev/null @@ -1,2 +0,0 @@ - -Boogie program verifier finished with 1 verified, 0 errors diff --git a/Test/implementationDivision/split/assumeFalseSplit/Foo.split.0.bpl.expect b/Test/implementationDivision/split/assumeFalseSplit/Foo.split.0.bpl.expect deleted file mode 100644 index c8727ad27..000000000 --- a/Test/implementationDivision/split/assumeFalseSplit/Foo.split.0.bpl.expect +++ /dev/null @@ -1,10 +0,0 @@ -implementation Foo() -{ - - anon3_Then: - assert 2 == 1 + 1; - assume false; - return; -} - - diff --git a/Test/implementationDivision/split/assumeFalseSplit/Foo.split.1.bpl.expect b/Test/implementationDivision/split/assumeFalseSplit/Foo.split.1.bpl.expect deleted file mode 100644 index 29ba05d16..000000000 --- a/Test/implementationDivision/split/assumeFalseSplit/Foo.split.1.bpl.expect +++ /dev/null @@ -1,12 +0,0 @@ -implementation Foo() -{ - - anon3_Else: - assume 2 == 1 + 1; - assert {:split_here} 2 == 2; - assume 3 == 2 + 1; - assume 1 == 1; - goto ; -} - - diff --git a/Test/implementationDivision/split/assumeFalseSplit/Foo.split.2.bpl.expect b/Test/implementationDivision/split/assumeFalseSplit/Foo.split.2.bpl.expect deleted file mode 100644 index 838d5aebd..000000000 --- a/Test/implementationDivision/split/assumeFalseSplit/Foo.split.2.bpl.expect +++ /dev/null @@ -1,12 +0,0 @@ -implementation Foo() -{ - - anon3_Else: - assume 2 == 1 + 1; - assume 2 == 2; - assert {:split_here} 3 == 2 + 1; - assert 1 == 1; - goto ; -} - - diff --git a/Test/implementationDivision/split/originalSplit/Foo.split.0.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.0.bpl.expect deleted file mode 100644 index 79578bccc..000000000 --- a/Test/implementationDivision/split/originalSplit/Foo.split.0.bpl.expect +++ /dev/null @@ -1,15 +0,0 @@ -implementation Foo() returns (y: int) -{ - - anon0: - assert 5 + 0 == 5; - assert 5 * 5 <= 25; - assume x#AT#0 + y#AT#0 == 5; - assume x#AT#0 * x#AT#0 <= 25; - assume {:partition} 0 >= x#AT#0; - assume y#AT#2 == y#AT#0; - assert y#AT#2 >= 0; - return; -} - - diff --git a/Test/implementationDivision/split/originalSplit/Foo.split.1.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.1.bpl.expect deleted file mode 100644 index 37297e028..000000000 --- a/Test/implementationDivision/split/originalSplit/Foo.split.1.bpl.expect +++ /dev/null @@ -1,19 +0,0 @@ -implementation Foo() returns (y: int) -{ - - anon0: - assume 5 + 0 == 5; - assume 5 * 5 <= 25; - assume x#AT#0 + y#AT#0 == 5; - assume x#AT#0 * x#AT#0 <= 25; - 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; - assume {:partition} x#AT#1 < 3; - assert 2 < 2; - assume y#AT#1 * y#AT#1 > 4; - goto ; -} - - diff --git a/Test/implementationDivision/split/originalSplit/Foo.split.2.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.2.bpl.expect deleted file mode 100644 index 7f28a2ebf..000000000 --- a/Test/implementationDivision/split/originalSplit/Foo.split.2.bpl.expect +++ /dev/null @@ -1,19 +0,0 @@ -implementation Foo() returns (y: int) -{ - - anon0: - assume 5 + 0 == 5; - assume 5 * 5 <= 25; - assume x#AT#0 + y#AT#0 == 5; - assume x#AT#0 * x#AT#0 <= 25; - 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; - assume {:partition} 3 <= x#AT#1; - assert {:split_here} y#AT#1 * y#AT#1 * y#AT#1 < 8; - assert 2 < 2; - goto ; -} - - diff --git a/Test/implementationDivision/split/originalSplit/Foo.split.3.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.3.bpl.expect deleted file mode 100644 index eb031aa38..000000000 --- a/Test/implementationDivision/split/originalSplit/Foo.split.3.bpl.expect +++ /dev/null @@ -1,35 +0,0 @@ -implementation Foo() returns (y: int) -{ - - anon0: - assume 5 + 0 == 5; - assume 5 * 5 <= 25; - assume x#AT#0 + y#AT#0 == 5; - assume x#AT#0 * x#AT#0 <= 25; - 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_Then, anon6_Else; - - anon6_Else: - assume {:partition} 3 <= x#AT#1; - assume y#AT#1 * y#AT#1 * y#AT#1 < 8; - assume 2 < 2; - goto anon4; - - anon4: - assert {:split_here} (x#AT#1 + y#AT#1) * (x#AT#1 + y#AT#1) == 25; - assert x#AT#1 + y#AT#1 == 5; - assert x#AT#1 * x#AT#1 <= 25; - assume false; - return; - - anon6_Then: - assume {:partition} x#AT#1 < 3; - assume 2 < 2; - assume y#AT#1 * y#AT#1 > 4; - goto anon4; -} - - diff --git a/Test/implementationDivision/split/originalSplit/Foo.split.4.bpl.expect b/Test/implementationDivision/split/originalSplit/Foo.split.4.bpl.expect deleted file mode 100644 index 83b9850a7..000000000 --- a/Test/implementationDivision/split/originalSplit/Foo.split.4.bpl.expect +++ /dev/null @@ -1,31 +0,0 @@ -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_Then; - - anon6_Then: - assume {:partition} x#AT#1 < 3; - assume 2 < 2; - assert {:split_here} y#AT#1 * y#AT#1 > 4; - goto ; -} - - diff --git a/Test/implementationDivision/split/originalSplit/Split.bpl.expect b/Test/implementationDivision/split/originalSplit/Split.bpl.expect deleted file mode 100644 index 67f8e9921..000000000 --- a/Test/implementationDivision/split/originalSplit/Split.bpl.expect +++ /dev/null @@ -1,5 +0,0 @@ -Split.bpl(19,5): Error: this assertion could not be proved -Execution trace: - Split.bpl(12,5): anon0 - -Boogie program verifier finished with 0 verified, 1 error diff --git a/Test/pruning/MonomorphicSplit.bpl b/Test/pruning/MonomorphicSplit.bpl index 15351bb11..1fccbd8bf 100644 --- a/Test/pruning/MonomorphicSplit.bpl +++ b/Test/pruning/MonomorphicSplit.bpl @@ -1,5 +1,5 @@ // RUN: %parallel-boogie /prune:1 /errorTrace:0 /printSplit:"%t" /printSplitDeclarations "%s" > "%t" -// RUN: %OutputCheck "%s" --file-to-check="%t-monomorphicSplit--1.spl" +// RUN: %OutputCheck "%s" --file-to-check="%t-monomorphicSplit.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 From 9a276546610e2fd10ccc668b3cc3c8ec5b5d13d1 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 11:10:33 +0200 Subject: [PATCH 47/70] Fixes --- Source/Graph/Graph.cs | 2 +- Source/VCGeneration/Splits/BlockRewriter.cs | 53 ++++++++++++------- .../Splits/FocusAttributeHandler.cs | 1 - .../IsolateAttributeOnAssertsHandler.cs | 13 +++-- .../Splits/IsolateAttributeOnJumpsHandler.cs | 7 ++- .../VCGeneration/Splits/ManualSplitFinder.cs | 10 ++-- Source/VCGeneration/Splits/Split.cs | 4 +- .../isolateAssertion.bpl.expect | 48 ++++++++--------- .../isolateJump/isolateJump.bpl.expect | 4 +- .../split/Split.bpl.expect | 50 ++++++++--------- 10 files changed, 106 insertions(+), 86 deletions(-) diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 0efd5ef58..949457345 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -1157,7 +1157,7 @@ public string ToDot(Func NodeLabel = null, Func Node return s.ToString(); } - public ICollection ComputeReachability(Node start, bool forward = true) + public ISet ComputeReachability(Node start, bool forward = true) { var todo = new Stack(); var visited = new HashSet(); diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index afc220a84..be3a7cb94 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -12,24 +12,27 @@ namespace VCGeneration.Splits; public class BlockRewriter { private readonly Dictionary assumedAssertions = new(); public VCGenOptions Options { get; } - private readonly Graph dag; + public Graph Dag { get; } public Func, ManualSplit> CreateSplit { get; } - public BlockRewriter(VCGenOptions options, List blocks, Func, ManualSplit> createSplit) { + public BlockRewriter(VCGenOptions options, List blocks, + Func, ManualSplit> createSplit) { this.Options = options; CreateSplit = createSplit; - dag = Program.GraphFromBlocks(blocks); + Dag = Program.GraphFromBlocks(blocks); } public Cmd TransformAssertCmd(Cmd cmd) { if (cmd is AssertCmd assertCmd) { - return assumedAssertions.GetOrCreate(assertCmd, () => VerificationConditionGenerator.AssertTurnedIntoAssume(Options, assertCmd)); + return assumedAssertions.GetOrCreate(assertCmd, + () => VerificationConditionGenerator.AssertTurnedIntoAssume(Options, assertCmd)); } return cmd; } - public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IReadOnlySet? blocksToInclude, IToken origin) { + public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IReadOnlySet? blocksToInclude, + IToken origin) { var blockToVisit = new Stack>(); var newToOldBlocks = new Dictionary(); var newLastBlock = Block.ShallowClone(lastBlock); @@ -37,7 +40,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead blockToVisit.Push(ImmutableStack.Create(newLastBlock)); newToOldBlocks[newLastBlock] = lastBlock; - while(blockToVisit.Any()) { + while (blockToVisit.Any()) { var path = blockToVisit.Pop(); var firstBlock = path.Peek(); IEnumerable predecessors = firstBlock.Predecessors; @@ -51,21 +54,24 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead var newPrevious = Block.ShallowClone(oldPrevious); newPrevious.Predecessors = oldPrevious.Predecessors; newPrevious.Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList(); - + newToOldBlocks[newPrevious] = oldPrevious; if (newPrevious.TransferCmd is GotoCmd gotoCmd) { newPrevious.TransferCmd = - new GotoCmd(gotoCmd.tok, new List { firstBlock.Label }, new List - { + new GotoCmd(gotoCmd.tok, new List { firstBlock.Label }, new List { firstBlock }); } + blockToVisit.Push(path.Push(newPrevious)); } + if (!hadPredecessors) { - - var filteredDag = blocksToInclude == null ? dag : Program.GraphFromBlocksSubset(newToOldBlocks[path.Peek()], blocksToInclude); - var nonDominatedBranches = path.Where(b => + + var filteredDag = blocksToInclude == null + ? Dag + : Program.GraphFromBlocksSubset(newToOldBlocks[path.Peek()], blocksToInclude); + var nonDominatedBranches = path.Where(b => !filteredDag.DominatorMap.DominatedBy(lastBlock, newToOldBlocks[b])).ToList(); var newFirstBlock = Block.ShallowClone(firstBlock); newFirstBlock.TransferCmd = new ReturnCmd(firstBlock.TransferCmd.tok); @@ -74,14 +80,25 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead } } } - - public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks(ISet blocksToInclude, IEnumerable blocksReversed, - ISet freeAssumeBlocks) + + public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks( + ISet blocksToInclude, + IEnumerable blocksReversed, + ISet freeAssumeBlocks) { + return ComputeNewBlocks(blocksToInclude, blocksReversed, block => + freeAssumeBlocks.Contains(block) + ? block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList() + : block.Cmds); + } + + public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks( + ISet blocksToInclude, + IEnumerable blocksReversed, + Func> getCommands) { var newBlocks = new List(); var oldToNewBlockMap = new Dictionary(blocksToInclude.Count); - // TODO, use ManualSplitFinder.CreateSplit() // Traverse backwards to allow settings the jumps to the new blocks foreach (var block in blocksReversed) { @@ -94,9 +111,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead oldToNewBlockMap[block] = newBlock; // freeBlocks consist of the predecessors of the relevant foci. // Their assertions turn into assumes and any splits inside them are disabled. - newBlock.Cmds = freeAssumeBlocks.Contains(block) - ? block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList() - : block.Cmds; + newBlock.Cmds = getCommands(block); if (block.TransferCmd is GotoCmd gtc) { diff --git a/Source/VCGeneration/Splits/FocusAttributeHandler.cs b/Source/VCGeneration/Splits/FocusAttributeHandler.cs index 5449ef404..301230367 100644 --- a/Source/VCGeneration/Splits/FocusAttributeHandler.cs +++ b/Source/VCGeneration/Splits/FocusAttributeHandler.cs @@ -47,7 +47,6 @@ public List GetParts(ImplementationRun run) reachables.Remove(fb.Block); ancestorsPerBlock[fb.Block] = reachables; }); - var dominators = dag.DominatorMap; focusBlocks.ForEach(fb => descendantsPerBlock[fb.Block] = dag.ComputeReachability(fb.Block).ToHashSet()); var result = new List(); diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs index 9cccd0772..e8d5722f1 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs @@ -1,5 +1,6 @@ #nullable enable using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using Microsoft.Boogie; using VC; @@ -47,17 +48,19 @@ public IsolateAttributeOnAssertsHandler(BlockRewriter rewriter) { } } + if (!results.Any()) { + return (results,partToDivide); + } + return (results,GetSplitWithoutIsolatedAssertions()); - ManualSplit GetSplitForIsolatedAssertion(Block blockWithAssert, AssertCmd assertCmd) { - var blocksToVisit = new Stack(new[] { blockWithAssert }); - var orderedNewBlocks = BlockRewriter.UpdateBlocks(blocksToVisit, - new HashSet(), + var blocksToKeep = rewriter.Dag.ComputeReachability(blockWithAssert, false); + var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToKeep, rewriter.Dag.TopologicalSort().Reversed(), oldBlock => oldBlock == blockWithAssert ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) : oldBlock.Cmds.Select(rewriter.TransformAssertCmd).ToList()); - return rewriter.CreateSplit(new IsolatedAssertionOrigin(assertCmd), orderedNewBlocks.Values.OrderBy(b => b.tok).ToList()); + return rewriter.CreateSplit(new IsolatedAssertionOrigin(assertCmd), newBlocks); } List GetCommandsForBlockWithAssert(Block currentBlock, AssertCmd assert) diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs index 44e73b9c0..9b5c459b8 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs @@ -18,8 +18,7 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { this.rewriter = rewriter; } - public (List Isolated, ManualSplit Remainder) GetParts( - ManualSplit partToDivide) { + public (List Isolated, ManualSplit Remainder) GetParts(ManualSplit partToDivide) { var results = new List(); var blocks = partToDivide.Blocks; @@ -62,6 +61,10 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { } } + if (!results.Any()) { + return (results,partToDivide); + } + return (results, GetPartWithoutIsolatedReturns()); ManualSplit GetPartWithoutIsolatedReturns() { diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index af8e05be1..36c51a7fa 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -13,19 +13,21 @@ namespace VCGeneration; public static class ManualSplitFinder { public static IEnumerable GetParts(VCGenOptions options, ImplementationRun run, - Func, ManualSplit> createPart) - { + Func, ManualSplit> createPart) { + var blockRewriter = new BlockRewriter(options, run.Implementation.Blocks, createPart); var focussedParts = new FocusAttributeHandler(blockRewriter).GetParts(run); - return focussedParts.SelectMany(focussedPart => { + // return focussedParts; + var result = focussedParts.SelectMany(focussedPart => { var (isolatedJumps, withoutIsolatedJumps) = new IsolateAttributeOnJumpsHandler(blockRewriter).GetParts(focussedPart); var (isolatedAssertions, withoutIsolatedAssertions) = new IsolateAttributeOnAssertsHandler(new BlockRewriter(options, withoutIsolatedJumps.Blocks, createPart)).GetParts(withoutIsolatedJumps); - + var splitParts = new SplitAttributeHandler(blockRewriter).GetParts(withoutIsolatedAssertions); return isolatedJumps.Concat(isolatedAssertions).Concat(splitParts); }); + return result; } } diff --git a/Source/VCGeneration/Splits/Split.cs b/Source/VCGeneration/Splits/Split.cs index 9930e6ecc..f5ecfb6df 100644 --- a/Source/VCGeneration/Splits/Split.cs +++ b/Source/VCGeneration/Splits/Split.cs @@ -449,13 +449,11 @@ void UpdateIncomingPaths(BlockStats s) void ComputeBlockSetsHelper(Block b, bool allowSmall) { Contract.Requires(b != null); - if (keepAtAll.Contains(b)) + if (!keepAtAll.Add(b)) { return; } - keepAtAll.Add(b); - if (allowSmall) { foreach (Block ch in b.Exits()) diff --git a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect index e832a09f9..bada08fe2 100644 --- a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect @@ -73,32 +73,16 @@ implementation IsolateAssertion(x: int, y: int) } -implementation IsolatePathsAssertion/assert@40[31,37](x: int, y: int) -{ - - anon0: - assume {:partition} 0 >= x; - assume z#AT#1 == 0 + 2; - assume z#AT#2 == z#AT#1; - assume {:partition} 0 >= y; - assume z#AT#4 == z#AT#2 + 4; - assume z#AT#5 == z#AT#4; - assume z#AT#5 > 1; - assert {:isolate "paths"} z#AT#5 > 5; - return; -} - - -implementation IsolatePathsAssertion/assert@40[29,37](x: int, y: int) +implementation IsolatePathsAssertion/assert@40[29,35](x: int, y: int) { anon0: assume {:partition} x > 0; assume z#AT#0 == 0 + 1; assume z#AT#2 == z#AT#0; - assume {:partition} 0 >= y; - assume z#AT#4 == z#AT#2 + 4; - assume z#AT#5 == z#AT#4; + assume {:partition} y > 0; + assume z#AT#3 == z#AT#2 + 3; + assume z#AT#5 == z#AT#3; assume z#AT#5 > 1; assert {:isolate "paths"} z#AT#5 > 5; return; @@ -121,16 +105,32 @@ implementation IsolatePathsAssertion/assert@40[31,35](x: int, y: int) } -implementation IsolatePathsAssertion/assert@40[29,35](x: int, y: int) +implementation IsolatePathsAssertion/assert@40[29,37](x: int, y: int) { anon0: assume {:partition} x > 0; assume z#AT#0 == 0 + 1; assume z#AT#2 == z#AT#0; - assume {:partition} y > 0; - assume z#AT#3 == z#AT#2 + 3; - assume z#AT#5 == z#AT#3; + assume {:partition} 0 >= y; + assume z#AT#4 == z#AT#2 + 4; + assume z#AT#5 == z#AT#4; + assume z#AT#5 > 1; + assert {:isolate "paths"} z#AT#5 > 5; + return; +} + + +implementation IsolatePathsAssertion/assert@40[31,37](x: int, y: int) +{ + + anon0: + assume {:partition} 0 >= x; + assume z#AT#1 == 0 + 2; + assume z#AT#2 == z#AT#1; + assume {:partition} 0 >= y; + assume z#AT#4 == z#AT#2 + 4; + assume z#AT#5 == z#AT#4; assume z#AT#5 > 1; assert {:isolate "paths"} z#AT#5 > 5; return; diff --git a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect index fc715a4d0..8b4d1c292 100644 --- a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect +++ b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect @@ -58,7 +58,7 @@ implementation IsolateReturn(x: int, y: int) returns (r: int) } -isolateJump.bpl(20,1): Error: a postcondition could not be proved on this return path +isolateJump.bpl(16,21): Error: a postcondition could not be proved on this return path isolateJump.bpl(5,3): Related location: this is the postcondition that could not be proved implementation IsolateReturnPaths/assert@34[27](x: int, y: int) returns (r: int) { @@ -120,7 +120,7 @@ implementation IsolateReturnPaths(x: int, y: int) returns (r: int) } -isolateJump.bpl(25,5): Error: a postcondition could not be proved on this return path +isolateJump.bpl(22,11): Error: a postcondition could not be proved on this return path isolateJump.bpl(23,3): Related location: this is the postcondition that could not be proved Boogie program verifier finished with 0 verified, 2 errors diff --git a/Test/implementationDivision/split/Split.bpl.expect b/Test/implementationDivision/split/Split.bpl.expect index c8b6615c9..9f213b896 100644 --- a/Test/implementationDivision/split/Split.bpl.expect +++ b/Test/implementationDivision/split/Split.bpl.expect @@ -32,25 +32,6 @@ implementation Foo/split@15() returns (y: int) } -implementation Foo/split@19() returns (y: int) -{ - - anon0: - assume 5 + 0 == 5; - assume 5 * 5 <= 25; - assume x#AT#0 + y#AT#0 == 5; - assume x#AT#0 * x#AT#0 <= 25; - 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; - assume {:partition} x#AT#1 < 3; - assume 2 < 2; - assert {:split_here} y#AT#1 * y#AT#1 > 4; - goto ; -} - - implementation Foo/split@22() returns (y: int) { @@ -84,12 +65,6 @@ implementation Foo/split@25() returns (y: int) assume y#AT#1 == y#AT#0 + 1; goto anon6_Then, anon6_Else; - anon6_Then: - assume {:partition} x#AT#1 < 3; - assume 2 < 2; - assume y#AT#1 * y#AT#1 > 4; - goto anon4; - anon6_Else: assume {:partition} 3 <= x#AT#1; assume y#AT#1 * y#AT#1 * y#AT#1 < 8; @@ -102,6 +77,31 @@ implementation Foo/split@25() returns (y: int) assert x#AT#1 * x#AT#1 <= 25; assume false; return; + + anon6_Then: + assume {:partition} x#AT#1 < 3; + assume 2 < 2; + assume y#AT#1 * y#AT#1 > 4; + goto anon4; +} + + +implementation Foo/split@19() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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; + assume {:partition} x#AT#1 < 3; + assume 2 < 2; + assert {:split_here} y#AT#1 * y#AT#1 > 4; + goto ; } From 43d6824f261e459184b6be503e01d1dba555a423 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 11:13:41 +0200 Subject: [PATCH 48/70] Update tests --- .../isolateAssertion/isolateAssertion.bpl | 4 ++-- .../isolateAssertion/isolateAssertion.bpl.expect | 10 ++++++---- .../isolateJump/isolateJump.bpl.expect | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl index 1582a6b2e..59cd30f93 100644 --- a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl @@ -17,8 +17,8 @@ procedure IsolateAssertion(x: int, y: int) z := z + 4; } assert z > 1; - assert {:isolate} z > 2; - assert z > 3; + assert {:isolate} z > 5; + assert z > 6; } procedure IsolatePathsAssertion(x: int, y: int) diff --git a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect index bada08fe2..27589907d 100644 --- a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl.expect @@ -30,7 +30,7 @@ implementation IsolateAssertion/assert@20(x: int, y: int) anon6: assume z#AT#5 > 1; - assert {:isolate} z#AT#5 > 2; + assert {:isolate} z#AT#5 > 5; return; } @@ -67,12 +67,14 @@ implementation IsolateAssertion(x: int, y: int) anon6: assert z#AT#5 > 1; - assume z#AT#5 > 2; - assert z#AT#5 > 3; + assume z#AT#5 > 5; + assert z#AT#5 > 6; return; } +isolateAssertion.bpl(20,3): Error: this assertion could not be proved +isolateAssertion.bpl(21,3): Error: this assertion could not be proved implementation IsolatePathsAssertion/assert@40[29,35](x: int, y: int) { @@ -178,4 +180,4 @@ implementation IsolatePathsAssertion(x: int, y: int) isolateAssertion.bpl(40,3): Error: this assertion could not be proved isolateAssertion.bpl(41,3): Error: this assertion could not be proved -Boogie program verifier finished with 1 verified, 4 errors +Boogie program verifier finished with 0 verified, 6 errors diff --git a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect index 8b4d1c292..8a62ce819 100644 --- a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect +++ b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect @@ -120,7 +120,7 @@ implementation IsolateReturnPaths(x: int, y: int) returns (r: int) } -isolateJump.bpl(22,11): Error: a postcondition could not be proved on this return path +isolateJump.bpl(34,11): Error: a postcondition could not be proved on this return path isolateJump.bpl(23,3): Related location: this is the postcondition that could not be proved Boogie program verifier finished with 0 verified, 2 errors From 1a5ff898652b41f70dc49a697b88e8fb8f3fe557 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 11:22:20 +0200 Subject: [PATCH 49/70] Fix path isolation error reporting --- Source/VCGeneration/Splits/BlockRewriter.cs | 8 ++++---- .../implementationDivision/focus/{Focus.bpl => focus.bpl} | 0 .../focus/{Focus.bpl.expect => focus.bpl.expect} | 2 +- .../isolateJump/isolateJump.bpl.expect | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename Test/implementationDivision/focus/{Focus.bpl => focus.bpl} (100%) rename Test/implementationDivision/focus/{Focus.bpl.expect => focus.bpl.expect} (50%) diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index be3a7cb94..feda23001 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -73,10 +73,10 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead : Program.GraphFromBlocksSubset(newToOldBlocks[path.Peek()], blocksToInclude); var nonDominatedBranches = path.Where(b => !filteredDag.DominatorMap.DominatedBy(lastBlock, newToOldBlocks[b])).ToList(); - var newFirstBlock = Block.ShallowClone(firstBlock); - newFirstBlock.TransferCmd = new ReturnCmd(firstBlock.TransferCmd.tok); - newFirstBlock.Cmds = path.SelectMany(b => b.Cmds).ToList(); - yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), new List { newFirstBlock }); + var singletonBlock = Block.ShallowClone(firstBlock); + singletonBlock.TransferCmd = new ReturnCmd(origin); + singletonBlock.Cmds = path.SelectMany(b => b.Cmds).ToList(); + yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), new List { singletonBlock }); } } } diff --git a/Test/implementationDivision/focus/Focus.bpl b/Test/implementationDivision/focus/focus.bpl similarity index 100% rename from Test/implementationDivision/focus/Focus.bpl rename to Test/implementationDivision/focus/focus.bpl diff --git a/Test/implementationDivision/focus/Focus.bpl.expect b/Test/implementationDivision/focus/focus.bpl.expect similarity index 50% rename from Test/implementationDivision/focus/Focus.bpl.expect rename to Test/implementationDivision/focus/focus.bpl.expect index 71c2b6338..2c28a1a35 100644 --- a/Test/implementationDivision/focus/Focus.bpl.expect +++ b/Test/implementationDivision/focus/focus.bpl.expect @@ -1,3 +1,3 @@ -Focus.bpl(15,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 diff --git a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect index 8a62ce819..3c47dc663 100644 --- a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect +++ b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect @@ -120,7 +120,7 @@ implementation IsolateReturnPaths(x: int, y: int) returns (r: int) } -isolateJump.bpl(34,11): Error: a postcondition could not be proved on this return path +isolateJump.bpl(34,29): Error: a postcondition could not be proved on this return path isolateJump.bpl(23,3): Related location: this is the postcondition that could not be proved Boogie program verifier finished with 0 verified, 2 errors From be86f1931f6424521ccb92347a79bc478bb691bc Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 11:25:24 +0200 Subject: [PATCH 50/70] Refactoring --- Source/VCGeneration/Splits/BlockRewriter.cs | 56 ++----------------- .../IsolateAttributeOnAssertsHandler.cs | 7 ++- 2 files changed, 10 insertions(+), 53 deletions(-) diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index feda23001..62c54aedb 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -83,7 +83,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks( ISet blocksToInclude, - IEnumerable blocksReversed, + IReadOnlyList blocksReversed, ISet freeAssumeBlocks) { return ComputeNewBlocks(blocksToInclude, blocksReversed, block => freeAssumeBlocks.Contains(block) @@ -92,17 +92,17 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead } public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks( - ISet blocksToInclude, - IEnumerable blocksReversed, + ISet? blocksToInclude, + IReadOnlyList blocksReversed, Func> getCommands) { var newBlocks = new List(); - var oldToNewBlockMap = new Dictionary(blocksToInclude.Count); + var oldToNewBlockMap = new Dictionary(blocksReversed.Count); // Traverse backwards to allow settings the jumps to the new blocks foreach (var block in blocksReversed) { - if (!blocksToInclude.Contains(block)) { + if (blocksToInclude != null && !blocksToInclude.Contains(block)) { continue; } @@ -115,7 +115,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead if (block.TransferCmd is GotoCmd gtc) { - var targets = gtc.LabelTargets.Where(blocksToInclude.Contains).ToList(); + var targets = blocksToInclude == null ? gtc.LabelTargets : gtc.LabelTargets.Where(blocksToInclude.Contains).ToList(); newBlock.TransferCmd = new GotoCmd(gtc.tok, targets.Select(blk => oldToNewBlockMap[blk].Label).ToList(), targets.Select(blk => oldToNewBlockMap[blk]).ToList()); @@ -127,48 +127,4 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); return (newBlocks, oldToNewBlockMap); } - - public static OrderedDictionary UpdateBlocks(Stack blocksToVisit, - HashSet visitedBlocks, Func> getCommands) - { - var oldToNewBlockMap = new OrderedDictionary(); - while(blocksToVisit.Any()) { - var oldBlock = blocksToVisit.Pop(); - if (!visitedBlocks.Add(oldBlock)) { - continue; - } - - var newBlock = Block.ShallowClone(oldBlock); - oldToNewBlockMap.Add(oldBlock, newBlock); - newBlock.Cmds = getCommands(oldBlock); - foreach (var previous in oldBlock.Predecessors) { - blocksToVisit.Push(previous); - } - - } - - foreach (var (oldBlock, newBlock) in oldToNewBlockMap) { - if (oldBlock.TransferCmd is GotoCmd gtc) - { - var targets = gtc.LabelTargets.Where(oldToNewBlockMap.ContainsKey).ToList(); - newBlock.TransferCmd = new GotoCmd(gtc.tok, - targets.Select(block => oldToNewBlockMap[block].Label).ToList(), - targets.Select(block => oldToNewBlockMap[block]).ToList()); - } - } - - return oldToNewBlockMap; - } - - private static Cmd DisableSplits(Cmd command) - { - if (command is not PredicateCmd pc) - { - return command; - } - - pc.Attributes = new QKeyValue(Token.NoToken, "split", - new List { new LiteralExpr(Token.NoToken, false) }, pc.Attributes); - return command; - } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs index e8d5722f1..bf3ed2ecf 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs @@ -83,10 +83,11 @@ ManualSplit GetSplitWithoutIsolatedAssertions() { return rewriter.CreateSplit(origin, partToDivide.Blocks); } - var newBlocks = BlockRewriter.UpdateBlocks(new Stack(partToDivide.Blocks), - new HashSet(), + + var (newBlocks, mapping) = rewriter.ComputeNewBlocks(null, rewriter.Dag.TopologicalSort().Reversed(), block => block.Cmds.Select(cmd => isolatedAssertions.Contains(cmd) ? rewriter.TransformAssertCmd(cmd) : cmd).ToList()); - return rewriter.CreateSplit(origin, newBlocks.Values.OrderBy(b => b.tok).ToList()); + + return rewriter.CreateSplit(origin, newBlocks); } } From d91787c5c85cb1bac1da59ab244758c91dfd2dd4 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 11:36:01 +0200 Subject: [PATCH 51/70] Refactoring --- Source/VCGeneration/ManualSplit.cs | 4 +-- Source/VCGeneration/Splits/BlockRewriter.cs | 12 ++++---- .../Splits/FocusAttributeHandler.cs | 16 ++++------ .../IsolateAttributeOnAssertsHandler.cs | 30 ++++++++++--------- .../Splits/IsolateAttributeOnJumpsHandler.cs | 18 ++++------- .../VCGeneration/Splits/ManualSplitFinder.cs | 12 +++----- Source/VCGeneration/Splits/Split.cs | 6 ++-- .../Splits/SplitAttributeHandler.cs | 8 +---- 8 files changed, 44 insertions(+), 62 deletions(-) diff --git a/Source/VCGeneration/ManualSplit.cs b/Source/VCGeneration/ManualSplit.cs index 701732e10..f9ee1629f 100644 --- a/Source/VCGeneration/ManualSplit.cs +++ b/Source/VCGeneration/ManualSplit.cs @@ -12,10 +12,10 @@ public class ManualSplit : Split public ManualSplit(VCGenOptions options, Func> blocks, - VerificationConditionGenerator par, + VerificationConditionGenerator parent, ImplementationRun run, ImplementationPartOrigin origin, int? randomSeed = null) - : base(options, blocks, par, run, randomSeed) + : base(options, blocks, parent, run, randomSeed) { Origin = origin; } diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index 62c54aedb..d31456427 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -11,6 +11,7 @@ namespace VCGeneration.Splits; public class BlockRewriter { private readonly Dictionary assumedAssertions = new(); + private readonly IReadOnlyList reversedBlocks; public VCGenOptions Options { get; } public Graph Dag { get; } public Func, ManualSplit> CreateSplit { get; } @@ -20,6 +21,7 @@ public BlockRewriter(VCGenOptions options, List blocks, this.Options = options; CreateSplit = createSplit; Dag = Program.GraphFromBlocks(blocks); + reversedBlocks = Dag.TopologicalSort().Reversed(); } public Cmd TransformAssertCmd(Cmd cmd) { @@ -83,24 +85,22 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks( ISet blocksToInclude, - IReadOnlyList blocksReversed, ISet freeAssumeBlocks) { - return ComputeNewBlocks(blocksToInclude, blocksReversed, block => + return ComputeNewBlocks(blocksToInclude, block => freeAssumeBlocks.Contains(block) ? block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList() : block.Cmds); } public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks( - ISet? blocksToInclude, - IReadOnlyList blocksReversed, + ISet? blocksToInclude, Func> getCommands) { var newBlocks = new List(); - var oldToNewBlockMap = new Dictionary(blocksReversed.Count); + var oldToNewBlockMap = new Dictionary(reversedBlocks.Count); // Traverse backwards to allow settings the jumps to the new blocks - foreach (var block in blocksReversed) + foreach (var block in reversedBlocks) { if (blocksToInclude != null && !blocksToInclude.Contains(block)) { continue; diff --git a/Source/VCGeneration/Splits/FocusAttributeHandler.cs b/Source/VCGeneration/Splits/FocusAttributeHandler.cs index 301230367..dfab70e2d 100644 --- a/Source/VCGeneration/Splits/FocusAttributeHandler.cs +++ b/Source/VCGeneration/Splits/FocusAttributeHandler.cs @@ -9,27 +9,23 @@ using Cmd = Microsoft.Boogie.Cmd; using PredicateCmd = Microsoft.Boogie.PredicateCmd; using QKeyValue = Microsoft.Boogie.QKeyValue; -using VCGenOptions = Microsoft.Boogie.VCGenOptions; namespace VCGeneration; public class FocusAttributeHandler { - private readonly BlockRewriter rewriter; - - public FocusAttributeHandler(BlockRewriter rewriter) { - this.rewriter = rewriter; - } /// /// Each focus block creates two options. /// We recurse twice for each focus, leading to potentially 2^N splits /// - public List GetParts(ImplementationRun run) + public static List GetParts(VCGenOptions options, ImplementationRun run, + Func, ManualSplit> createPart) { + var rewriter = new BlockRewriter(options, run.Implementation.Blocks, createPart); + var implementation = run.Implementation; - var dag = Program.GraphFromImpl(implementation); + var dag = rewriter.Dag; var topologicallySortedBlocks = dag.TopologicalSort(); - var blocksReversed = Enumerable.Reverse(topologicallySortedBlocks).ToList(); // By default, we process the foci in a top-down fashion, i.e., in the topological order. // If the user sets the RelaxFocus flag, we use the reverse (topological) order. var focusBlocks = GetFocusBlocks(topologicallySortedBlocks); @@ -56,7 +52,7 @@ public List GetParts(ImplementationRun run) void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet blocksToInclude, ISet freeAssumeBlocks) { var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; if (allFocusBlocksHaveBeenProcessed) { - var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, blocksReversed, freeAssumeBlocks); + var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, freeAssumeBlocks); ImplementationPartOrigin token = path.Any() ? new PathOrigin(run.Implementation.tok, path.ToList()) // TODO fix : new ImplementationRootOrigin(run.Implementation); diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs index bf3ed2ecf..8950159f8 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -9,14 +10,11 @@ namespace VCGeneration; class IsolateAttributeOnAssertsHandler { - private readonly BlockRewriter rewriter; - public IsolateAttributeOnAssertsHandler(BlockRewriter rewriter) { - this.rewriter = rewriter; - } - - public (List IsolatedParts, ManualSplit Remainder) GetParts(ManualSplit partToDivide) { - + public static (List IsolatedParts, ManualSplit Remainder) GetParts(VCGenOptions options, ManualSplit partToDivide, + Func, ManualSplit> createPart) { + var rewriter = new BlockRewriter(options, partToDivide.Blocks, createPart); + var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; partToDivide.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); @@ -56,10 +54,13 @@ public IsolateAttributeOnAssertsHandler(BlockRewriter rewriter) { ManualSplit GetSplitForIsolatedAssertion(Block blockWithAssert, AssertCmd assertCmd) { var blocksToKeep = rewriter.Dag.ComputeReachability(blockWithAssert, false); - var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToKeep, rewriter.Dag.TopologicalSort().Reversed(), - oldBlock => oldBlock == blockWithAssert - ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) - : oldBlock.Cmds.Select(rewriter.TransformAssertCmd).ToList()); + + List GetCommands(Block oldBlock) => + oldBlock == blockWithAssert + ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) + : oldBlock.Cmds.Select(rewriter.TransformAssertCmd).ToList(); + + var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToKeep, GetCommands); return rewriter.CreateSplit(new IsolatedAssertionOrigin(assertCmd), newBlocks); } @@ -83,11 +84,12 @@ ManualSplit GetSplitWithoutIsolatedAssertions() { return rewriter.CreateSplit(origin, partToDivide.Blocks); } - - var (newBlocks, mapping) = rewriter.ComputeNewBlocks(null, rewriter.Dag.TopologicalSort().Reversed(), - block => block.Cmds.Select(cmd => isolatedAssertions.Contains(cmd) ? rewriter.TransformAssertCmd(cmd) : cmd).ToList()); + var (newBlocks, mapping) = rewriter.ComputeNewBlocks(null, GetCommands); return rewriter.CreateSplit(origin, newBlocks); + + List GetCommands(Block block) => block.Cmds.Select(cmd => + isolatedAssertions.Contains(cmd) ? rewriter.TransformAssertCmd(cmd) : cmd).ToList(); } } diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs index 9b5c459b8..a35e8f2b9 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs @@ -12,19 +12,14 @@ namespace VCGeneration; class IsolateAttributeOnJumpsHandler { - private readonly BlockRewriter rewriter; - - public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { - this.rewriter = rewriter; - } - - public (List Isolated, ManualSplit Remainder) GetParts(ManualSplit partToDivide) { + public static (List Isolated, ManualSplit Remainder) GetParts(VCGenOptions options, ManualSplit partToDivide, + Func, ManualSplit> createPart) { + var rewriter = new BlockRewriter(options, partToDivide.Blocks, createPart); + var results = new List(); var blocks = partToDivide.Blocks; var dag = Program.GraphFromBlocks(blocks); - var topoSorted = dag.TopologicalSort(); - var reversedBlocks = topoSorted.Reversed(); var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; partToDivide.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); @@ -55,8 +50,7 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { Debug.Assert(gotoCmd.LabelTargets[0].TransferCmd is not GotoCmd); results.AddRange(rewriter.GetSplitsForIsolatedPaths(gotoCmd.LabelTargets[0], blocksToInclude, originalReturn.tok)); } else { - var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, - reversedBlocks, ancestors.ToHashSet()); + var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, ancestors.ToHashSet()); results.Add(rewriter.CreateSplit(new ReturnOrigin(originalReturn), newBlocks)); } } @@ -69,7 +63,7 @@ public IsolateAttributeOnJumpsHandler(BlockRewriter rewriter) { ManualSplit GetPartWithoutIsolatedReturns() { // TODO this needs an extra test. In case the isolated jump is followed by something it dominates - var (newBlocks, mapping) = rewriter.ComputeNewBlocks(blocks.ToHashSet(), reversedBlocks, new HashSet()); + var (newBlocks, mapping) = rewriter.ComputeNewBlocks(blocks.ToHashSet(), new HashSet()); foreach (var (oldBlock, newBlock) in mapping) { if (isolatedBlocks.Contains(oldBlock)) { newBlock.TransferCmd = new ReturnCmd(Token.NoToken); diff --git a/Source/VCGeneration/Splits/ManualSplitFinder.cs b/Source/VCGeneration/Splits/ManualSplitFinder.cs index 36c51a7fa..4be4db4fa 100644 --- a/Source/VCGeneration/Splits/ManualSplitFinder.cs +++ b/Source/VCGeneration/Splits/ManualSplitFinder.cs @@ -1,11 +1,9 @@ #nullable enable using System; using System.Collections.Generic; -using System.Diagnostics.Contracts; using System.Linq; using Microsoft.Boogie; using VC; -using VCGeneration.Splits; namespace VCGeneration; @@ -15,16 +13,14 @@ public static class ManualSplitFinder { public static IEnumerable GetParts(VCGenOptions options, ImplementationRun run, Func, ManualSplit> createPart) { - var blockRewriter = new BlockRewriter(options, run.Implementation.Blocks, createPart); - var focussedParts = new FocusAttributeHandler(blockRewriter).GetParts(run); - // return focussedParts; + var focussedParts = FocusAttributeHandler.GetParts(options, run, createPart); var result = focussedParts.SelectMany(focussedPart => { var (isolatedJumps, withoutIsolatedJumps) = - new IsolateAttributeOnJumpsHandler(blockRewriter).GetParts(focussedPart); + IsolateAttributeOnJumpsHandler.GetParts(options, focussedPart, createPart); var (isolatedAssertions, withoutIsolatedAssertions) = - new IsolateAttributeOnAssertsHandler(new BlockRewriter(options, withoutIsolatedJumps.Blocks, createPart)).GetParts(withoutIsolatedJumps); + IsolateAttributeOnAssertsHandler.GetParts(options, withoutIsolatedJumps, createPart); - var splitParts = new SplitAttributeHandler(blockRewriter).GetParts(withoutIsolatedAssertions); + var splitParts = SplitAttributeHandler.GetParts(withoutIsolatedAssertions); return isolatedJumps.Concat(isolatedAssertions).Concat(splitParts); }); return result; diff --git a/Source/VCGeneration/Splits/Split.cs b/Source/VCGeneration/Splits/Split.cs index f5ecfb6df..48025ae40 100644 --- a/Source/VCGeneration/Splits/Split.cs +++ b/Source/VCGeneration/Splits/Split.cs @@ -76,11 +76,11 @@ public readonly VerificationConditionGenerator /*!*/ public VerificationConditionGenerator.ErrorReporter reporter; public Split(VCGenOptions options, Func> /*!*/ getBlocks, - VerificationConditionGenerator /*!*/ par, ImplementationRun run, int? randomSeed = null) + VerificationConditionGenerator /*!*/ parent, ImplementationRun run, int? randomSeed = null) { - Contract.Requires(par != null); + Contract.Requires(parent != null); this.getBlocks = getBlocks; - parent = par; + this.parent = parent; this.Run = run; this.Options = options; Interlocked.Increment(ref currentId); diff --git a/Source/VCGeneration/Splits/SplitAttributeHandler.cs b/Source/VCGeneration/Splits/SplitAttributeHandler.cs index 0779bcef0..8158a8d5d 100644 --- a/Source/VCGeneration/Splits/SplitAttributeHandler.cs +++ b/Source/VCGeneration/Splits/SplitAttributeHandler.cs @@ -5,18 +5,12 @@ using System.Linq; using Microsoft.Boogie; using VC; -using VCGeneration.Splits; namespace VCGeneration; class SplitAttributeHandler { - private readonly BlockRewriter rewriter; - public SplitAttributeHandler(BlockRewriter rewriter) { - this.rewriter = rewriter; - } - - public List GetParts(ManualSplit partToSplit) { + public static List GetParts(ManualSplit partToSplit) { var splitsPerBlock = new Dictionary>(); var splits = new HashSet(); foreach (var block in partToSplit.Blocks) { From 2e8c8e4f03b293cfe8ce2c16d7f4409f55c37e83 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 11:37:14 +0200 Subject: [PATCH 52/70] Remove TODO --- Source/VCGeneration/Splits/BlockRewriter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index d31456427..c02142ffa 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -123,7 +123,6 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead } newBlocks.Reverse(); - // TODO remove? BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); return (newBlocks, oldToNewBlockMap); } From 91c304d30780a3294b9f4e4ebe7e1ae5ef95e885 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 11:47:55 +0200 Subject: [PATCH 53/70] Refactoring --- Source/Core/AST/AbsyCmd.cs | 5 +-- Source/Core/AST/AbsyQuant.cs | 2 +- Source/Core/AST/Implementation.cs | 8 ++-- Source/Core/AST/Program.cs | 38 ++++--------------- Source/Core/Generic/OrderedDictionary.cs | 31 --------------- Source/VCGeneration/Splits/BlockRewriter.cs | 7 ++-- .../Splits/FocusAttributeHandler.cs | 6 +-- 7 files changed, 20 insertions(+), 77 deletions(-) delete mode 100644 Source/Core/Generic/OrderedDictionary.cs diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 784843309..97b66daf7 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -1285,10 +1285,7 @@ public static void ComputeChecksums(CoreOptions options, Cmd cmd, Implementation { ComputeChecksums(options, c, impl, usedVariables, currentChecksum); currentChecksum = c.Checksum; - if (c.SugaredCmdChecksum == null) - { - c.SugaredCmdChecksum = cmd.Checksum; - } + c.SugaredCmdChecksum ??= cmd.Checksum; } } else diff --git a/Source/Core/AST/AbsyQuant.cs b/Source/Core/AST/AbsyQuant.cs index aa827671f..301a3bf70 100644 --- a/Source/Core/AST/AbsyQuant.cs +++ b/Source/Core/AST/AbsyQuant.cs @@ -552,7 +552,7 @@ public static bool FindBoolAttribute(QKeyValue kv, string name) ((LiteralExpr) qkv.Params[0]).IsTrue))); return kv != null; } - + public static int FindIntAttribute(QKeyValue kv, string name, int defl) { Contract.Requires(name != null); diff --git a/Source/Core/AST/Implementation.cs b/Source/Core/AST/Implementation.cs index a48a18b5a..7a6c565ac 100644 --- a/Source/Core/AST/Implementation.cs +++ b/Source/Core/AST/Implementation.cs @@ -462,19 +462,19 @@ void BlocksWriters(TokenTextWriter stream) { } public void EmitImplementation(TokenTextWriter stream, int level, IEnumerable blocks, - bool showLocals, string namePrefix = "") { + bool showLocals, string nameSuffix = "") { EmitImplementation(stream, level, writer => { foreach (var block in blocks) { block.Emit(writer, level + 1); } - }, showLocals, namePrefix); + }, showLocals, nameSuffix); } - public void EmitImplementation(TokenTextWriter stream, int level, Action printBlocks, bool showLocals, string namePrefix = "") + private void EmitImplementation(TokenTextWriter stream, int level, Action printBlocks, bool showLocals, string nameSuffix = "") { stream.Write(this, level, "implementation "); EmitAttributes(stream); - stream.Write(this, level, "{0}", TokenTextWriter.SanitizeIdentifier(Name) + namePrefix); + stream.Write(this, level, "{0}", TokenTextWriter.SanitizeIdentifier(Name) + nameSuffix); EmitSignature(stream, false); stream.WriteLine(); diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index d764284e8..82ec6e4ba 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -498,15 +498,15 @@ public static Graph BuildCallGraph(CoreOptions options, Program return callGraph; } - public static Graph GraphFromBlocksSubset(Block source, IReadOnlySet subset, bool forward = true) + public static Graph GraphFromBlocksSubset(IReadOnlyList blocks, IReadOnlySet subset = null, bool forward = true) { var result = new Graph(); - if (!subset.Any()) + if (!blocks.Any()) { return result; } void AddEdge(Block a, Block b) { - if (!subset.Contains(a) || !subset.Contains(b)) { + if (subset != null && (!subset.Contains(a) || !subset.Contains(b))) { return; } Contract.Assert(a != null && b != null); @@ -517,8 +517,8 @@ void AddEdge(Block a, Block b) { } } - result.AddSource(source); - foreach (var block in subset) + result.AddSource(blocks[0]); + foreach (var block in blocks) { if (block.TransferCmd is GotoCmd gtc) { @@ -529,32 +529,8 @@ void AddEdge(Block a, Block b) { return result; } - public static Graph GraphFromBlocks(IReadOnlyList blocks, bool forward = true) - { - var result = new Graph(); - if (!blocks.Any()) - { - return result; - } - void AddEdge(Block a, Block b) { - Contract.Assert(a != null && b != null); - if (forward) { - result.AddEdge(a, b); - } else { - result.AddEdge(b, a); - } - } - - result.AddSource(cce.NonNull(blocks[0])); // there is always at least one node in the graph - foreach (var block in blocks) - { - if (block.TransferCmd is GotoCmd gtc) - { - Contract.Assume(gtc.LabelTargets != null); - gtc.LabelTargets.ForEach(dest => AddEdge(block, dest)); - } - } - return result; + public static Graph GraphFromBlocks(IReadOnlyList blocks, bool forward = true) { + return GraphFromBlocksSubset(blocks, null, forward); } public static Graph /*!*/ GraphFromImpl(Implementation impl, bool forward = true) diff --git a/Source/Core/Generic/OrderedDictionary.cs b/Source/Core/Generic/OrderedDictionary.cs deleted file mode 100644 index 7608b4f04..000000000 --- a/Source/Core/Generic/OrderedDictionary.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections; -using System.Collections.Generic; -using System.Linq; - -namespace Microsoft.Boogie; - -public class OrderedDictionary : IEnumerable> { - private readonly Dictionary mapping = new(); - private readonly List orderedKeys = new(); - - public void Add(TKey key, TValue value) { - orderedKeys.Add(key); - mapping[key] = value; - } - - public bool ContainsKey(TKey key) { - return mapping.ContainsKey(key); - } - - public TValue this[TKey key] => mapping[key]; - - public IEnumerable Keys => orderedKeys; - public IEnumerable Values => orderedKeys.Select(k => mapping[k]); - public IEnumerator> GetEnumerator() { - return mapping.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() { - return GetEnumerator(); - } -} \ No newline at end of file diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index c02142ffa..2f0159d96 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -12,6 +12,7 @@ namespace VCGeneration.Splits; public class BlockRewriter { private readonly Dictionary assumedAssertions = new(); private readonly IReadOnlyList reversedBlocks; + public List OrderedBlocks { get; } public VCGenOptions Options { get; } public Graph Dag { get; } public Func, ManualSplit> CreateSplit { get; } @@ -21,7 +22,8 @@ public BlockRewriter(VCGenOptions options, List blocks, this.Options = options; CreateSplit = createSplit; Dag = Program.GraphFromBlocks(blocks); - reversedBlocks = Dag.TopologicalSort().Reversed(); + OrderedBlocks = Dag.TopologicalSort(); + reversedBlocks = OrderedBlocks.Reversed(); } public Cmd TransformAssertCmd(Cmd cmd) { @@ -69,10 +71,9 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead } if (!hadPredecessors) { - var filteredDag = blocksToInclude == null ? Dag - : Program.GraphFromBlocksSubset(newToOldBlocks[path.Peek()], blocksToInclude); + : Program.GraphFromBlocksSubset(OrderedBlocks, blocksToInclude); var nonDominatedBranches = path.Where(b => !filteredDag.DominatorMap.DominatedBy(lastBlock, newToOldBlocks[b])).ToList(); var singletonBlock = Block.ShallowClone(firstBlock); diff --git a/Source/VCGeneration/Splits/FocusAttributeHandler.cs b/Source/VCGeneration/Splits/FocusAttributeHandler.cs index dfab70e2d..197bc1fe1 100644 --- a/Source/VCGeneration/Splits/FocusAttributeHandler.cs +++ b/Source/VCGeneration/Splits/FocusAttributeHandler.cs @@ -25,10 +25,10 @@ public static List GetParts(VCGenOptions options, ImplementationRun var implementation = run.Implementation; var dag = rewriter.Dag; - var topologicallySortedBlocks = dag.TopologicalSort(); + // By default, we process the foci in a top-down fashion, i.e., in the topological order. // If the user sets the RelaxFocus flag, we use the reverse (topological) order. - var focusBlocks = GetFocusBlocks(topologicallySortedBlocks); + var focusBlocks = GetFocusBlocks(rewriter.OrderedBlocks); if (rewriter.Options.RelaxFocus) { focusBlocks.Reverse(); } @@ -66,7 +66,7 @@ void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet } else { - var dominatedBlocks = DominatedBlocks(topologicallySortedBlocks, focusBlock, blocksToInclude); + var dominatedBlocks = DominatedBlocks(rewriter.OrderedBlocks, focusBlock, blocksToInclude); // Recursive call that does NOT focus the block // Contains all blocks except the ones dominated by the focus block AddSplitsFromIndex(path, focusIndex + 1, From ff1442a8847fcc79ba789417719b1c6b39e9ad0d Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 12:32:41 +0200 Subject: [PATCH 54/70] Remove TODO --- Source/Graph/Graph.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 949457345..2af21485a 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -72,9 +72,6 @@ public class DomRelation [NotDelayed] internal DomRelation(Graph g, Node source) { - // TODO should we enable saying that the graph is a DAG, to enable an O(N) dominance algorithm? - // Or is the algorithm already O(N) for DAG graphs? - graph = g; // slot 0 not used: nodes are numbered from 1 to n so zero // can represent undefined. From c603cc88cc8ba695031e46c0631c078c729f8760 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 12:37:28 +0200 Subject: [PATCH 55/70] Cleanup --- Source/ExecutionEngine/VerificationTask.cs | 4 ++-- Source/VCGeneration/ManualSplit.cs | 4 ++-- Source/VCGeneration/Splits/Split.cs | 3 +-- Source/VCGeneration/Splits/SplitAndVerifyWorker.cs | 2 +- Source/VCGeneration/Splits/SplitAttributeHandler.cs | 2 +- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Source/ExecutionEngine/VerificationTask.cs b/Source/ExecutionEngine/VerificationTask.cs index 3571dac41..50f3fd8e5 100644 --- a/Source/ExecutionEngine/VerificationTask.cs +++ b/Source/ExecutionEngine/VerificationTask.cs @@ -24,7 +24,7 @@ public class VerificationTask : IVerificationTask { public IToken ScopeToken => Split.Implementation.tok; public string ScopeId => Split.Implementation.VerboseName; - public IToken Token => Split.Origin; + public IToken Token => Split.Token; public ManualSplit Split { get; } public VerificationTask(ExecutionEngine engine, ProcessedProgram processedProgram, ManualSplit split, @@ -45,7 +45,7 @@ public VerificationTask(ExecutionEngine engine, ProcessedProgram processedProgra public IVerificationTask FromSeed(int newSeed) { var split = new ManualSplit(Split.Options, () => Split.Blocks, - Split.parent, Split.Run, Split.Origin, newSeed); + Split.parent, Split.Run, Split.Token, newSeed); split.SplitIndex = Split.SplitIndex; return new VerificationTask(engine, ProcessedProgram, split, modelViewInfo); } diff --git a/Source/VCGeneration/ManualSplit.cs b/Source/VCGeneration/ManualSplit.cs index f9ee1629f..95b555c6e 100644 --- a/Source/VCGeneration/ManualSplit.cs +++ b/Source/VCGeneration/ManualSplit.cs @@ -8,7 +8,7 @@ namespace VC; public class ManualSplit : Split { - public ImplementationPartOrigin Origin { get; } + public ImplementationPartOrigin Token { get; } public ManualSplit(VCGenOptions options, Func> blocks, @@ -17,6 +17,6 @@ public ManualSplit(VCGenOptions options, ImplementationPartOrigin origin, int? randomSeed = null) : base(options, blocks, parent, run, randomSeed) { - Origin = origin; + Token = origin; } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/Split.cs b/Source/VCGeneration/Splits/Split.cs index 48025ae40..c4d33efd6 100644 --- a/Source/VCGeneration/Splits/Split.cs +++ b/Source/VCGeneration/Splits/Split.cs @@ -14,7 +14,6 @@ namespace VC { - // TODO rename since it's not only used to split a proof into two. For example focus doesn't really split public class Split : ProofRun { public VCGenOptions Options { get; } @@ -99,7 +98,7 @@ private void PrintSplit() { Thread.Sleep(100); } - var prefix = (this is ManualSplit manualSplit) ? manualSplit.Origin.ShortName : ""; + var prefix = (this is ManualSplit manualSplit) ? manualSplit.Token.ShortName : ""; var name = Implementation.Name + prefix; using var writer = printToConsole ? new TokenTextWriter("", Options.OutputWriter, false, Options.PrettyPrint, Options) diff --git a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs index a5d262108..57f3bfb33 100644 --- a/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs +++ b/Source/VCGeneration/Splits/SplitAndVerifyWorker.cs @@ -144,7 +144,7 @@ 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.Origin.line})" : "", + split is ManualSplit manualSplit ? $" (line {manualSplit.Token.line})" : "", 100 * provenCost / (provenCost + remainingCost))); } diff --git a/Source/VCGeneration/Splits/SplitAttributeHandler.cs b/Source/VCGeneration/Splits/SplitAttributeHandler.cs index 8158a8d5d..85bcc73a8 100644 --- a/Source/VCGeneration/Splits/SplitAttributeHandler.cs +++ b/Source/VCGeneration/Splits/SplitAttributeHandler.cs @@ -131,7 +131,7 @@ private static bool ShouldSplitHere(Cmd c) { return null; } - return createVc(new SplitOrigin(split?.tok ?? partToSplit.Origin), newBlocks); + return createVc(new SplitOrigin(split?.tok ?? partToSplit.Token), newBlocks); List GetCommandsForBlockImmediatelyDominatedBySplit(Block currentBlock) { From 506d9c1317761f74903dbb49e1040e9eb905e185 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 15:07:34 +0200 Subject: [PATCH 56/70] Refactoring before adding pathable blocks, but this broke some tests --- Source/Core/Analysis/BlockCoalescer.cs | 6 ++ Source/VCGeneration/BlockTransformations.cs | 9 +-- Source/VCGeneration/Splits/BlockRewriter.cs | 72 +++++++++---------- .../Splits/FocusAttributeHandler.cs | 5 +- .../IsolateAttributeOnAssertsHandler.cs | 6 +- .../Splits/IsolateAttributeOnJumpsHandler.cs | 8 +-- 6 files changed, 51 insertions(+), 55 deletions(-) diff --git a/Source/Core/Analysis/BlockCoalescer.cs b/Source/Core/Analysis/BlockCoalescer.cs index 60100930f..7b62b144b 100644 --- a/Source/Core/Analysis/BlockCoalescer.cs +++ b/Source/Core/Analysis/BlockCoalescer.cs @@ -79,6 +79,12 @@ public override Implementation VisitImplementation(Implementation impl) return impl; } + public static void CoalesceInPlace(List blocks) { + var coalesced = BlockCoalescer.CoalesceFromRootBlock(blocks); + blocks.Clear(); + blocks.AddRange(coalesced); + } + public static List CoalesceFromRootBlock(List blocks) { if (!blocks.Any()) diff --git a/Source/VCGeneration/BlockTransformations.cs b/Source/VCGeneration/BlockTransformations.cs index 95e0d7788..c3f27da23 100644 --- a/Source/VCGeneration/BlockTransformations.cs +++ b/Source/VCGeneration/BlockTransformations.cs @@ -20,11 +20,8 @@ public static void Optimize(List blocks) { } DeleteBlocksNotLeadingToAssertions(blocks); - DeleteUselessBlocks(blocks); - - var coalesced = BlockCoalescer.CoalesceFromRootBlock(blocks); - blocks.Clear(); - blocks.AddRange(coalesced); + DeleteStraightLineBlocksWithoutCommands(blocks); + BlockCoalescer.CoalesceInPlace(blocks); } private static void StopControlFlowAtAssumeFalse(Block block) @@ -99,7 +96,7 @@ private static bool ContainsAssert(Block b) public static bool IsNonTrivialAssert (Cmd c) { return c is AssertCmd { Expr: not LiteralExpr { asBool: true } }; } - private static void DeleteUselessBlocks(List blocks) { + private static void DeleteStraightLineBlocksWithoutCommands(List blocks) { var toVisit = new HashSet(); var removed = new HashSet(); foreach (var block in blocks) { diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index 2f0159d96..3e0181c5f 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -37,12 +37,13 @@ public Cmd TransformAssertCmd(Cmd cmd) { public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IReadOnlySet? blocksToInclude, IToken origin) { + var filteredDag = blocksToInclude == null + ? Dag + : Program.GraphFromBlocksSubset(OrderedBlocks, blocksToInclude); + var blockToVisit = new Stack>(); - var newToOldBlocks = new Dictionary(); - var newLastBlock = Block.ShallowClone(lastBlock); - newLastBlock.Predecessors = lastBlock.Predecessors; - blockToVisit.Push(ImmutableStack.Create(newLastBlock)); - newToOldBlocks[newLastBlock] = lastBlock; + + blockToVisit.Push(ImmutableStack.Create(lastBlock)); while (blockToVisit.Any()) { var path = blockToVisit.Pop(); @@ -53,49 +54,43 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead } var hadPredecessors = false; - foreach (var oldPrevious in predecessors) { + foreach (var predecessor in predecessors) { hadPredecessors = true; - var newPrevious = Block.ShallowClone(oldPrevious); - newPrevious.Predecessors = oldPrevious.Predecessors; - newPrevious.Cmds = oldPrevious.Cmds.Select(TransformAssertCmd).ToList(); - - newToOldBlocks[newPrevious] = oldPrevious; - if (newPrevious.TransferCmd is GotoCmd gotoCmd) { - newPrevious.TransferCmd = - new GotoCmd(gotoCmd.tok, new List { firstBlock.Label }, new List { - firstBlock - }); - } - - blockToVisit.Push(path.Push(newPrevious)); + blockToVisit.Push(path.Push(predecessor)); } if (!hadPredecessors) { - var filteredDag = blocksToInclude == null - ? Dag - : Program.GraphFromBlocksSubset(OrderedBlocks, blocksToInclude); - var nonDominatedBranches = path.Where(b => - !filteredDag.DominatorMap.DominatedBy(lastBlock, newToOldBlocks[b])).ToList(); - var singletonBlock = Block.ShallowClone(firstBlock); - singletonBlock.TransferCmd = new ReturnCmd(origin); - singletonBlock.Cmds = path.SelectMany(b => b.Cmds).ToList(); - yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), new List { singletonBlock }); + var newBlocks = ComputeNewBlocks(path.ToHashSet(), + (oldBlock, newBlock) => { + newBlock.Cmds = oldBlock == lastBlock ? oldBlock.Cmds : oldBlock.Cmds.Select(TransformAssertCmd).ToList(); + if (oldBlock == lastBlock) { + newBlock.TransferCmd = new ReturnCmd(origin); + } + }); + + var nonDominatedBranches = path.Where(block => + !filteredDag.DominatorMap.DominatedBy(lastBlock, block)).ToList(); + + yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), newBlocks); } } } - public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks( + public List ComputeNewBlocks( ISet blocksToInclude, ISet freeAssumeBlocks) { - return ComputeNewBlocks(blocksToInclude, block => - freeAssumeBlocks.Contains(block) - ? block.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList() - : block.Cmds); + return ComputeNewBlocks(blocksToInclude, (oldBlock, newBlock) => { + newBlock.Cmds = freeAssumeBlocks.Contains(oldBlock) + ? oldBlock.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList() + : oldBlock.Cmds; + }); + + // newBlock.Cmds = getCommands(block); } - public (List NewBlocks, Dictionary Mapping) ComputeNewBlocks( + public List ComputeNewBlocks( ISet? blocksToInclude, - Func> getCommands) + Action updateNewBlock) { var newBlocks = new List(); var oldToNewBlockMap = new Dictionary(reversedBlocks.Count); @@ -110,9 +105,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead var newBlock = Block.ShallowClone(block); newBlocks.Add(newBlock); oldToNewBlockMap[block] = newBlock; - // freeBlocks consist of the predecessors of the relevant foci. - // Their assertions turn into assumes and any splits inside them are disabled. - newBlock.Cmds = getCommands(block); + updateNewBlock(block, newBlock); if (block.TransferCmd is GotoCmd gtc) { @@ -125,6 +118,7 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead newBlocks.Reverse(); BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); - return (newBlocks, oldToNewBlockMap); + BlockCoalescer.CoalesceInPlace(newBlocks); + return newBlocks; } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/FocusAttributeHandler.cs b/Source/VCGeneration/Splits/FocusAttributeHandler.cs index 197bc1fe1..c3db1c586 100644 --- a/Source/VCGeneration/Splits/FocusAttributeHandler.cs +++ b/Source/VCGeneration/Splits/FocusAttributeHandler.cs @@ -52,7 +52,10 @@ public static List GetParts(VCGenOptions options, ImplementationRun void AddSplitsFromIndex(ImmutableStack path, int focusIndex, ISet blocksToInclude, ISet freeAssumeBlocks) { var allFocusBlocksHaveBeenProcessed = focusIndex == focusBlocks.Count; if (allFocusBlocksHaveBeenProcessed) { - var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, freeAssumeBlocks); + + // freeBlocks consist of the predecessors of the relevant foci. + // Their assertions turn into assumes and any splits inside them are disabled. + var newBlocks = rewriter.ComputeNewBlocks(blocksToInclude, freeAssumeBlocks); ImplementationPartOrigin token = path.Any() ? new PathOrigin(run.Implementation.tok, path.ToList()) // TODO fix : new ImplementationRootOrigin(run.Implementation); diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs index 8950159f8..c0855ae51 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnAssertsHandler.cs @@ -1,7 +1,6 @@ #nullable enable using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using Microsoft.Boogie; using VC; @@ -60,7 +59,7 @@ List GetCommands(Block oldBlock) => ? GetCommandsForBlockWithAssert(oldBlock, assertCmd) : oldBlock.Cmds.Select(rewriter.TransformAssertCmd).ToList(); - var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToKeep, GetCommands); + var newBlocks = rewriter.ComputeNewBlocks(blocksToKeep, (oldBlock, newBlock) => newBlock.Cmds = GetCommands(oldBlock)); return rewriter.CreateSplit(new IsolatedAssertionOrigin(assertCmd), newBlocks); } @@ -84,8 +83,7 @@ ManualSplit GetSplitWithoutIsolatedAssertions() { return rewriter.CreateSplit(origin, partToDivide.Blocks); } - var (newBlocks, mapping) = rewriter.ComputeNewBlocks(null, GetCommands); - + var newBlocks = rewriter.ComputeNewBlocks(null, (oldBlock, newBlock) => newBlock.Cmds = GetCommands(oldBlock)); return rewriter.CreateSplit(origin, newBlocks); List GetCommands(Block block) => block.Cmds.Select(cmd => diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs index a35e8f2b9..25c658d3c 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs @@ -50,7 +50,7 @@ public static (List Isolated, ManualSplit Remainder) GetParts(VCGen Debug.Assert(gotoCmd.LabelTargets[0].TransferCmd is not GotoCmd); results.AddRange(rewriter.GetSplitsForIsolatedPaths(gotoCmd.LabelTargets[0], blocksToInclude, originalReturn.tok)); } else { - var (newBlocks, _) = rewriter.ComputeNewBlocks(blocksToInclude, ancestors.ToHashSet()); + var newBlocks = rewriter.ComputeNewBlocks(blocksToInclude, ancestors.ToHashSet()); results.Add(rewriter.CreateSplit(new ReturnOrigin(originalReturn), newBlocks)); } } @@ -63,13 +63,11 @@ public static (List Isolated, ManualSplit Remainder) GetParts(VCGen ManualSplit GetPartWithoutIsolatedReturns() { // TODO this needs an extra test. In case the isolated jump is followed by something it dominates - var (newBlocks, mapping) = rewriter.ComputeNewBlocks(blocks.ToHashSet(), new HashSet()); - foreach (var (oldBlock, newBlock) in mapping) { + var newBlocks = rewriter.ComputeNewBlocks(blocks.ToHashSet(), (oldBlock, newBlock) => { if (isolatedBlocks.Contains(oldBlock)) { newBlock.TransferCmd = new ReturnCmd(Token.NoToken); } - } - BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); + }); return rewriter.CreateSplit(new ImplementationRootOrigin(partToDivide.Implementation), newBlocks); } From d2278dceaaa3a2b178ea65b0296c4f192b863403 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 15:59:16 +0200 Subject: [PATCH 57/70] Refactoring so BlockCoalescer does not need to know about GotoFromReturn --- Source/Core/Analysis/BlockCoalescer.cs | 8 +++++++- Source/VCGeneration/Splits/BlockRewriter.cs | 6 ++---- .../Splits/IsolateAttributeOnJumpsHandler.cs | 6 +++--- Source/VCGeneration/Transformations/DesugarReturns.cs | 3 +-- Source/VCGeneration/VerificationConditionGenerator.cs | 8 ++++---- .../isolateJump/isolateJump.bpl.expect | 9 --------- 6 files changed, 17 insertions(+), 23 deletions(-) diff --git a/Source/Core/Analysis/BlockCoalescer.cs b/Source/Core/Analysis/BlockCoalescer.cs index 7b62b144b..12219f7c7 100644 --- a/Source/Core/Analysis/BlockCoalescer.cs +++ b/Source/Core/Analysis/BlockCoalescer.cs @@ -80,7 +80,7 @@ public override Implementation VisitImplementation(Implementation impl) } public static void CoalesceInPlace(List blocks) { - var coalesced = BlockCoalescer.CoalesceFromRootBlock(blocks); + var coalesced = CoalesceFromRootBlock(blocks); blocks.Clear(); blocks.AddRange(coalesced); } @@ -141,7 +141,13 @@ public static List CoalesceFromRootBlock(List blocks) } block.Cmds.AddRange(successor.Cmds); + var originalTransferToken = block.TransferCmd.tok; block.TransferCmd = successor.TransferCmd; + if (!block.TransferCmd.tok.IsValid) { + block.TransferCmd = (TransferCmd)block.TransferCmd.Clone(); + block.TransferCmd.tok = originalTransferToken; + } + if (!block.tok.IsValid && successor.tok.IsValid) { block.tok = successor.tok; diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index 3e0181c5f..8795387fb 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -84,8 +84,6 @@ public List ComputeNewBlocks( ? oldBlock.Cmds.Select(c => CommandTransformations.AssertIntoAssume(Options, c)).ToList() : oldBlock.Cmds; }); - - // newBlock.Cmds = getCommands(block); } public List ComputeNewBlocks( @@ -105,8 +103,6 @@ public List ComputeNewBlocks( var newBlock = Block.ShallowClone(block); newBlocks.Add(newBlock); oldToNewBlockMap[block] = newBlock; - updateNewBlock(block, newBlock); - if (block.TransferCmd is GotoCmd gtc) { var targets = blocksToInclude == null ? gtc.LabelTargets : gtc.LabelTargets.Where(blocksToInclude.Contains).ToList(); @@ -114,10 +110,12 @@ public List ComputeNewBlocks( targets.Select(blk => oldToNewBlockMap[blk].Label).ToList(), targets.Select(blk => oldToNewBlockMap[blk]).ToList()); } + updateNewBlock(block, newBlock); } newBlocks.Reverse(); BlockTransformations.DeleteBlocksNotLeadingToAssertions(newBlocks); + BlockCoalescer.CoalesceInPlace(newBlocks); return newBlocks; } diff --git a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs index 25c658d3c..88b0baa13 100644 --- a/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs +++ b/Source/VCGeneration/Splits/IsolateAttributeOnJumpsHandler.cs @@ -24,7 +24,7 @@ public static (List Isolated, ManualSplit Remainder) GetParts(VCGen var splitOnEveryAssert = partToDivide.Options.VcsSplitOnEveryAssert; partToDivide.Run.Implementation.CheckBooleanAttribute("vcs_split_on_every_assert", ref splitOnEveryAssert); - var isolatedBlocks = new HashSet(); + var isolatedBlockJumps = new HashSet(); foreach (var block in partToDivide.Blocks) { if (block.TransferCmd is not GotoCmd gotoCmd) { @@ -38,7 +38,7 @@ public static (List Isolated, ManualSplit Remainder) GetParts(VCGen continue; } - isolatedBlocks.Add(block); + isolatedBlockJumps.Add(block); var ancestors = dag.ComputeReachability(block, false); var descendants = dag.ComputeReachability(block, true); var blocksToInclude = ancestors.Union(descendants).ToHashSet(); @@ -64,7 +64,7 @@ public static (List Isolated, ManualSplit Remainder) GetParts(VCGen ManualSplit GetPartWithoutIsolatedReturns() { // TODO this needs an extra test. In case the isolated jump is followed by something it dominates var newBlocks = rewriter.ComputeNewBlocks(blocks.ToHashSet(), (oldBlock, newBlock) => { - if (isolatedBlocks.Contains(oldBlock)) { + if (isolatedBlockJumps.Contains(oldBlock)) { newBlock.TransferCmd = new ReturnCmd(Token.NoToken); } }); diff --git a/Source/VCGeneration/Transformations/DesugarReturns.cs b/Source/VCGeneration/Transformations/DesugarReturns.cs index 90ab89089..132a411b3 100644 --- a/Source/VCGeneration/Transformations/DesugarReturns.cs +++ b/Source/VCGeneration/Transformations/DesugarReturns.cs @@ -26,8 +26,7 @@ public static Block GenerateUnifiedExit(Implementation impl) if (returnBlocks > 1) { string unifiedExitLabel = "GeneratedUnifiedExit"; - var unifiedExit = new Block(Token.NoToken, unifiedExitLabel, new List(), - new ReturnCmd(impl.StructuredStmts != null ? impl.StructuredStmts.EndCurly : Token.NoToken)); + var unifiedExit = new Block(Token.NoToken, unifiedExitLabel, new List(), new ReturnCmd(Token.NoToken)); Contract.Assert(unifiedExit != null); foreach (var block in impl.Blocks) { if (block.TransferCmd is not ReturnCmd returnCmd) { diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index 315a6b047..1b957d2ae 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -571,11 +571,11 @@ public override void OnModel(IList labels /*!*/ /*!*/, Model model, if (newCounterexample is ReturnCounterexample returnExample) { - foreach (var b in returnExample.Trace) + foreach (var block in returnExample.Trace) { - Contract.Assert(b != null); - Contract.Assume(b.TransferCmd != null); - if (b.TransferCmd.tok is GotoFromReturn gotoFromReturn) { + Contract.Assert(block != null); + Contract.Assume(block.TransferCmd != null); + if (block.TransferCmd.tok is GotoFromReturn gotoFromReturn) { returnExample.FailingReturn = gotoFromReturn.Origin; } } diff --git a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect index 3c47dc663..fc9c3c33b 100644 --- a/Test/implementationDivision/isolateJump/isolateJump.bpl.expect +++ b/Test/implementationDivision/isolateJump/isolateJump.bpl.expect @@ -20,9 +20,6 @@ implementation IsolateReturn/return@16(x: int, y: int) returns (r: int) assume {:partition} y > 0; assume r#AT#3 == r#AT#2 + 3; assume r#AT#5 == r#AT#3; - goto GeneratedUnifiedExit; - - GeneratedUnifiedExit: assert r#AT#5 > 4; return; } @@ -50,9 +47,6 @@ implementation IsolateReturn(x: int, y: int) returns (r: int) assume {:partition} 0 >= y; assume r#AT#4 == r#AT#2 + 4; assume r#AT#5 == r#AT#4; - goto GeneratedUnifiedExit; - - GeneratedUnifiedExit: assert r#AT#5 > 4; return; } @@ -112,9 +106,6 @@ implementation IsolateReturnPaths(x: int, y: int) returns (r: int) assume {:partition} 0 >= y; assume r#AT#4 == r#AT#2 + 4; assume r#AT#5 == r#AT#4; - goto GeneratedUnifiedExit; - - GeneratedUnifiedExit: assert r#AT#5 > 4; return; } From 09bba24d057a92f805dd0126f6fdb5d59db1f803 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Thu, 3 Oct 2024 16:14:44 +0200 Subject: [PATCH 58/70] Update focus expect file --- Test/implementationDivision/focus/focus.bpl | 2 +- .../focus/focus.bpl.expect | 155 +++++++++++++++++- 2 files changed, 154 insertions(+), 3 deletions(-) diff --git a/Test/implementationDivision/focus/focus.bpl b/Test/implementationDivision/focus/focus.bpl index 735a57d47..432235c4c 100644 --- a/Test/implementationDivision/focus/focus.bpl +++ b/Test/implementationDivision/focus/focus.bpl @@ -1,4 +1,4 @@ -// RUN: %parallel-boogie /printSplit:%t /errorTrace:0 "%s" > "%t" +// RUN: %boogie /printSplit:- /errorTrace:0 "%s" > "%t" // RUN: %diff "%s.expect" "%t" procedure Ex() returns (y: int) diff --git a/Test/implementationDivision/focus/focus.bpl.expect b/Test/implementationDivision/focus/focus.bpl.expect index 2c28a1a35..4b7fd678d 100644 --- a/Test/implementationDivision/focus/focus.bpl.expect +++ b/Test/implementationDivision/focus/focus.bpl.expect @@ -1,3 +1,154 @@ -focus.bpl(15,5): Error: this assertion could not be proved +implementation Ex() returns (y: int) +{ -Boogie program verifier finished with 1 verified, 1 error + anon0: + assert 5 + 0 == 5; + assert 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + assume {:partition} 0 >= x#AT#0; + assume y#AT#2 == y#AT#0; + assert y#AT#2 >= 0; + return; +} + + +implementation Ex/split@15() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + assume {:partition} 0 >= x#AT#0; + assume y#AT#2 == y#AT#0; + assume y#AT#2 >= 0; + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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) > 16; + assert {:focus} (x#AT#1 + y#AT#0) * (x#AT#1 + y#AT#0) > 25; + assume y#AT#1 == y#AT#0 + 1; + assume {:partition} 3 <= x#AT#1; + assert 2 < 2; + goto ; +} + + +implementation Ex/assert@4[25,14]() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + assume {:partition} 0 >= x#AT#0; + assume y#AT#2 == y#AT#0; + assume y#AT#2 >= 0; + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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) > 16; + assume (x#AT#1 + y#AT#0) * (x#AT#1 + y#AT#0) > 25; + assume y#AT#1 == y#AT#0 + 1; + assume {:partition} 3 <= x#AT#1; + assume 2 < 2; + assert {:focus} (x#AT#1 + y#AT#1) * (x#AT#1 + y#AT#1) == 25; + assert x#AT#1 + y#AT#1 == 5; + assert x#AT#1 * x#AT#1 <= 25; + assume false; + assume y#AT#2 == y#AT#1; + return; +} + + +implementation Ex/assert@4[19,14]() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + assume {:partition} 0 >= x#AT#0; + assume y#AT#2 == y#AT#0; + assume y#AT#2 >= 0; + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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) > 16; + assume (x#AT#1 + y#AT#0) * (x#AT#1 + y#AT#0) > 25; + assume y#AT#1 == y#AT#0 + 1; + assume {:partition} x#AT#1 < 3; + assert 2 < 2; + assert {:focus} y#AT#1 * y#AT#1 > 4; + goto ; +} + + +implementation Ex/assert@4[25,19,14]() returns (y: int) +{ + + anon0: + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + assume {:partition} 0 >= x#AT#0; + assume y#AT#2 == y#AT#0; + assume y#AT#2 >= 0; + assume 5 + 0 == 5; + assume 5 * 5 <= 25; + assume x#AT#0 + y#AT#0 == 5; + assume x#AT#0 * x#AT#0 <= 25; + 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) > 16; + assume (x#AT#1 + y#AT#0) * (x#AT#1 + y#AT#0) > 25; + assume y#AT#1 == y#AT#0 + 1; + assume {:partition} x#AT#1 < 3; + assume 2 < 2; + assume y#AT#1 * y#AT#1 > 4; + assert {:focus} (x#AT#1 + y#AT#1) * (x#AT#1 + y#AT#1) == 25; + assert x#AT#1 + y#AT#1 == 5; + assert x#AT#1 * x#AT#1 <= 25; + assume false; + assume y#AT#2 == y#AT#1; + return; +} + + +implementation focusInconsistency(x: int) returns (y: int) +{ + + anon0: + assume {:where i} false && true; + goto ; +} + + +implementation focusInconsistency/assert@29[38](x: int) returns (y: int) +{ + + anon0: + assume {:where i} false && true; + assume {:partition} 0 >= x; + assume {:focus} true; + assert false; + return; +} + + + +Boogie program verifier finished with 2 verified, 0 errors From 54535419ce28d0fb1126f757468a3f58dc13489b Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 10:12:41 +0200 Subject: [PATCH 59/70] Extract Source/Core/AST/BigBlocksResolutionContext.cs into a separate file, and IfCmd as well --- Source/Concurrency/LinearTypeChecker.cs | 10 +- Source/Core/AST/AbsyCmd.cs | 693 +----------------- Source/Core/AST/BigBlocksResolutionContext.cs | 599 +++++++++++++++ Source/Core/AST/IfCmd.cs | 106 +++ 4 files changed, 711 insertions(+), 697 deletions(-) create mode 100644 Source/Core/AST/BigBlocksResolutionContext.cs create mode 100644 Source/Core/AST/IfCmd.cs diff --git a/Source/Concurrency/LinearTypeChecker.cs b/Source/Concurrency/LinearTypeChecker.cs index 2e3fb6aa8..848c82dfe 100644 --- a/Source/Concurrency/LinearTypeChecker.cs +++ b/Source/Concurrency/LinearTypeChecker.cs @@ -773,14 +773,14 @@ void ProcessIfCmd(IfCmd ifCmd) { checkingContext.Error(ifCmd.tok, "access to linear store not allowed"); } - stmtLists.Push(ifCmd.thn); - if (ifCmd.elseIf != null) + stmtLists.Push(ifCmd.Thn); + if (ifCmd.ElseIf != null) { - ProcessIfCmd(ifCmd.elseIf); + ProcessIfCmd(ifCmd.ElseIf); } - else if (ifCmd.elseBlock != null) + else if (ifCmd.ElseBlock != null) { - stmtLists.Push(ifCmd.elseBlock); + stmtLists.Push(ifCmd.ElseBlock); } } ProcessIfCmd(ifCmd); diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index a6400e232..5a4d6d493 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -350,593 +350,7 @@ public void AddLocalVariable(string name) // TODO } } - - class BigBlocksResolutionContext - { - StmtList /*!*/ - stmtList; - - [Peer] List blocks; - - string /*!*/ - prefix = "anon"; - - int anon = 0; - - int FreshAnon() - { - return anon++; - } - - HashSet allLabels = new HashSet(); - - Errors /*!*/ - errorHandler; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(stmtList != null); - Contract.Invariant(cce.NonNullElements(blocks, true)); - Contract.Invariant(prefix != null); - Contract.Invariant(cce.NonNullElements(allLabels, true)); - Contract.Invariant(errorHandler != null); - } - - private void ComputeAllLabels(StmtList stmts) - { - if (stmts == null) - { - return; - } - - foreach (BigBlock bb in stmts.BigBlocks) - { - if (bb.LabelName != null) - { - allLabels.Add(bb.LabelName); - } - - ComputeAllLabels(bb.ec); - } - } - - private void ComputeAllLabels(StructuredCmd cmd) - { - if (cmd == null) - { - return; - } - - if (cmd is IfCmd) - { - IfCmd ifCmd = (IfCmd) cmd; - ComputeAllLabels(ifCmd.thn); - ComputeAllLabels(ifCmd.elseIf); - ComputeAllLabels(ifCmd.elseBlock); - } - else if (cmd is WhileCmd) - { - WhileCmd whileCmd = (WhileCmd) cmd; - ComputeAllLabels(whileCmd.Body); - } - } - - public BigBlocksResolutionContext(StmtList stmtList, Errors errorHandler) - { - Contract.Requires(errorHandler != null); - Contract.Requires(stmtList != null); - this.stmtList = stmtList; - // Inject an empty big block at the end of the body of a while loop if its current end is another while loop. - // This transformation creates a suitable jump target for break statements inside the nested while loop. - InjectEmptyBigBlockInsideWhileLoopBody(stmtList); - this.errorHandler = errorHandler; - ComputeAllLabels(stmtList); - } - - public List /*!*/ Blocks - { - get - { - Contract.Ensures(cce.NonNullElements(Contract.Result>())); - if (blocks == null) - { - blocks = new List(); - - int startErrorCount = this.errorHandler.count; - // Check that all goto statements go to a label in allLabels, and no break statement to a non-enclosing loop. - // Also, determine a good value for "prefix". - CheckLegalLabels(stmtList, null, null); - - // fill in names of anonymous blocks - NameAnonymousBlocks(stmtList); - - // determine successor blocks - RecordSuccessors(stmtList, null); - - if (this.errorHandler.count == startErrorCount) - { - // generate blocks from the big blocks - CreateBlocks(stmtList, null); - } - } - - return blocks; - } - } - - void InjectEmptyBigBlockInsideWhileLoopBody(StmtList stmtList) - { - foreach (var bb in stmtList.BigBlocks) - { - InjectEmptyBigBlockInsideWhileLoopBody(bb.ec); - } - } - - void InjectEmptyBigBlockInsideWhileLoopBody(StructuredCmd structuredCmd) - { - if (structuredCmd is WhileCmd whileCmd) - { - InjectEmptyBigBlockInsideWhileLoopBody(whileCmd.Body); - if (whileCmd.Body.BigBlocks.Count > 0 && whileCmd.Body.BigBlocks.Last().ec is WhileCmd) - { - var newBigBlocks = new List(whileCmd.Body.BigBlocks); - newBigBlocks.Add(new BigBlock(Token.NoToken, null, new List(), null, null)); - whileCmd.Body = new StmtList(newBigBlocks, whileCmd.Body.EndCurly); - } - } - else if (structuredCmd is IfCmd ifCmd) - { - InjectEmptyBigBlockInsideWhileLoopBody(ifCmd.thn); - if (ifCmd.elseBlock != null) - { - InjectEmptyBigBlockInsideWhileLoopBody(ifCmd.elseBlock); - } - - if (ifCmd.elseIf != null) - { - InjectEmptyBigBlockInsideWhileLoopBody(ifCmd.elseIf); - } - } - } - - void CheckLegalLabels(StmtList stmtList, StmtList parentContext, BigBlock parentBigBlock) - { - Contract.Requires(stmtList != null); - Contract.Requires((parentContext == null) == (parentBigBlock == null)); - Contract.Requires(stmtList.ParentContext == null); // it hasn't been set yet - //modifies stmtList.*; - Contract.Ensures(stmtList.ParentContext == parentContext); - stmtList.ParentContext = parentContext; - stmtList.ParentBigBlock = parentBigBlock; - - // record the labels declared in this StmtList - foreach (BigBlock b in stmtList.BigBlocks) - { - if (b.LabelName != null) - { - string n = b.LabelName; - if (n.StartsWith(prefix)) - { - if (prefix.Length < n.Length && n[prefix.Length] == '0') - { - prefix += "1"; - } - else - { - prefix += "0"; - } - } - - stmtList.AddLabel(b.LabelName); - } - } - - // check that labels in this and nested StmtList's are legal - foreach (BigBlock b in stmtList.BigBlocks) - { - // goto's must reference blocks in enclosing blocks - if (b.tc is GotoCmd) - { - GotoCmd g = (GotoCmd) b.tc; - foreach (string /*!*/ lbl in cce.NonNull(g.LabelNames)) - { - Contract.Assert(lbl != null); - /* - bool found = false; - for (StmtList sl = stmtList; sl != null; sl = sl.ParentContext) { - if (sl.Labels.Contains(lbl)) { - found = true; - break; - } - } - if (!found) { - this.errorHandler.SemErr(g.tok, "Error: goto label '" + lbl + "' is undefined or out of reach"); - } - */ - if (!allLabels.Contains(lbl)) - { - this.errorHandler.SemErr(g.tok, "Error: goto label '" + lbl + "' is undefined"); - } - } - } - - // break labels must refer to an enclosing while statement - else if (b.ec is BreakCmd) - { - BreakCmd bcmd = (BreakCmd) b.ec; - Contract.Assert(bcmd.BreakEnclosure == null); // it hasn't been initialized yet - bool found = false; - for (StmtList sl = stmtList; sl.ParentBigBlock != null; sl = sl.ParentContext) - { - cce.LoopInvariant(sl != null); - BigBlock bb = sl.ParentBigBlock; - - if (bcmd.Label == null) - { - // a label-less break statement breaks out of the innermost enclosing while statement - if (bb.ec is WhileCmd) - { - bcmd.BreakEnclosure = bb; - found = true; - break; - } - } - else if (bcmd.Label == bb.LabelName) - { - // a break statement with a label can break out of both if statements and while statements - if (bb.simpleCmds.Count == 0) - { - // this is a good target: the label refers to the if/while statement - bcmd.BreakEnclosure = bb; - } - else - { - // the label of bb refers to the first statement of bb, which in which case is a simple statement, not an if/while statement - this.errorHandler.SemErr(bcmd.tok, - "Error: break label '" + bcmd.Label + "' must designate an enclosing statement"); - } - - found = true; // don't look any further, since we've found a matching label - break; - } - } - - if (!found) - { - if (bcmd.Label == null) - { - this.errorHandler.SemErr(bcmd.tok, "Error: break statement is not inside a loop"); - } - else - { - this.errorHandler.SemErr(bcmd.tok, - "Error: break label '" + bcmd.Label + "' must designate an enclosing statement"); - } - } - } - - // recurse - else if (b.ec is WhileCmd) - { - WhileCmd wcmd = (WhileCmd) b.ec; - CheckLegalLabels(wcmd.Body, stmtList, b); - } - else - { - for (IfCmd ifcmd = b.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.elseIf) - { - CheckLegalLabels(ifcmd.thn, stmtList, b); - if (ifcmd.elseBlock != null) - { - CheckLegalLabels(ifcmd.elseBlock, stmtList, b); - } - } - } - } - } - - void NameAnonymousBlocks(StmtList stmtList) - { - Contract.Requires(stmtList != null); - foreach (BigBlock b in stmtList.BigBlocks) - { - if (b.LabelName == null) - { - b.LabelName = prefix + FreshAnon(); - } - - if (b.ec is WhileCmd) - { - WhileCmd wcmd = (WhileCmd) b.ec; - NameAnonymousBlocks(wcmd.Body); - } - else - { - for (IfCmd ifcmd = b.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.elseIf) - { - NameAnonymousBlocks(ifcmd.thn); - if (ifcmd.elseBlock != null) - { - NameAnonymousBlocks(ifcmd.elseBlock); - } - } - } - } - } - - void RecordSuccessors(StmtList stmtList, BigBlock successor) - { - Contract.Requires(stmtList != null); - for (int i = stmtList.BigBlocks.Count; 0 <= --i;) - { - BigBlock big = stmtList.BigBlocks[i]; - big.successorBigBlock = successor; - - if (big.ec is WhileCmd) - { - WhileCmd wcmd = (WhileCmd) big.ec; - RecordSuccessors(wcmd.Body, big); - } - else - { - for (IfCmd ifcmd = big.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.elseIf) - { - RecordSuccessors(ifcmd.thn, successor); - if (ifcmd.elseBlock != null) - { - RecordSuccessors(ifcmd.elseBlock, successor); - } - } - } - - successor = big; - } - } - - // If the enclosing context is a loop, then "runOffTheEndLabel" is the loop head label; - // otherwise, it is null. - void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) - { - Contract.Requires(stmtList != null); - Contract.Requires(blocks != null); - List cmdPrefixToApply = stmtList.PrefixCommands; - - int n = stmtList.BigBlocks.Count; - foreach (BigBlock b in stmtList.BigBlocks) - { - n--; - Contract.Assert(b.LabelName != null); - List theSimpleCmds; - if (cmdPrefixToApply == null) - { - theSimpleCmds = b.simpleCmds; - } - else - { - theSimpleCmds = new List(); - theSimpleCmds.AddRange(cmdPrefixToApply); - theSimpleCmds.AddRange(b.simpleCmds); - cmdPrefixToApply = null; // now, we've used 'em up - } - - if (b.tc != null) - { - // this BigBlock has the very same components as a Block - Contract.Assert(b.ec == null); - Block block = new Block(b.tok, b.LabelName, theSimpleCmds, b.tc); - blocks.Add(block); - } - else if (b.ec == null) - { - TransferCmd trCmd; - if (n == 0 && runOffTheEndLabel != null) - { - // goto the given label instead of the textual successor block - trCmd = new GotoCmd(stmtList.EndCurly, new List {runOffTheEndLabel}); - } - else - { - trCmd = GotoSuccessor(stmtList.EndCurly, b); - } - - Block block = new Block(b.tok, b.LabelName, theSimpleCmds, trCmd); - blocks.Add(block); - } - else if (b.ec is BreakCmd) - { - BreakCmd bcmd = (BreakCmd) b.ec; - Contract.Assert(bcmd.BreakEnclosure != null); - Block block = new Block(b.tok, b.LabelName, theSimpleCmds, GotoSuccessor(b.ec.tok, bcmd.BreakEnclosure)); - blocks.Add(block); - } - else if (b.ec is WhileCmd) - { - WhileCmd wcmd = (WhileCmd) b.ec; - var a = FreshAnon(); - string loopHeadLabel = prefix + a + "_LoopHead"; - string /*!*/ - loopBodyLabel = prefix + a + "_LoopBody"; - string loopDoneLabel = prefix + a + "_LoopDone"; - - List ssBody = new List(); - List ssDone = new List(); - if (wcmd.Guard != null) - { - var ac = new AssumeCmd(wcmd.tok, wcmd.Guard); - ac.Attributes = new QKeyValue(wcmd.tok, "partition", new List(), null); - ssBody.Add(ac); - - ac = new AssumeCmd(wcmd.tok, Expr.Not(wcmd.Guard)); - ac.Attributes = new QKeyValue(wcmd.tok, "partition", new List(), null); - ssDone.Add(ac); - } - - // Try to squeeze in ssBody into the first block of wcmd.Body - bool bodyGuardTakenCareOf = wcmd.Body.PrefixFirstBlock(ssBody, ref loopBodyLabel); - - // ... goto LoopHead; - Block block = new Block(b.tok, b.LabelName, theSimpleCmds, - new GotoCmd(wcmd.tok, new List {loopHeadLabel})); - blocks.Add(block); - - // LoopHead: assert/assume loop_invariant; goto LoopDone, LoopBody; - List ssHead = new List(); - foreach (CallCmd yield in wcmd.Yields) - { - ssHead.Add(yield); - } - foreach (PredicateCmd inv in wcmd.Invariants) - { - ssHead.Add(inv); - } - - block = new Block(wcmd.tok, loopHeadLabel, ssHead, - new GotoCmd(wcmd.tok, new List {loopDoneLabel, loopBodyLabel})); - blocks.Add(block); - - if (!bodyGuardTakenCareOf) - { - // LoopBody: assume guard; goto firstLoopBlock; - block = new Block(wcmd.tok, loopBodyLabel, ssBody, - new GotoCmd(wcmd.tok, new List {wcmd.Body.BigBlocks[0].LabelName})); - blocks.Add(block); - } - - // recurse to create the blocks for the loop body - CreateBlocks(wcmd.Body, loopHeadLabel); - - // LoopDone: assume !guard; goto loopSuccessor; - TransferCmd trCmd; - if (n == 0 && runOffTheEndLabel != null) - { - // goto the given label instead of the textual successor block - trCmd = new GotoCmd(wcmd.tok, new List {runOffTheEndLabel}); - } - else - { - trCmd = GotoSuccessor(wcmd.tok, b); - } - - block = new Block(wcmd.tok, loopDoneLabel, ssDone, trCmd); - blocks.Add(block); - } - else - { - IfCmd ifcmd = (IfCmd) b.ec; - string predLabel = b.LabelName; - List predCmds = theSimpleCmds; - - for (; ifcmd != null; ifcmd = ifcmd.elseIf) - { - var a = FreshAnon(); - string thenLabel = prefix + a + "_Then"; - Contract.Assert(thenLabel != null); - string elseLabel = prefix + a + "_Else"; - Contract.Assert(elseLabel != null); - - List ssThen = new List(); - List ssElse = new List(); - if (ifcmd.Guard != null) - { - var ac = new AssumeCmd(ifcmd.tok, ifcmd.Guard); - ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); - ssThen.Add(ac); - - ac = new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard)); - ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); - ssElse.Add(ac); - } - - // Try to squeeze in ssThen/ssElse into the first block of ifcmd.thn/ifcmd.elseBlock - bool thenGuardTakenCareOf = ifcmd.thn.PrefixFirstBlock(ssThen, ref thenLabel); - bool elseGuardTakenCareOf = false; - if (ifcmd.elseBlock != null) - { - elseGuardTakenCareOf = ifcmd.elseBlock.PrefixFirstBlock(ssElse, ref elseLabel); - } - - // ... goto Then, Else; - var jumpBlock = new Block(b.tok, predLabel, predCmds, - new GotoCmd(ifcmd.tok, new List {thenLabel, elseLabel})); - blocks.Add(jumpBlock); - - if (!thenGuardTakenCareOf) - { - // Then: assume guard; goto firstThenBlock; - var thenJumpBlock = new Block(ifcmd.tok, thenLabel, ssThen, - new GotoCmd(ifcmd.tok, new List {ifcmd.thn.BigBlocks[0].LabelName})); - blocks.Add(thenJumpBlock); - } - - // recurse to create the blocks for the then branch - CreateBlocks(ifcmd.thn, n == 0 ? runOffTheEndLabel : null); - - if (ifcmd.elseBlock != null) - { - Contract.Assert(ifcmd.elseIf == null); - if (!elseGuardTakenCareOf) - { - // Else: assume !guard; goto firstElseBlock; - var elseJumpBlock = new Block(ifcmd.tok, elseLabel, ssElse, - new GotoCmd(ifcmd.tok, new List {ifcmd.elseBlock.BigBlocks[0].LabelName})); - blocks.Add(elseJumpBlock); - } - - // recurse to create the blocks for the else branch - CreateBlocks(ifcmd.elseBlock, n == 0 ? runOffTheEndLabel : null); - } - else if (ifcmd.elseIf != null) - { - // this is an "else if" - predLabel = elseLabel; - predCmds = new List(); - if (ifcmd.Guard != null) - { - var ac = new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard)); - ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); - predCmds.Add(ac); - } - } - else - { - // no else alternative is specified, so else branch is just "skip" - // Else: assume !guard; goto ifSuccessor; - TransferCmd trCmd; - if (n == 0 && runOffTheEndLabel != null) - { - // goto the given label instead of the textual successor block - trCmd = new GotoCmd(ifcmd.tok, new List {runOffTheEndLabel}); - } - else - { - trCmd = GotoSuccessor(ifcmd.tok, b); - } - - var block = new Block(ifcmd.tok, elseLabel, ssElse, trCmd); - blocks.Add(block); - } - } - } - } - } - - TransferCmd GotoSuccessor(IToken tok, BigBlock b) - { - Contract.Requires(b != null); - Contract.Requires(tok != null); - Contract.Ensures(Contract.Result() != null); - if (b.successorBigBlock != null) - { - return new GotoCmd(tok, new List {b.successorBigBlock.LabelName}); - } - else - { - return new ReturnCmd(tok); - } - } - } - + [ContractClass(typeof(StructuredCmdContracts))] public abstract class StructuredCmd { @@ -986,111 +400,6 @@ public StructuredCmdContracts() : base(null) } } - public class IfCmd : StructuredCmd - { - public Expr Guard; - - private StmtList /*!*/ - _thn; - - public StmtList /*!*/ thn - { - get - { - Contract.Ensures(Contract.Result() != null); - return this._thn; - } - set - { - Contract.Requires(value != null); - this._thn = value; - } - } - - private IfCmd _elseIf; - - public IfCmd elseIf - { - get { return this._elseIf; } - set - { - Contract.Requires(value == null || this.elseBlock == null); - this._elseIf = value; - } - } - - private StmtList _elseBlock; - - public StmtList elseBlock - { - get { return this._elseBlock; } - set - { - Contract.Requires(value == null || this.elseIf == null); - this._elseBlock = value; - } - } - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(this._thn != null); - Contract.Invariant(this._elseIf == null || this._elseBlock == null); - } - - public IfCmd(IToken /*!*/ tok, Expr guard, StmtList /*!*/ thn, IfCmd elseIf, StmtList elseBlock) - : base(tok) - { - Contract.Requires(tok != null); - Contract.Requires(thn != null); - Contract.Requires(elseIf == null || elseBlock == null); - this.Guard = guard; - this._thn = thn; - this._elseIf = elseIf; - this._elseBlock = elseBlock; - } - - public override void Emit(TokenTextWriter stream, int level) - { - stream.Write(level, "if ("); - IfCmd /*!*/ - ifcmd = this; - while (true) - { - if (ifcmd.Guard == null) - { - stream.Write("*"); - } - else - { - ifcmd.Guard.Emit(stream); - } - - stream.WriteLine(")"); - - stream.WriteLine(level, "{"); - ifcmd.thn.Emit(stream, level + 1); - stream.WriteLine(level, "}"); - - if (ifcmd.elseIf != null) - { - stream.Write(level, "else if ("); - ifcmd = ifcmd.elseIf; - continue; - } - else if (ifcmd.elseBlock != null) - { - stream.WriteLine(level, "else"); - stream.WriteLine(level, "{"); - ifcmd.elseBlock.Emit(stream, level + 1); - stream.WriteLine(level, "}"); - } - - break; - } - } - } - public class WhileCmd : StructuredCmd { [Peer] public Expr Guard; diff --git a/Source/Core/AST/BigBlocksResolutionContext.cs b/Source/Core/AST/BigBlocksResolutionContext.cs new file mode 100644 index 000000000..4e66238e2 --- /dev/null +++ b/Source/Core/AST/BigBlocksResolutionContext.cs @@ -0,0 +1,599 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; + +namespace Microsoft.Boogie; + +class BigBlocksResolutionContext +{ + StmtList /*!*/ + stmtList; + + [Peer] List blocks; + + string /*!*/ + prefix = "anon"; + + int anon = 0; + + int FreshAnon() + { + return anon++; + } + + private readonly HashSet allLabels = new(); + + private readonly Errors /*!*/ errorHandler; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(stmtList != null); + Contract.Invariant(cce.NonNullElements(blocks, true)); + Contract.Invariant(prefix != null); + Contract.Invariant(cce.NonNullElements(allLabels, true)); + Contract.Invariant(errorHandler != null); + } + + private void ComputeAllLabels(StmtList stmts) + { + if (stmts == null) + { + return; + } + + foreach (BigBlock bb in stmts.BigBlocks) + { + if (bb.LabelName != null) + { + allLabels.Add(bb.LabelName); + } + + ComputeAllLabels(bb.ec); + } + } + + private void ComputeAllLabels(StructuredCmd cmd) + { + if (cmd == null) + { + return; + } + + if (cmd is IfCmd) + { + IfCmd ifCmd = (IfCmd) cmd; + ComputeAllLabels(ifCmd.Thn); + ComputeAllLabels(ifCmd.ElseIf); + ComputeAllLabels(ifCmd.ElseBlock); + } + else if (cmd is WhileCmd) + { + WhileCmd whileCmd = (WhileCmd) cmd; + ComputeAllLabels(whileCmd.Body); + } + } + + public BigBlocksResolutionContext(StmtList stmtList, Errors errorHandler) + { + Contract.Requires(errorHandler != null); + Contract.Requires(stmtList != null); + this.stmtList = stmtList; + // Inject an empty big block at the end of the body of a while loop if its current end is another while loop. + // This transformation creates a suitable jump target for break statements inside the nested while loop. + InjectEmptyBigBlockInsideWhileLoopBody(stmtList); + this.errorHandler = errorHandler; + ComputeAllLabels(stmtList); + } + + public List /*!*/ Blocks + { + get + { + Contract.Ensures(cce.NonNullElements(Contract.Result>())); + if (blocks == null) + { + blocks = new List(); + + int startErrorCount = this.errorHandler.count; + // Check that all goto statements go to a label in allLabels, and no break statement to a non-enclosing loop. + // Also, determine a good value for "prefix". + CheckLegalLabels(stmtList, null, null); + + // fill in names of anonymous blocks + NameAnonymousBlocks(stmtList); + + // determine successor blocks + RecordSuccessors(stmtList, null); + + if (this.errorHandler.count == startErrorCount) + { + // generate blocks from the big blocks + CreateBlocks(stmtList, null); + } + } + + return blocks; + } + } + + void InjectEmptyBigBlockInsideWhileLoopBody(StmtList stmtList) + { + foreach (var bb in stmtList.BigBlocks) + { + InjectEmptyBigBlockInsideWhileLoopBody(bb.ec); + } + } + + void InjectEmptyBigBlockInsideWhileLoopBody(StructuredCmd structuredCmd) + { + if (structuredCmd is WhileCmd whileCmd) + { + InjectEmptyBigBlockInsideWhileLoopBody(whileCmd.Body); + if (whileCmd.Body.BigBlocks.Count > 0 && whileCmd.Body.BigBlocks.Last().ec is WhileCmd) + { + var newBigBlocks = new List(whileCmd.Body.BigBlocks); + newBigBlocks.Add(new BigBlock(Token.NoToken, null, new List(), null, null)); + whileCmd.Body = new StmtList(newBigBlocks, whileCmd.Body.EndCurly); + } + } + else if (structuredCmd is IfCmd ifCmd) + { + InjectEmptyBigBlockInsideWhileLoopBody(ifCmd.Thn); + if (ifCmd.ElseBlock != null) + { + InjectEmptyBigBlockInsideWhileLoopBody(ifCmd.ElseBlock); + } + + if (ifCmd.ElseIf != null) + { + InjectEmptyBigBlockInsideWhileLoopBody(ifCmd.ElseIf); + } + } + } + + void CheckLegalLabels(StmtList stmtList, StmtList parentContext, BigBlock parentBigBlock) + { + Contract.Requires(stmtList != null); + Contract.Requires((parentContext == null) == (parentBigBlock == null)); + Contract.Requires(stmtList.ParentContext == null); // it hasn't been set yet + //modifies stmtList.*; + Contract.Ensures(stmtList.ParentContext == parentContext); + stmtList.ParentContext = parentContext; + stmtList.ParentBigBlock = parentBigBlock; + + // record the labels declared in this StmtList + foreach (BigBlock b in stmtList.BigBlocks) + { + if (b.LabelName != null) + { + string n = b.LabelName; + if (n.StartsWith(prefix)) + { + if (prefix.Length < n.Length && n[prefix.Length] == '0') + { + prefix += "1"; + } + else + { + prefix += "0"; + } + } + + stmtList.AddLabel(b.LabelName); + } + } + + // check that labels in this and nested StmtList's are legal + foreach (BigBlock b in stmtList.BigBlocks) + { + // goto's must reference blocks in enclosing blocks + if (b.tc is GotoCmd) + { + GotoCmd g = (GotoCmd) b.tc; + foreach (string /*!*/ lbl in cce.NonNull(g.LabelNames)) + { + Contract.Assert(lbl != null); + /* + bool found = false; + for (StmtList sl = stmtList; sl != null; sl = sl.ParentContext) { + if (sl.Labels.Contains(lbl)) { + found = true; + break; + } + } + if (!found) { + this.errorHandler.SemErr(g.tok, "Error: goto label '" + lbl + "' is undefined or out of reach"); + } + */ + if (!allLabels.Contains(lbl)) + { + this.errorHandler.SemErr(g.tok, "Error: goto label '" + lbl + "' is undefined"); + } + } + } + + // break labels must refer to an enclosing while statement + else if (b.ec is BreakCmd) + { + BreakCmd bcmd = (BreakCmd) b.ec; + Contract.Assert(bcmd.BreakEnclosure == null); // it hasn't been initialized yet + bool found = false; + for (StmtList sl = stmtList; sl.ParentBigBlock != null; sl = sl.ParentContext) + { + cce.LoopInvariant(sl != null); + BigBlock bb = sl.ParentBigBlock; + + if (bcmd.Label == null) + { + // a label-less break statement breaks out of the innermost enclosing while statement + if (bb.ec is WhileCmd) + { + bcmd.BreakEnclosure = bb; + found = true; + break; + } + } + else if (bcmd.Label == bb.LabelName) + { + // a break statement with a label can break out of both if statements and while statements + if (bb.simpleCmds.Count == 0) + { + // this is a good target: the label refers to the if/while statement + bcmd.BreakEnclosure = bb; + } + else + { + // the label of bb refers to the first statement of bb, which in which case is a simple statement, not an if/while statement + this.errorHandler.SemErr(bcmd.tok, + "Error: break label '" + bcmd.Label + "' must designate an enclosing statement"); + } + + found = true; // don't look any further, since we've found a matching label + break; + } + } + + if (!found) + { + if (bcmd.Label == null) + { + this.errorHandler.SemErr(bcmd.tok, "Error: break statement is not inside a loop"); + } + else + { + this.errorHandler.SemErr(bcmd.tok, + "Error: break label '" + bcmd.Label + "' must designate an enclosing statement"); + } + } + } + + // recurse + else if (b.ec is WhileCmd) + { + WhileCmd wcmd = (WhileCmd) b.ec; + CheckLegalLabels(wcmd.Body, stmtList, b); + } + else + { + for (IfCmd ifcmd = b.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.ElseIf) + { + CheckLegalLabels(ifcmd.Thn, stmtList, b); + if (ifcmd.ElseBlock != null) + { + CheckLegalLabels(ifcmd.ElseBlock, stmtList, b); + } + } + } + } + } + + void NameAnonymousBlocks(StmtList stmtList) + { + Contract.Requires(stmtList != null); + foreach (BigBlock b in stmtList.BigBlocks) + { + if (b.LabelName == null) + { + b.LabelName = prefix + FreshAnon(); + } + + if (b.ec is WhileCmd) + { + WhileCmd wcmd = (WhileCmd) b.ec; + NameAnonymousBlocks(wcmd.Body); + } + else + { + for (IfCmd ifcmd = b.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.ElseIf) + { + NameAnonymousBlocks(ifcmd.Thn); + if (ifcmd.ElseBlock != null) + { + NameAnonymousBlocks(ifcmd.ElseBlock); + } + } + } + } + } + + void RecordSuccessors(StmtList stmtList, BigBlock successor) + { + Contract.Requires(stmtList != null); + for (int i = stmtList.BigBlocks.Count; 0 <= --i;) + { + BigBlock big = stmtList.BigBlocks[i]; + big.successorBigBlock = successor; + + if (big.ec is WhileCmd) + { + WhileCmd wcmd = (WhileCmd) big.ec; + RecordSuccessors(wcmd.Body, big); + } + else + { + for (IfCmd ifcmd = big.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.ElseIf) + { + RecordSuccessors(ifcmd.Thn, successor); + if (ifcmd.ElseBlock != null) + { + RecordSuccessors(ifcmd.ElseBlock, successor); + } + } + } + + successor = big; + } + } + + // If the enclosing context is a loop, then "runOffTheEndLabel" is the loop head label; + // otherwise, it is null. + private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) + { + Contract.Requires(stmtList != null); + Contract.Requires(blocks != null); + List cmdPrefixToApply = stmtList.PrefixCommands; + + int n = stmtList.BigBlocks.Count; + foreach (var bigBlock in stmtList.BigBlocks) + { + n--; + Contract.Assert(bigBlock.LabelName != null); + List theSimpleCmds; + if (cmdPrefixToApply == null) + { + theSimpleCmds = bigBlock.simpleCmds; + } + else + { + theSimpleCmds = new List(); + theSimpleCmds.AddRange(cmdPrefixToApply); + theSimpleCmds.AddRange(bigBlock.simpleCmds); + cmdPrefixToApply = null; // now, we've used 'em up + } + + if (bigBlock.tc != null) + { + // this BigBlock has the very same components as a Block + Contract.Assert(bigBlock.ec == null); + Block block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, bigBlock.tc); + blocks.Add(block); + } + else + { + switch (bigBlock.ec) + { + case null: + { + TransferCmd trCmd; + if (n == 0 && runOffTheEndLabel != null) + { + // goto the given label instead of the textual successor block + trCmd = new GotoCmd(stmtList.EndCurly, new List {runOffTheEndLabel}); + } + else + { + trCmd = GotoSuccessor(stmtList.EndCurly, bigBlock); + } + + Block block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, trCmd); + blocks.Add(block); + break; + } + case BreakCmd ec: + { + Contract.Assert(ec.BreakEnclosure != null); + Block block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, GotoSuccessor(ec.tok, ec.BreakEnclosure)); + blocks.Add(block); + break; + } + case WhileCmd ec: + { + var a = FreshAnon(); + string loopHeadLabel = prefix + a + "_LoopHead"; + string /*!*/ + loopBodyLabel = prefix + a + "_LoopBody"; + string loopDoneLabel = prefix + a + "_LoopDone"; + + List ssBody = new List(); + List ssDone = new List(); + if (ec.Guard != null) + { + var ac = new AssumeCmd(ec.tok, ec.Guard); + ac.Attributes = new QKeyValue(ec.tok, "partition", new List(), null); + ssBody.Add(ac); + + ac = new AssumeCmd(ec.tok, Expr.Not(ec.Guard)); + ac.Attributes = new QKeyValue(ec.tok, "partition", new List(), null); + ssDone.Add(ac); + } + + // Try to squeeze in ssBody into the first block of wcmd.Body + bool bodyGuardTakenCareOf = ec.Body.PrefixFirstBlock(ssBody, ref loopBodyLabel); + + // ... goto LoopHead; + Block block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, + new GotoCmd(ec.tok, new List {loopHeadLabel})); + blocks.Add(block); + + // LoopHead: assert/assume loop_invariant; goto LoopDone, LoopBody; + List ssHead = new List(); + foreach (CallCmd yield in ec.Yields) + { + ssHead.Add(yield); + } + foreach (PredicateCmd inv in ec.Invariants) + { + ssHead.Add(inv); + } + + block = new Block(ec.tok, loopHeadLabel, ssHead, + new GotoCmd(ec.tok, new List {loopDoneLabel, loopBodyLabel})); + blocks.Add(block); + + if (!bodyGuardTakenCareOf) + { + // LoopBody: assume guard; goto firstLoopBlock; + block = new Block(ec.tok, loopBodyLabel, ssBody, + new GotoCmd(ec.tok, new List {ec.Body.BigBlocks[0].LabelName})); + blocks.Add(block); + } + + // recurse to create the blocks for the loop body + CreateBlocks(ec.Body, loopHeadLabel); + + // LoopDone: assume !guard; goto loopSuccessor; + TransferCmd trCmd; + if (n == 0 && runOffTheEndLabel != null) + { + // goto the given label instead of the textual successor block + trCmd = new GotoCmd(ec.tok, new List {runOffTheEndLabel}); + } + else + { + trCmd = GotoSuccessor(ec.tok, bigBlock); + } + + block = new Block(ec.tok, loopDoneLabel, ssDone, trCmd); + blocks.Add(block); + break; + } + case IfCmd ifcmd: + { + string predLabel = bigBlock.LabelName; + List predCmds = theSimpleCmds; + + for (; ifcmd != null; ifcmd = ifcmd.ElseIf) + { + var a = FreshAnon(); + string thenLabel = prefix + a + "_Then"; + Contract.Assert(thenLabel != null); + string elseLabel = prefix + a + "_Else"; + Contract.Assert(elseLabel != null); + + List ssThen = new List(); + List ssElse = new List(); + if (ifcmd.Guard != null) + { + var ac = new AssumeCmd(ifcmd.tok, ifcmd.Guard); + ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); + ssThen.Add(ac); + + ac = new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard)); + ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); + ssElse.Add(ac); + } + + // Try to squeeze in ssThen/ssElse into the first block of ifcmd.thn/ifcmd.elseBlock + bool thenGuardTakenCareOf = ifcmd.Thn.PrefixFirstBlock(ssThen, ref thenLabel); + bool elseGuardTakenCareOf = false; + if (ifcmd.ElseBlock != null) + { + elseGuardTakenCareOf = ifcmd.ElseBlock.PrefixFirstBlock(ssElse, ref elseLabel); + } + + // ... goto Then, Else; + var jumpBlock = new Block(bigBlock.tok, predLabel, predCmds, + new GotoCmd(ifcmd.tok, new List {thenLabel, elseLabel})); + blocks.Add(jumpBlock); + + if (!thenGuardTakenCareOf) + { + // Then: assume guard; goto firstThenBlock; + var thenJumpBlock = new Block(ifcmd.tok, thenLabel, ssThen, + new GotoCmd(ifcmd.tok, new List {ifcmd.Thn.BigBlocks[0].LabelName})); + blocks.Add(thenJumpBlock); + } + + // recurse to create the blocks for the then branch + CreateBlocks(ifcmd.Thn, n == 0 ? runOffTheEndLabel : null); + + if (ifcmd.ElseBlock != null) + { + Contract.Assert(ifcmd.ElseIf == null); + if (!elseGuardTakenCareOf) + { + // Else: assume !guard; goto firstElseBlock; + var elseJumpBlock = new Block(ifcmd.tok, elseLabel, ssElse, + new GotoCmd(ifcmd.tok, new List {ifcmd.ElseBlock.BigBlocks[0].LabelName})); + blocks.Add(elseJumpBlock); + } + + // recurse to create the blocks for the else branch + CreateBlocks(ifcmd.ElseBlock, n == 0 ? runOffTheEndLabel : null); + } + else if (ifcmd.ElseIf != null) + { + // this is an "else if" + predLabel = elseLabel; + predCmds = new List(); + if (ifcmd.Guard != null) + { + var ac = new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard)); + ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); + predCmds.Add(ac); + } + } + else + { + // no else alternative is specified, so else branch is just "skip" + // Else: assume !guard; goto ifSuccessor; + TransferCmd trCmd; + if (n == 0 && runOffTheEndLabel != null) + { + // goto the given label instead of the textual successor block + trCmd = new GotoCmd(ifcmd.tok, new List {runOffTheEndLabel}); + } + else + { + trCmd = GotoSuccessor(ifcmd.tok, bigBlock); + } + + var block = new Block(ifcmd.tok, elseLabel, ssElse, trCmd); + blocks.Add(block); + } + } + + break; + } + } + } + } + } + + TransferCmd GotoSuccessor(IToken tok, BigBlock b) + { + Contract.Requires(b != null); + Contract.Requires(tok != null); + Contract.Ensures(Contract.Result() != null); + if (b.successorBigBlock != null) + { + return new GotoCmd(tok, new List {b.successorBigBlock.LabelName}); + } + else + { + return new ReturnCmd(tok); + } + } +} \ No newline at end of file diff --git a/Source/Core/AST/IfCmd.cs b/Source/Core/AST/IfCmd.cs new file mode 100644 index 000000000..dfbe74009 --- /dev/null +++ b/Source/Core/AST/IfCmd.cs @@ -0,0 +1,106 @@ +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class IfCmd : StructuredCmd +{ + public Expr Guard; + + private StmtList /*!*/ thn; + + public StmtList /*!*/ Thn + { + get + { + Contract.Ensures(Contract.Result() != null); + return this.thn; + } + set + { + Contract.Requires(value != null); + this.thn = value; + } + } + + private IfCmd elseIf; + + public IfCmd ElseIf + { + get { return this.elseIf; } + set + { + Contract.Requires(value == null || this.ElseBlock == null); + this.elseIf = value; + } + } + + private StmtList elseBlock; + + public StmtList ElseBlock + { + get { return this.elseBlock; } + set + { + Contract.Requires(value == null || this.ElseIf == null); + this.elseBlock = value; + } + } + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(this.thn != null); + Contract.Invariant(this.elseIf == null || this.elseBlock == null); + } + + public IfCmd(IToken /*!*/ tok, Expr guard, StmtList /*!*/ thn, IfCmd elseIf, StmtList elseBlock) + : base(tok) + { + Contract.Requires(tok != null); + Contract.Requires(thn != null); + Contract.Requires(elseIf == null || elseBlock == null); + this.Guard = guard; + this.thn = thn; + this.elseIf = elseIf; + this.elseBlock = elseBlock; + } + + public override void Emit(TokenTextWriter stream, int level) + { + stream.Write(level, "if ("); + var /*!*/ ifcmd = this; + while (true) + { + if (ifcmd.Guard == null) + { + stream.Write("*"); + } + else + { + ifcmd.Guard.Emit(stream); + } + + stream.WriteLine(")"); + + stream.WriteLine(level, "{"); + ifcmd.Thn.Emit(stream, level + 1); + stream.WriteLine(level, "}"); + + if (ifcmd.ElseIf != null) + { + stream.Write(level, "else if ("); + ifcmd = ifcmd.ElseIf; + continue; + } + else if (ifcmd.ElseBlock != null) + { + stream.WriteLine(level, "else"); + stream.WriteLine(level, "{"); + ifcmd.ElseBlock.Emit(stream, level + 1); + stream.WriteLine(level, "}"); + } + + break; + } + } +} \ No newline at end of file From eff7523afc87bb19a0cd6a5747bfc875be237e53 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 10:14:29 +0200 Subject: [PATCH 60/70] Change FreshAnon into FreshPrefix --- Source/Core/AST/BigBlocksResolutionContext.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Source/Core/AST/BigBlocksResolutionContext.cs b/Source/Core/AST/BigBlocksResolutionContext.cs index 4e66238e2..62ba00f92 100644 --- a/Source/Core/AST/BigBlocksResolutionContext.cs +++ b/Source/Core/AST/BigBlocksResolutionContext.cs @@ -12,14 +12,13 @@ class BigBlocksResolutionContext [Peer] List blocks; - string /*!*/ - prefix = "anon"; + string /*!*/ prefix = "anon"; int anon = 0; - int FreshAnon() + string FreshPrefix() { - return anon++; + return prefix + anon++; } private readonly HashSet allLabels = new(); @@ -296,7 +295,7 @@ void NameAnonymousBlocks(StmtList stmtList) { if (b.LabelName == null) { - b.LabelName = prefix + FreshAnon(); + b.LabelName = FreshPrefix(); } if (b.ec is WhileCmd) @@ -410,11 +409,11 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) } case WhileCmd ec: { - var a = FreshAnon(); - string loopHeadLabel = prefix + a + "_LoopHead"; + var freshPrefix = FreshPrefix(); + string loopHeadLabel = freshPrefix + "_LoopHead"; string /*!*/ - loopBodyLabel = prefix + a + "_LoopBody"; - string loopDoneLabel = prefix + a + "_LoopDone"; + loopBodyLabel = freshPrefix + "_LoopBody"; + string loopDoneLabel = freshPrefix + "_LoopDone"; List ssBody = new List(); List ssDone = new List(); @@ -486,10 +485,10 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) for (; ifcmd != null; ifcmd = ifcmd.ElseIf) { - var a = FreshAnon(); - string thenLabel = prefix + a + "_Then"; + var freshPrefix = FreshPrefix(); + string thenLabel = freshPrefix + "_Then"; Contract.Assert(thenLabel != null); - string elseLabel = prefix + a + "_Else"; + string elseLabel = freshPrefix + "_Else"; Contract.Assert(elseLabel != null); List ssThen = new List(); From fff02b87c0b3d88b1980dafb2b568ebd012406af Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 10:21:09 +0200 Subject: [PATCH 61/70] Automated refactoring --- .../YieldingProcInstrumentation.cs | 96 ++++++++++--------- Source/Core/AST/BigBlocksResolutionContext.cs | 6 +- Source/Core/AST/Block.cs | 25 ++--- .../ExecutionEngineTest.cs | 2 +- Source/VCGeneration/Split.cs | 2 +- 5 files changed, 63 insertions(+), 68 deletions(-) diff --git a/Source/Concurrency/YieldingProcInstrumentation.cs b/Source/Concurrency/YieldingProcInstrumentation.cs index 71a3623f4..424958a81 100644 --- a/Source/Concurrency/YieldingProcInstrumentation.cs +++ b/Source/Concurrency/YieldingProcInstrumentation.cs @@ -474,65 +474,73 @@ private void DesugarConcurrency(Implementation impl, List preconditions) // add jumps to noninterferenceChecker, returnChecker, and refinementChecker blocks var implRefinementCheckingBlocks = new List(); - foreach (var b in impl.Blocks) + foreach (var block in impl.Blocks) { - if (b.TransferCmd is GotoCmd gotoCmd) + if (block.TransferCmd is not GotoCmd gotoCmd) + { + block.TransferCmd = new GotoCmd(block.TransferCmd.tok, + new List { returnCheckerBlock, returnBlock, noninterferenceCheckerBlock }); + } + else { var targetBlocks = new List(); var addEdge = false; foreach (var nextBlock in gotoCmd.LabelTargets) { - if (nextBlock.cmds.Count > 0) + if (nextBlock.Cmds.Count <= 0) + { + continue; + } + + var cmd = nextBlock.Cmds[0]; + if (cmd is not ParCallCmd parCallCmd) { - var cmd = nextBlock.cmds[0]; - if (cmd is ParCallCmd parCallCmd) + continue; + } + + foreach (var callCmd in parCallCmd.CallCmds) + { + if (!refinementBlocks.TryGetValue(callCmd, out var targetBlock)) { - foreach (var callCmd in parCallCmd.CallCmds) - { - if (refinementBlocks.ContainsKey(callCmd)) - { - var targetBlock = refinementBlocks[callCmd]; - FixUpImplRefinementCheckingBlock(targetBlock, - CivlAttributes.IsCallMarked(callCmd) - ? returnCheckerBlock - : unchangedCheckerBlock); - targetBlocks.Add(targetBlock); - implRefinementCheckingBlocks.Add(targetBlock); - } - } - addEdge = true; + continue; } + + FixUpImplRefinementCheckingBlock(targetBlock, + CivlAttributes.IsCallMarked(callCmd) + ? returnCheckerBlock + : unchangedCheckerBlock); + targetBlocks.Add(targetBlock); + implRefinementCheckingBlocks.Add(targetBlock); } + + addEdge = true; } gotoCmd.AddTargets(targetBlocks); - if (addEdge) + if (!addEdge) { - AddEdge(gotoCmd, noninterferenceCheckerBlock); - if (blocksInYieldingLoops.Contains(b)) - { - AddEdge(gotoCmd, unchangedCheckerBlock); - } - else - { - b.Cmds.AddRange(refinementInstrumentation.CreateActionEvaluationCmds()); - AddEdge(gotoCmd, refinementCheckerBlock); - } + continue; + } + + AddEdge(gotoCmd, noninterferenceCheckerBlock); + if (blocksInYieldingLoops.Contains(block)) + { + AddEdge(gotoCmd, unchangedCheckerBlock); + } + else + { + block.Cmds.AddRange(refinementInstrumentation.CreateActionEvaluationCmds()); + AddEdge(gotoCmd, refinementCheckerBlock); } - } - else - { - b.TransferCmd = new GotoCmd(b.TransferCmd.tok, - new List {returnCheckerBlock, returnBlock, noninterferenceCheckerBlock}); } } // desugar ParCallCmd foreach (Block b in impl.Blocks) { - if (b.cmds.Count > 0) + if (b.Cmds.Count > 0) { - var cmd = b.cmds[0]; + var cmd = b.Cmds[0]; if (cmd is ParCallCmd) { DesugarParCallCmdInBlock(b, blocksInYieldingLoops.Contains(b)); @@ -578,11 +586,11 @@ private void SplitBlocks(Implementation impl) { var currTransferCmd = b.TransferCmd; int labelCount = 0; - int lastSplitIndex = b.cmds.Count; - for (int i = b.cmds.Count - 1; i >= 0; i--) + int lastSplitIndex = b.Cmds.Count; + for (int i = b.Cmds.Count - 1; i >= 0; i--) { var split = false; - var cmd = b.cmds[i]; + var cmd = b.Cmds[i]; if (cmd is ParCallCmd) { split = true; @@ -590,7 +598,7 @@ private void SplitBlocks(Implementation impl) if (split) { - var newBlock = new Block(b.tok, $"{b.Label}_{labelCount++}", b.cmds.GetRange(i, lastSplitIndex - i), + var newBlock = new Block(b.tok, $"{b.Label}_{labelCount++}", b.Cmds.GetRange(i, lastSplitIndex - i), currTransferCmd); newBlocks.Add(newBlock); currTransferCmd = new GotoCmd(b.tok, new List {newBlock}); @@ -598,7 +606,7 @@ private void SplitBlocks(Implementation impl) } } - b.cmds = b.cmds.GetRange(0, lastSplitIndex); + b.Cmds = b.Cmds.GetRange(0, lastSplitIndex); b.TransferCmd = currTransferCmd; } @@ -714,8 +722,8 @@ private void DesugarParCallCmdInBlock(Block block, bool isBlockInYieldingLoop) newCmds.AddRange(CreateUpdatesToOldGlobalVars()); newCmds.AddRange(refinementInstrumentation.CreateUpdatesToOldOutputVars()); newCmds.AddRange(CreateUpdatesToPermissionCollector(parCallCmd)); - newCmds.AddRange(block.cmds.GetRange(1, block.cmds.Count - 1)); - block.cmds = newCmds; + newCmds.AddRange(block.Cmds.GetRange(1, block.Cmds.Count - 1)); + block.Cmds = newCmds; } private Formal ParCallDesugarFormal(Variable v, int count, bool incoming) diff --git a/Source/Core/AST/BigBlocksResolutionContext.cs b/Source/Core/AST/BigBlocksResolutionContext.cs index 62ba00f92..9e6b846b8 100644 --- a/Source/Core/AST/BigBlocksResolutionContext.cs +++ b/Source/Core/AST/BigBlocksResolutionContext.cs @@ -491,8 +491,8 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) string elseLabel = freshPrefix + "_Else"; Contract.Assert(elseLabel != null); - List ssThen = new List(); - List ssElse = new List(); + var ssThen = new List(); + var ssElse = new List(); if (ifcmd.Guard != null) { var ac = new AssumeCmd(ifcmd.tok, ifcmd.Guard); @@ -514,7 +514,7 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) // ... goto Then, Else; var jumpBlock = new Block(bigBlock.tok, predLabel, predCmds, - new GotoCmd(ifcmd.tok, new List {thenLabel, elseLabel})); + new GotoCmd(ifcmd.tok, new List {thenLabel, elseLabel})); blocks.Add(jumpBlock); if (!thenGuardTakenCareOf) diff --git a/Source/Core/AST/Block.cs b/Source/Core/AST/Block.cs index c6ac50740..075dfa2f5 100644 --- a/Source/Core/AST/Block.cs +++ b/Source/Core/AST/Block.cs @@ -23,21 +23,9 @@ public string /*!*/ Label } } - [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; - } - } + [field: Rep] + [field: ElementsPeer] + public List Cmds { get; set; } public IEnumerable Exits() { @@ -70,8 +58,7 @@ public enum VisitState 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 + 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; @@ -113,7 +100,7 @@ public IEnumerable liveVarsBefore void ObjectInvariant() { Contract.Invariant(this.label != null); - Contract.Invariant(this.cmds != null); + Contract.Invariant(this.Cmds != null); Contract.Invariant(cce.NonNullElements(this._liveVarsBefore, true)); } @@ -140,7 +127,7 @@ public Block(IToken tok, string /*!*/ label, List /*!*/ cmds, TransferCmd t Contract.Requires(cmds != null); Contract.Requires(tok != null); this.label = label; - this.cmds = cmds; + this.Cmds = cmds; this.TransferCmd = transferCmd; this.Predecessors = new List(); this._liveVarsBefore = null; diff --git a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs index 6c39f1ec0..7415571ea 100644 --- a/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs +++ b/Source/UnitTests/ExecutionEngineTests/ExecutionEngineTest.cs @@ -251,7 +251,7 @@ public async Task LoopInvariantDescriptions() Parser.Parse(programString, "fakeFilename", out var program1); foreach (var block in program1.Implementations.First().Blocks) { - foreach (var cmd in block.cmds) { + foreach (var cmd in block.Cmds) { if (cmd is AssertCmd assertCmd) { assertCmd.Description = new FakeDescription(); } diff --git a/Source/VCGeneration/Split.cs b/Source/VCGeneration/Split.cs index fc5ff9473..aa22d0779 100644 --- a/Source/VCGeneration/Split.cs +++ b/Source/VCGeneration/Split.cs @@ -25,7 +25,7 @@ public class Split : ProofRun public List Blocks => blocks ??= getBlocks(); readonly List bigBlocks = new(); - public List Asserts => Blocks.SelectMany(block => block.cmds.OfType()).ToList(); + public List Asserts => Blocks.SelectMany(block => block.Cmds.OfType()).ToList(); public IReadOnlyList prunedDeclarations; public IReadOnlyList PrunedDeclarations { From d9722c3bbefc4d80bdb2a23bb6590d3b26250126 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 10:25:46 +0200 Subject: [PATCH 62/70] Automated refactoring --- .../AbstractInterpretation/NativeLattice.cs | 14 +- Source/AbstractInterpretation/Traverse.cs | 4 +- Source/Core/AST/Block.cs | 73 +++---- .../LiveVariableAnalysis.cs | 8 +- Source/Core/VariableDependenceAnalyser.cs | 198 +++++++++--------- Source/VCGeneration/ConditionGeneration.cs | 10 +- 6 files changed, 148 insertions(+), 159 deletions(-) diff --git a/Source/AbstractInterpretation/NativeLattice.cs b/Source/AbstractInterpretation/NativeLattice.cs index 2efc1f0bf..8b63e17f7 100644 --- a/Source/AbstractInterpretation/NativeLattice.cs +++ b/Source/AbstractInterpretation/NativeLattice.cs @@ -195,7 +195,7 @@ public void Analyze(Implementation impl, NativeLattice lattice, NativeLattice.El int n = 0; foreach (var block in impl.Blocks) { - block.aiId = n; + block.AiId = n; // Note: The forward analysis below will store lattice elements in pre[n] if pre[n] is non-null. // Thus, the assignment "pre[n] = bottom;" below must be done under the following condition: // n == 0 || block.widenBlock @@ -219,7 +219,7 @@ public void Analyze(Implementation impl, NativeLattice lattice, NativeLattice.El { var workItem = workItems.Dequeue(); var b = workItem.Item1; - var id = b.aiId; + var id = b.AiId; var e = workItem.Item2; if (pre[id] == null) { @@ -230,7 +230,7 @@ public void Analyze(Implementation impl, NativeLattice lattice, NativeLattice.El // no change continue; } - else if (b.widenBlock && options.Ai.StepsBeforeWidening <= iterations[id]) + else if (b.WidenBlock && options.Ai.StepsBeforeWidening <= iterations[id]) { e = lattice.Widen(pre[id], e); pre[id] = e; @@ -275,8 +275,8 @@ void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeLattice. foreach (var b in impl.Blocks) { - var element = pre[b.aiId]; - if (element != null && (b.widenBlock || options.InstrumentInfer == + var element = pre[b.AiId]; + if (element != null && (b.WidenBlock || options.InstrumentInfer == CoreOptions.InstrumentationPlaces.Everywhere)) { List newCommands = new List(); @@ -294,9 +294,9 @@ void Instrument(Implementation impl, NativeLattice.Element[] pre, NativeLattice. newCommands.Add(cmd); newCommands.AddRange(b.Cmds); - if (post != null && post[b.aiId] != null) + if (post != null && post[b.AiId] != null) { - inv = post[b.aiId].ToExpr(options); + inv = post[b.AiId].ToExpr(options); kv = new QKeyValue(Token.NoToken, "inferred", new List(), null); if (options.InstrumentWithAsserts) { diff --git a/Source/AbstractInterpretation/Traverse.cs b/Source/AbstractInterpretation/Traverse.cs index 1a1315c51..69a6d361f 100644 --- a/Source/AbstractInterpretation/Traverse.cs +++ b/Source/AbstractInterpretation/Traverse.cs @@ -54,7 +54,7 @@ static void Visit(Block b) { cce.BeginExpose(b); // we got here through a back-edge - b.widenBlock = true; + b.WidenBlock = true; cce.EndExpose(); } else if (b.TraversingStatus == Block.VisitState.AlreadyVisited) @@ -111,7 +111,7 @@ static void Visit(Block b) /// public static List ComputeLoopBodyFrom(Block block) { - Contract.Requires(block.widenBlock); + Contract.Requires(block.WidenBlock); Contract.Requires(block != null); Contract.Ensures(cce.NonNullElements(Contract.Result>())); diff --git a/Source/Core/AST/Block.cs b/Source/Core/AST/Block.cs index 075dfa2f5..bfebf90fc 100644 --- a/Source/Core/AST/Block.cs +++ b/Source/Core/AST/Block.cs @@ -7,21 +7,7 @@ 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; - } - } + public string Label { get; set; } [field: Rep] [field: ElementsPeer] @@ -37,8 +23,7 @@ public IEnumerable Exits() } [Rep] //PM: needed to verify Traverse.Visit - public TransferCmd - TransferCmd; // maybe null only because we allow deferred initialization (necessary for cyclic structures) + public TransferCmd TransferCmd; // maybe null only because we allow deferred initialization (necessary for cyclic structures) public byte[] Checksum; @@ -55,31 +40,31 @@ public enum VisitState 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 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 + 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; + public int SuccCount; - private HashSet _liveVarsBefore; + private HashSet liveVarsBefore; - public IEnumerable liveVarsBefore + public IEnumerable LiveVarsBefore { get { Contract.Ensures(cce.NonNullElements(Contract.Result>(), true)); - if (this._liveVarsBefore == null) + if (this.liveVarsBefore == null) { return null; } else { - return this._liveVarsBefore.AsEnumerable(); + return this.liveVarsBefore.AsEnumerable(); } } set @@ -87,11 +72,11 @@ public IEnumerable liveVarsBefore Contract.Requires(cce.NonNullElements(value, true)); if (value == null) { - this._liveVarsBefore = null; + this.liveVarsBefore = null; } else { - this._liveVarsBefore = new HashSet(value); + this.liveVarsBefore = new HashSet(value); } } } @@ -99,24 +84,28 @@ public IEnumerable liveVarsBefore [ContractInvariantMethod] void ObjectInvariant() { - Contract.Invariant(this.label != null); + Contract.Invariant(this.Label != null); Contract.Invariant(this.Cmds != null); - Contract.Invariant(cce.NonNullElements(this._liveVarsBefore, true)); + Contract.Invariant(cce.NonNullElements(this.liveVarsBefore, true)); } public bool IsLive(Variable v) { Contract.Requires(v != null); - if (liveVarsBefore == null) + if (LiveVarsBefore == null) { return true; } - return liveVarsBefore.Contains(v); + return LiveVarsBefore.Contains(v); } - public Block(IToken tok) - : this(tok, "", new List(), new ReturnCmd(tok)) + public static Block ShallowClone(Block block) { + return new Block(block.tok, block.Label, block.Cmds, block.TransferCmd); + } + + public Block(IToken tok, TransferCmd cmd) + : this(tok, "", new List(), cmd) { } @@ -126,13 +115,13 @@ public Block(IToken tok, string /*!*/ label, List /*!*/ cmds, TransferCmd t Contract.Requires(label != null); Contract.Requires(cmds != null); Contract.Requires(tok != null); - this.label = label; + this.Label = label; this.Cmds = cmds; this.TransferCmd = transferCmd; this.Predecessors = new List(); - this._liveVarsBefore = null; + this.liveVarsBefore = null; this.TraversingStatus = VisitState.ToVisit; - this.iterations = 0; + this.Iterations = 0; } public void Emit(TokenTextWriter stream, int level) @@ -146,7 +135,7 @@ public void Emit(TokenTextWriter stream, int level) stream.Options.PrintWithUniqueASTIds ? String.Format("h{0}^^{1}", this.GetHashCode(), this.Label) : this.Label, - this.widenBlock ? " // cut point" : ""); + this.WidenBlock ? " // cut point" : ""); foreach (Cmd /*!*/ c in this.Cmds) { @@ -178,10 +167,10 @@ public override void Resolve(ResolutionContext rc) public override void Typecheck(TypecheckingContext tc) { - foreach (Cmd /*!*/ c in Cmds) + foreach (var /*!*/ cmd in Cmds) { - Contract.Assert(c != null); - c.Typecheck(tc); + Contract.Assert(cmd != null); + cmd.Typecheck(tc); } Contract.Assume(this.TransferCmd != null); @@ -195,14 +184,14 @@ public void ResetAbstractInterpretationState() { // this.currentlyTraversed = false; this.TraversingStatus = VisitState.ToVisit; - this.iterations = 0; + this.Iterations = 0; } [Pure] public override string ToString() { Contract.Ensures(Contract.Result() != null); - return this.Label + (this.widenBlock ? "[w]" : ""); + return this.Label + (this.WidenBlock ? "[w]" : ""); } public override Absy StdDispatch(StandardVisitor visitor) diff --git a/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs b/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs index bcdbbf517..98fdef014 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs @@ -20,7 +20,7 @@ public static void ClearLiveVariables(Implementation impl) foreach (Block /*!*/ block in impl.Blocks) { Contract.Assert(block != null); - block.liveVarsBefore = null; + block.LiveVarsBefore = null; } } @@ -59,8 +59,8 @@ public void ComputeLiveVariables(Implementation impl) foreach (Block /*!*/ succ in gotoCmd.LabelTargets) { Contract.Assert(succ != null); - Contract.Assert(succ.liveVarsBefore != null); - liveVarsAfter.UnionWith(succ.liveVarsBefore); + Contract.Assert(succ.LiveVarsBefore != null); + liveVarsAfter.UnionWith(succ.LiveVarsBefore); } } } @@ -84,7 +84,7 @@ public void ComputeLiveVariables(Implementation impl) Propagate(cmds[i], liveVarsAfter); } - block.liveVarsBefore = liveVarsAfter; + block.LiveVarsBefore = liveVarsAfter; } } diff --git a/Source/Core/VariableDependenceAnalyser.cs b/Source/Core/VariableDependenceAnalyser.cs index 2e0ae6c67..3e6fdd2b3 100644 --- a/Source/Core/VariableDependenceAnalyser.cs +++ b/Source/Core/VariableDependenceAnalyser.cs @@ -137,8 +137,8 @@ public class VariableDependenceAnalyser : IVariableDependenceAnalyser private CoreOptions options; private Graph dependsOnNonTransitive; private Program prog; - private Dictionary> BlockToControllingBlocks; - private Dictionary> ControllingBlockToVariables; + private Dictionary> blockToControllingBlocks; + private Dictionary> controllingBlockToVariables; public VariableDependenceAnalyser(Program prog, CoreOptions options) { @@ -151,30 +151,30 @@ public VariableDependenceAnalyser(Program prog, CoreOptions options) private void Initialise() { foreach (var descriptor in - prog.Variables.Where(Item => VariableRelevantToAnalysis(Item, null)).Select(Variable => Variable.Name) - .Select(Name => new GlobalDescriptor(Name))) + prog.Variables.Where(item => VariableRelevantToAnalysis(item, null)).Select(variable => variable.Name) + .Select(name => new GlobalDescriptor(name))) { dependsOnNonTransitive.AddEdge(descriptor, descriptor); } - foreach (var Proc in prog.NonInlinedProcedures()) + foreach (var proc in prog.NonInlinedProcedures()) { List parameters = new List(); - parameters.AddRange(Proc.InParams); - parameters.AddRange(Proc.OutParams); + parameters.AddRange(proc.InParams); + parameters.AddRange(proc.OutParams); foreach (var descriptor in - parameters.Select(Variable => Variable.Name).Select(Name => new LocalDescriptor(Proc.Name, Name))) + parameters.Select(variable => variable.Name).Select(name => new LocalDescriptor(proc.Name, name))) { dependsOnNonTransitive.AddEdge(descriptor, descriptor); } } - foreach (var Impl in prog.NonInlinedImplementations()) + foreach (var impl in prog.NonInlinedImplementations()) { List locals = new List(); - locals.AddRange(Impl.LocVars); + locals.AddRange(impl.LocVars); foreach (var descriptor in - locals.Select(Variable => Variable.Name).Select(Name => new LocalDescriptor(Impl.Name, Name))) + locals.Select(variable => variable.Name).Select(name => new LocalDescriptor(impl.Name, name))) { dependsOnNonTransitive.AddEdge(descriptor, descriptor); } @@ -279,29 +279,29 @@ public void Analyse() options.OutputWriter.WriteLine("Variable dependence analysis: Computing control dependence info"); } - BlockToControllingBlocks = ComputeGlobalControlDependences(); + blockToControllingBlocks = ComputeGlobalControlDependences(); if (options.Trace) { options.OutputWriter.WriteLine("Variable dependence analysis: Computing control dependence variables"); } - ControllingBlockToVariables = ComputeControllingVariables(BlockToControllingBlocks); - foreach (var Impl in prog.NonInlinedImplementations()) + controllingBlockToVariables = ComputeControllingVariables(blockToControllingBlocks); + foreach (var impl in prog.NonInlinedImplementations()) { if (options.Trace) { - options.OutputWriter.WriteLine("Variable dependence analysis: Analysing " + Impl.Name); + options.OutputWriter.WriteLine("Variable dependence analysis: Analysing " + impl.Name); } - Analyse(Impl); + Analyse(impl); } } - private void Analyse(Implementation Impl) + private void Analyse(Implementation impl) { - string proc = Impl.Name; - foreach (Block b in Impl.Blocks) + string proc = impl.Name; + foreach (Block b in impl.Blocks) { Analyse(proc, b); } @@ -349,7 +349,7 @@ private void HandleCall(string proc, Block b, CallCmd call) private void HandleAssign(string proc, Block b, AssignCmd assign) { foreach (var assignPair in assign.Lhss.Zip(assign.Rhss).Where( - Item => VariableRelevantToAnalysis(Item.Item1.DeepAssignedVariable, proc))) + item => VariableRelevantToAnalysis(item.Item1.DeepAssignedVariable, proc))) { VariableDescriptor assignedVariable = MakeDescriptor(proc, assignPair.Item1.DeepAssignedVariable); AddDependences(assignedVariable, GetReferencedVariables(assignPair.Item1, proc), @@ -362,24 +362,24 @@ private void HandleAssign(string proc, Block b, AssignCmd assign) private void AddControlDependences(Block b, VariableDescriptor v, string reason, IToken tok) { - if (!BlockToControllingBlocks.ContainsKey(b)) + if (!blockToControllingBlocks.ContainsKey(b)) { return; } - foreach (var controller in BlockToControllingBlocks[b]) + foreach (var controller in blockToControllingBlocks[b]) { - AddDependences(v, ControllingBlockToVariables[controller], + AddDependences(v, controllingBlockToVariables[controller], reason + " controlling block at (" + controller.tok.line + ":" + controller.tok.col + ")", tok); } } private IEnumerable GetReferencedVariables(Absy node, string proc) { - var VarCollector = new VariableCollector(); - VarCollector.Visit(node); - return VarCollector.usedVars.Where(Item => VariableRelevantToAnalysis(Item, proc)) - .Select(Item => MakeDescriptor(proc, Item)); + var varCollector = new VariableCollector(); + varCollector.Visit(node); + return varCollector.usedVars.Where(item => VariableRelevantToAnalysis(item, proc)) + .Select(item => MakeDescriptor(proc, item)); } void AddDependences(VariableDescriptor v, IEnumerable vs, string reason, IToken tok) @@ -397,14 +397,14 @@ void AddDependences(VariableDescriptor v, IEnumerable vs, st } private Dictionary> ComputeControllingVariables( - Dictionary> GlobalCtrlDep) + Dictionary> globalCtrlDep) { - Dictionary> result = new Dictionary>(); - foreach (var Impl in prog.NonInlinedImplementations()) + var result = new Dictionary>(); + foreach (var impl in prog.NonInlinedImplementations()) { - foreach (var b in Impl.Blocks) + foreach (var b in impl.Blocks) { - result[b] = GetControlDependencyVariables(Impl.Name, b); + result[b] = GetControlDependencyVariables(impl.Name, b); } } @@ -427,10 +427,10 @@ private HashSet GetControlDependencyVariables(string proc, B AssumeCmd a = c as AssumeCmd; if (a != null && QKeyValue.FindBoolAttribute(a.Attributes, "partition")) { - var VarCollector = new VariableCollector(); - VarCollector.VisitExpr(a.Expr); - result.UnionWith(VarCollector.usedVars.Where(Item => VariableRelevantToAnalysis(Item, proc)) - .Select(Item => MakeDescriptor(proc, Item))); + var varCollector = new VariableCollector(); + varCollector.VisitExpr(a.Expr); + result.UnionWith(varCollector.usedVars.Where(item => VariableRelevantToAnalysis(item, proc)) + .Select(item => MakeDescriptor(proc, item))); } else { @@ -443,21 +443,21 @@ private HashSet GetControlDependencyVariables(string proc, B return result; } - private HashSet IgnoredVariables = null; + private HashSet ignoredVariables; public bool Ignoring(Variable v, string proc) { - if (IgnoredVariables == null) + if (ignoredVariables == null) { MakeIgnoreList(); } - if (proc != null && IgnoredVariables.Contains(new LocalDescriptor(proc, v.Name))) + if (proc != null && ignoredVariables.Contains(new LocalDescriptor(proc, v.Name))) { return true; } - if (IgnoredVariables.Contains(new GlobalDescriptor(v.Name))) + if (ignoredVariables.Contains(new GlobalDescriptor(v.Name))) { return true; } @@ -472,7 +472,7 @@ public bool VariableRelevantToAnalysis(Variable v, string proc) private void MakeIgnoreList() { - IgnoredVariables = new HashSet(); + ignoredVariables = new HashSet(); if (options.VariableDependenceIgnore == null) { return; @@ -498,12 +498,12 @@ private void MakeIgnoreList() if (tokens.Count() == 1) { - IgnoredVariables.Add(new GlobalDescriptor(tokens[0])); + ignoredVariables.Add(new GlobalDescriptor(tokens[0])); continue; } Debug.Assert(tokens.Count() == 2); - IgnoredVariables.Add(new LocalDescriptor(tokens[0], tokens[1])); + ignoredVariables.Add(new LocalDescriptor(tokens[0], tokens[1])); } } catch (System.IO.IOException e) @@ -548,7 +548,7 @@ private Dictionary> ComputeGlobalControlDependences() var indirectCallees = ComputeIndirectCallees(callGraph, directCallee); foreach (var control in GetControllingBlocks(b, localCtrlDeps[impl])) { - foreach (var c in indirectCallees.Select(Item => Item.Blocks).SelectMany(Item => Item)) + foreach (var c in indirectCallees.Select(item => item.Blocks).SelectMany(item => item)) { globalCtrlDep[control].Add(c); } @@ -579,23 +579,23 @@ private Dictionary> ComputeGlobalControlDependences() return result; } - private HashSet ComputeIndirectCallees(Graph callGraph, Implementation DirectCallee) + private HashSet ComputeIndirectCallees(Graph callGraph, Implementation directCallee) { - return ComputeIndirectCallees(callGraph, DirectCallee, new HashSet()); + return ComputeIndirectCallees(callGraph, directCallee, new HashSet()); } - private HashSet ComputeIndirectCallees(Graph callGraph, Implementation DirectCallee, + private HashSet ComputeIndirectCallees(Graph callGraph, Implementation directCallee, HashSet seen) { - if (seen.Contains(DirectCallee)) + if (seen.Contains(directCallee)) { return new HashSet(); } HashSet result = new HashSet(); - result.Add(DirectCallee); - seen.Add(DirectCallee); - foreach (var succ in callGraph.Successors(DirectCallee)) + result.Add(directCallee); + seen.Add(directCallee); + foreach (var succ in callGraph.Successors(directCallee)) { result.UnionWith(ComputeIndirectCallees(callGraph, succ, seen)); } @@ -606,11 +606,11 @@ private HashSet ComputeIndirectCallees(Graph cal private HashSet GetControllingBlocks(Block b, Dictionary> ctrlDep) { HashSet result = new HashSet(); - foreach (var KeyValue in ctrlDep) + foreach (var keyValue in ctrlDep) { - if (KeyValue.Value.Contains(b)) + if (keyValue.Value.Contains(b)) { - result.Add(KeyValue.Key); + result.Add(keyValue.Key); } } @@ -619,11 +619,11 @@ private HashSet GetControllingBlocks(Block b, Dictionary Item is LocalDescriptor).Select( - Item => (LocalDescriptor) Item).Where(Item => Item.Proc.Equals(proc) && - Item.Name.Equals(v.Name)); - if (MatchingLocals.Count() > 0) + var matchingLocals = dependsOnNonTransitive.Nodes.Where(item => item is LocalDescriptor).Select( + item => (LocalDescriptor) item).Where(item => item.Proc.Equals(proc) && + item.Name.Equals(v.Name)); + if (matchingLocals.Count() > 0) { - Debug.Assert(MatchingLocals.Count() == 1); - return MatchingLocals.ToArray()[0]; + Debug.Assert(matchingLocals.Count() == 1); + return matchingLocals.ToArray()[0]; } // It must be a global with same name as v - return dependsOnNonTransitive.Nodes.Where(Item => Item is GlobalDescriptor && - Item.Name.Equals(v.Name)).ToArray()[0]; + return dependsOnNonTransitive.Nodes.Where(item => item is GlobalDescriptor && + item.Name.Equals(v.Name)).ToArray()[0]; } - private Dictionary, HashSet> DependsOnCache = + private Dictionary, HashSet> dependsOnCache = new Dictionary, HashSet>(); - private Graph> DependsOnSCCsDAG; - private Dictionary> VariableDescriptorToSCC; + private Graph> dependsOnScCsDag; + private Dictionary> variableDescriptorToScc; public HashSet DependsOn(VariableDescriptor v) { - if (DependsOnSCCsDAG == null) + if (dependsOnScCsDag == null) { if (options.Trace) { @@ -664,33 +664,33 @@ public HashSet DependsOn(VariableDescriptor v) Adjacency next = new Adjacency(dependsOnNonTransitive.Successors); Adjacency prev = new Adjacency(dependsOnNonTransitive.Predecessors); - StronglyConnectedComponents DependsOnSCCs = + StronglyConnectedComponents dependsOnScCs = new StronglyConnectedComponents( dependsOnNonTransitive.Nodes, next, prev); - DependsOnSCCs.Compute(); + dependsOnScCs.Compute(); - VariableDescriptorToSCC = new Dictionary>(); - foreach (var scc in DependsOnSCCs) + variableDescriptorToScc = new Dictionary>(); + foreach (var scc in dependsOnScCs) { foreach (var s in scc) { - VariableDescriptorToSCC[s] = scc; + variableDescriptorToScc[s] = scc; } } - DependsOnSCCsDAG = new Graph>(); + dependsOnScCsDag = new Graph>(); foreach (var edge in dependsOnNonTransitive.Edges) { - if (VariableDescriptorToSCC[edge.Item1] != VariableDescriptorToSCC[edge.Item2]) + if (variableDescriptorToScc[edge.Item1] != variableDescriptorToScc[edge.Item2]) { - DependsOnSCCsDAG.AddEdge(VariableDescriptorToSCC[edge.Item1], VariableDescriptorToSCC[edge.Item2]); + dependsOnScCsDag.AddEdge(variableDescriptorToScc[edge.Item1], variableDescriptorToScc[edge.Item2]); } } SCC dummy = new SCC(); foreach (var n in dependsOnNonTransitive.Nodes) { - DependsOnSCCsDAG.AddEdge(VariableDescriptorToSCC[n], dummy); + dependsOnScCsDag.AddEdge(variableDescriptorToScc[n], dummy); } if (options.Trace) @@ -699,27 +699,27 @@ public HashSet DependsOn(VariableDescriptor v) } } - return DependsOn(VariableDescriptorToSCC[v]); + return DependsOn(variableDescriptorToScc[v]); } - public HashSet DependsOn(SCC vSCC) + public HashSet DependsOn(SCC vScc) { - if (!DependsOnCache.ContainsKey(vSCC)) + if (!dependsOnCache.ContainsKey(vScc)) { HashSet result = new HashSet(); - if (vSCC.Count() > 0) + if (vScc.Count() > 0) { - result.UnionWith(vSCC); - foreach (var wSCC in DependsOnSCCsDAG.Successors(vSCC)) + result.UnionWith(vScc); + foreach (var wScc in dependsOnScCsDag.Successors(vScc)) { - result.UnionWith(DependsOn(wSCC)); + result.UnionWith(DependsOn(wScc)); } } - DependsOnCache[vSCC] = result; + dependsOnCache[vScc] = result; } - return DependsOnCache[vSCC]; + return dependsOnCache[vScc]; } public void Dump() @@ -730,34 +730,34 @@ public void Dump() options.OutputWriter.WriteLine("Global variables"); options.OutputWriter.WriteLine("================"); - foreach (var GlobalEntry in dependsOnNonTransitive.Nodes.Where(Item => Item is GlobalDescriptor)) + foreach (var globalEntry in dependsOnNonTransitive.Nodes.Where(item => item is GlobalDescriptor)) { - dump(GlobalEntry); + Dump(globalEntry); } foreach (var proc in Procedures()) { options.OutputWriter.WriteLine("Variables of " + proc); options.OutputWriter.WriteLine("====================="); - foreach (var LocalEntry in dependsOnNonTransitive.Nodes.Where(Item => Item is LocalDescriptor - && ((LocalDescriptor) Item).Proc.Equals( + foreach (var localEntry in dependsOnNonTransitive.Nodes.Where(item => item is LocalDescriptor + && ((LocalDescriptor) item).Proc.Equals( proc))) { - dump(LocalEntry); + Dump(localEntry); } } } - private void dump(VariableDescriptor vd) + private void Dump(VariableDescriptor vd) { options.OutputWriter.Write(vd + " <- {"); bool first = true; - var SortedDependents = DependsOn(vd).ToList(); - SortedDependents.Sort(); - foreach (var Descriptor in SortedDependents) + var sortedDependents = DependsOn(vd).ToList(); + sortedDependents.Sort(); + foreach (var descriptor in sortedDependents) { - options.OutputWriter.Write((first ? "" : ",") + "\n " + Descriptor); + options.OutputWriter.Write((first ? "" : ",") + "\n " + descriptor); if (first) { first = false; @@ -770,8 +770,8 @@ private void dump(VariableDescriptor vd) private HashSet Procedures() { - return new HashSet(dependsOnNonTransitive.Nodes.Where(Item => - Item is LocalDescriptor).Select(Item => ((LocalDescriptor) Item).Proc)); + return new HashSet(dependsOnNonTransitive.Nodes.Where(item => + item is LocalDescriptor).Select(item => ((LocalDescriptor) item).Proc)); } } diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 2e7f91060..3ba97ab5a 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -766,7 +766,7 @@ protected Dictionary ConvertBlocks2PassiveCmd(TextWriter traceWr Dictionary incarnationMap = ComputeIncarnationMap(b, block2Incarnation); // b.liveVarsBefore has served its purpose in the just-finished call to ComputeIncarnationMap; null it out. - b.liveVarsBefore = null; + b.LiveVarsBefore = null; // Decrement the succCount field in each predecessor. Once the field reaches zero in any block, // all its successors have been passified. Consequently, its entry in block2Incarnation can be removed. @@ -775,7 +775,7 @@ protected Dictionary ConvertBlocks2PassiveCmd(TextWriter traceWr variableCollectors[b] = mvc; foreach (Block p in b.Predecessors) { - p.succCount--; + p.SuccCount--; if (p.Checksum != null) { // Compute the checksum based on the checksums of the predecessor. The order should not matter. @@ -783,7 +783,7 @@ protected Dictionary ConvertBlocks2PassiveCmd(TextWriter traceWr } mvc.AddUsedVariables(variableCollectors[p].UsedVariables); - if (p.succCount == 0) + if (p.SuccCount == 0) { block2Incarnation.Remove(p); } @@ -794,12 +794,12 @@ protected Dictionary ConvertBlocks2PassiveCmd(TextWriter traceWr GotoCmd gotoCmd = b.TransferCmd as GotoCmd; if (gotoCmd == null) { - b.succCount = 0; + b.SuccCount = 0; } else { // incarnationMap needs to be added only if there is some successor of b - b.succCount = gotoCmd.LabelNames.Count; + b.SuccCount = gotoCmd.LabelNames.Count; block2Incarnation.Add(b, incarnationMap); } From 3e14a66c08118f2840f0c13b9efd55d809019749 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 10:36:33 +0200 Subject: [PATCH 63/70] Extract classes into separate files --- Source/Core/AST/AbsyCmd.cs | 228 +----------------- Source/Core/AST/{ => Blocks}/Block.cs | 12 +- Source/Core/AST/Blocks/GotoCmd.cs | 159 ++++++++++++ Source/Core/AST/Blocks/ReturnCmd.cs | 33 +++ Source/Core/AST/Blocks/TransferCmd.cs | 33 +++ .../Core/AST/Blocks/TransferCmdContracts.cs | 18 ++ Source/VCGeneration/ManualSplitFinder.cs | 17 -- 7 files changed, 246 insertions(+), 254 deletions(-) rename Source/Core/AST/{ => Blocks}/Block.cs (94%) create mode 100644 Source/Core/AST/Blocks/GotoCmd.cs create mode 100644 Source/Core/AST/Blocks/ReturnCmd.cs create mode 100644 Source/Core/AST/Blocks/TransferCmd.cs create mode 100644 Source/Core/AST/Blocks/TransferCmdContracts.cs diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/AbsyCmd.cs index 5a4d6d493..f42a4be00 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/AbsyCmd.cs @@ -2579,8 +2579,7 @@ public override Absy StdDispatch(StandardVisitor visitor) public class ReturnExprCmd : ReturnCmd { - public Expr /*!*/ - Expr; + public Expr /*!*/ Expr; [ContractInvariantMethod] void ObjectInvariant() @@ -2705,229 +2704,4 @@ public override Absy StdDispatch(StandardVisitor visitor) return visitor.VisitHavocCmd(this); } } - - //--------------------------------------------------------------------- - // Transfer commands - [ContractClass(typeof(TransferCmdContracts))] - public abstract class TransferCmd : Absy - { - public ProofObligationDescription Description { get; set; } = new PostconditionDescription(); - - internal TransferCmd(IToken /*!*/ tok) - : base(tok) - { - Contract.Requires(tok != null); - } - - public abstract void Emit(TokenTextWriter /*!*/ stream, int level); - - public override void Typecheck(TypecheckingContext tc) - { - //Contract.Requires(tc != null); - // nothing to typecheck - } - - public override string ToString() - { - Contract.Ensures(Contract.Result() != null); - System.IO.StringWriter buffer = new System.IO.StringWriter(); - using TokenTextWriter stream = new TokenTextWriter("", buffer, false, false, PrintOptions.Default); - this.Emit(stream, 0); - - return buffer.ToString(); - } - } - - [ContractClassFor(typeof(TransferCmd))] - public abstract class TransferCmdContracts : TransferCmd - { - public TransferCmdContracts() : base(null) - { - } - - public override void Emit(TokenTextWriter stream, int level) - { - Contract.Requires(stream != null); - throw new NotImplementedException(); - } - } - - public class ReturnCmd : TransferCmd - { - public ReturnCmd(IToken /*!*/ tok) - : base(tok) - { - Contract.Requires(tok != null); - } - - public override void Emit(TokenTextWriter stream, int level) - { - //Contract.Requires(stream != null); - stream.WriteLine(this, level, "return;"); - } - - public override void Resolve(ResolutionContext rc) - { - //Contract.Requires(rc != null); - // nothing to resolve - } - - public override Absy StdDispatch(StandardVisitor visitor) - { - //Contract.Requires(visitor != null); - Contract.Ensures(Contract.Result() != null); - return visitor.VisitReturnCmd(this); - } - } - - public class GotoCmd : TransferCmd - { - [Rep] public List LabelNames; - [Rep] public List LabelTargets; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(LabelNames == null || LabelTargets == null || LabelNames.Count == LabelTargets.Count); - } - - [NotDelayed] - public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq) - : base(tok) - { - Contract.Requires(tok != null); - Contract.Requires(labelSeq != null); - this.LabelNames = labelSeq; - } - - public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq, List /*!*/ blockSeq) - : base(tok) - { - Contract.Requires(tok != null); - Contract.Requires(labelSeq != null); - Contract.Requires(blockSeq != null); - Debug.Assert(labelSeq.Count == blockSeq.Count); - for (int i = 0; i < labelSeq.Count; i++) - { - Debug.Assert(Equals(labelSeq[i], cce.NonNull(blockSeq[i]).Label)); - } - - this.LabelNames = labelSeq; - this.LabelTargets = blockSeq; - } - - public GotoCmd(IToken /*!*/ tok, List /*!*/ blockSeq) - : base(tok) - { - //requires (blockSeq[i] != null ==> blockSeq[i].Label != null); - Contract.Requires(tok != null); - Contract.Requires(blockSeq != null); - List labelSeq = new List(); - for (int i = 0; i < blockSeq.Count; i++) - { - labelSeq.Add(cce.NonNull(blockSeq[i]).Label); - } - - this.LabelNames = labelSeq; - this.LabelTargets = blockSeq; - } - - public void RemoveTarget(Block b) { - LabelNames.Remove(b.Label); - LabelTargets.Remove(b); - } - - public void AddTarget(Block b) - { - Contract.Requires(b != null); - Contract.Requires(b.Label != null); - Contract.Requires(this.LabelTargets != null); - Contract.Requires(this.LabelNames != null); - this.LabelTargets.Add(b); - this.LabelNames.Add(b.Label); - } - - public void AddTargets(IEnumerable blocks) - { - Contract.Requires(blocks != null); - Contract.Requires(cce.NonNullElements(blocks)); - Contract.Requires(this.LabelTargets != null); - Contract.Requires(this.LabelNames != null); - foreach (var block in blocks) - { - AddTarget(block); - } - } - - public override void Emit(TokenTextWriter stream, int level) - { - //Contract.Requires(stream != null); - Contract.Assume(this.LabelNames != null); - stream.Write(this, level, "goto "); - if (stream.Options.PrintWithUniqueASTIds) - { - if (LabelTargets == null) - { - string sep = ""; - foreach (string name in LabelNames) - { - stream.Write("{0}{1}^^{2}", sep, "NoDecl", name); - sep = ", "; - } - } - else - { - string sep = ""; - foreach (Block /*!*/ b in LabelTargets) - { - Contract.Assert(b != null); - stream.Write("{0}h{1}^^{2}", sep, b.GetHashCode(), b.Label); - sep = ", "; - } - } - } - else - { - LabelNames.Emit(stream); - } - - stream.WriteLine(";"); - } - - public override void Resolve(ResolutionContext rc) - { - //Contract.Requires(rc != null); - Contract.Ensures(LabelTargets != null); - if (LabelTargets != null) - { - // already resolved - return; - } - - Contract.Assume(this.LabelNames != null); - LabelTargets = new List(); - foreach (string /*!*/ lbl in LabelNames) - { - Contract.Assert(lbl != null); - Block b = rc.LookUpBlock(lbl); - if (b == null) - { - rc.Error(this, "goto to unknown block: {0}", lbl); - } - else - { - LabelTargets.Add(b); - } - } - - Debug.Assert(rc.ErrorCount > 0 || LabelTargets.Count == LabelNames.Count); - } - - public override Absy StdDispatch(StandardVisitor visitor) - { - //Contract.Requires(visitor != null); - Contract.Ensures(Contract.Result() != null); - return visitor.VisitGotoCmd(this); - } - } } diff --git a/Source/Core/AST/Block.cs b/Source/Core/AST/Blocks/Block.cs similarity index 94% rename from Source/Core/AST/Block.cs rename to Source/Core/AST/Blocks/Block.cs index bfebf90fc..89a6c08cd 100644 --- a/Source/Core/AST/Block.cs +++ b/Source/Core/AST/Blocks/Block.cs @@ -27,10 +27,6 @@ public IEnumerable Exits() public byte[] Checksum; - // Abstract interpretation - - // public bool currentlyTraversed; - public enum VisitState { ToVisit, @@ -99,13 +95,9 @@ public bool IsLive(Variable v) return LiveVarsBefore.Contains(v); } - - public static Block ShallowClone(Block block) { - return new Block(block.tok, block.Label, block.Cmds, block.TransferCmd); - } - public Block(IToken tok, TransferCmd cmd) - : this(tok, "", new List(), cmd) + public Block(IToken tok) + : this(tok, "", new List(), new ReturnCmd(tok)) { } diff --git a/Source/Core/AST/Blocks/GotoCmd.cs b/Source/Core/AST/Blocks/GotoCmd.cs new file mode 100644 index 000000000..790988969 --- /dev/null +++ b/Source/Core/AST/Blocks/GotoCmd.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class GotoCmd : TransferCmd +{ + [Rep] public List LabelNames; + [Rep] public List LabelTargets; + + public QKeyValue Attributes { get; set; } + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(LabelNames == null || LabelTargets == null || LabelNames.Count == LabelTargets.Count); + } + + [NotDelayed] + public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq) + : base(tok) + { + Contract.Requires(tok != null); + Contract.Requires(labelSeq != null); + this.LabelNames = labelSeq; + } + + public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq, List /*!*/ blockSeq) + : base(tok) + { + Contract.Requires(tok != null); + Contract.Requires(labelSeq != null); + Contract.Requires(blockSeq != null); + Debug.Assert(labelSeq.Count == blockSeq.Count); + for (int i = 0; i < labelSeq.Count; i++) + { + Debug.Assert(Equals(labelSeq[i], cce.NonNull(blockSeq[i]).Label)); + } + + this.LabelNames = labelSeq; + this.LabelTargets = blockSeq; + } + + public GotoCmd(IToken /*!*/ tok, List /*!*/ blocks) + : base(tok) + { + //requires (blockSeq[i] != null ==> blockSeq[i].Label != null); + Contract.Requires(tok != null); + Contract.Requires(blocks != null); + var labels = new List(); + foreach (var block in blocks) + { + labels.Add(block.Label); + } + + this.LabelNames = labels; + this.LabelTargets = blocks; + } + + public void RemoveTarget(Block b) { + LabelNames.Remove(b.Label); + LabelTargets.Remove(b); + } + + public void AddTarget(Block b) + { + Contract.Requires(b != null); + Contract.Requires(b.Label != null); + Contract.Requires(this.LabelTargets != null); + Contract.Requires(this.LabelNames != null); + this.LabelTargets.Add(b); + this.LabelNames.Add(b.Label); + } + + public void AddTargets(IEnumerable blocks) + { + Contract.Requires(blocks != null); + Contract.Requires(cce.NonNullElements(blocks)); + Contract.Requires(this.LabelTargets != null); + Contract.Requires(this.LabelNames != null); + foreach (var block in blocks) + { + AddTarget(block); + } + } + + public override void Emit(TokenTextWriter stream, int level) + { + //Contract.Requires(stream != null); + Contract.Assume(this.LabelNames != null); + stream.Write(this, level, "goto "); + if (stream.Options.PrintWithUniqueASTIds) + { + if (LabelTargets == null) + { + string sep = ""; + foreach (string name in LabelNames) + { + stream.Write("{0}{1}^^{2}", sep, "NoDecl", name); + sep = ", "; + } + } + else + { + string sep = ""; + foreach (Block /*!*/ b in LabelTargets) + { + Contract.Assert(b != null); + stream.Write("{0}h{1}^^{2}", sep, b.GetHashCode(), b.Label); + sep = ", "; + } + } + } + else + { + LabelNames.Emit(stream); + } + + stream.WriteLine(";"); + } + + public override void Resolve(ResolutionContext rc) + { + //Contract.Requires(rc != null); + Contract.Ensures(LabelTargets != null); + if (LabelTargets != null) + { + // already resolved + return; + } + + Contract.Assume(this.LabelNames != null); + LabelTargets = new List(); + foreach (string /*!*/ lbl in LabelNames) + { + Contract.Assert(lbl != null); + Block b = rc.LookUpBlock(lbl); + if (b == null) + { + rc.Error(this, "goto to unknown block: {0}", lbl); + } + else + { + LabelTargets.Add(b); + } + } + + Debug.Assert(rc.ErrorCount > 0 || LabelTargets.Count == LabelNames.Count); + } + + public override Absy StdDispatch(StandardVisitor visitor) + { + //Contract.Requires(visitor != null); + Contract.Ensures(Contract.Result() != null); + return visitor.VisitGotoCmd(this); + } +} \ No newline at end of file diff --git a/Source/Core/AST/Blocks/ReturnCmd.cs b/Source/Core/AST/Blocks/ReturnCmd.cs new file mode 100644 index 000000000..9f60df66b --- /dev/null +++ b/Source/Core/AST/Blocks/ReturnCmd.cs @@ -0,0 +1,33 @@ +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class ReturnCmd : TransferCmd +{ + public QKeyValue Attributes { get; set; } + + public ReturnCmd(IToken /*!*/ tok) + : base(tok) + { + Contract.Requires(tok != null); + } + + public override void Emit(TokenTextWriter stream, int level) + { + //Contract.Requires(stream != null); + stream.WriteLine(this, level, "return;"); + } + + public override void Resolve(ResolutionContext rc) + { + //Contract.Requires(rc != null); + // nothing to resolve + } + + public override Absy StdDispatch(StandardVisitor visitor) + { + //Contract.Requires(visitor != null); + Contract.Ensures(Contract.Result() != null); + return visitor.VisitReturnCmd(this); + } +} \ No newline at end of file diff --git a/Source/Core/AST/Blocks/TransferCmd.cs b/Source/Core/AST/Blocks/TransferCmd.cs new file mode 100644 index 000000000..933ccdc91 --- /dev/null +++ b/Source/Core/AST/Blocks/TransferCmd.cs @@ -0,0 +1,33 @@ +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +[ContractClass(typeof(TransferCmdContracts))] +public abstract class TransferCmd : Absy +{ + public ProofObligationDescription Description { get; set; } = new PostconditionDescription(); + + internal TransferCmd(IToken /*!*/ tok) + : base(tok) + { + Contract.Requires(tok != null); + } + + public abstract void Emit(TokenTextWriter /*!*/ stream, int level); + + public override void Typecheck(TypecheckingContext tc) + { + //Contract.Requires(tc != null); + // nothing to typecheck + } + + public override string ToString() + { + Contract.Ensures(Contract.Result() != null); + System.IO.StringWriter buffer = new System.IO.StringWriter(); + using TokenTextWriter stream = new TokenTextWriter("", buffer, false, false, PrintOptions.Default); + this.Emit(stream, 0); + + return buffer.ToString(); + } +} \ No newline at end of file diff --git a/Source/Core/AST/Blocks/TransferCmdContracts.cs b/Source/Core/AST/Blocks/TransferCmdContracts.cs new file mode 100644 index 000000000..fbb7da6e0 --- /dev/null +++ b/Source/Core/AST/Blocks/TransferCmdContracts.cs @@ -0,0 +1,18 @@ +using System; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +[ContractClassFor(typeof(TransferCmd))] +public abstract class TransferCmdContracts : TransferCmd +{ + public TransferCmdContracts() : base(null) + { + } + + public override void Emit(TokenTextWriter stream, int level) + { + Contract.Requires(stream != null); + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Source/VCGeneration/ManualSplitFinder.cs b/Source/VCGeneration/ManualSplitFinder.cs index ffc6806d2..d6307ed29 100644 --- a/Source/VCGeneration/ManualSplitFinder.cs +++ b/Source/VCGeneration/ManualSplitFinder.cs @@ -103,23 +103,6 @@ private static Dictionary PickBlocksToVerify(List blocks, D return blockAssignments; } - 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(oldBlock.tok) { - Label = oldBlock.Label, - 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) { From 760c2b06c861ecc47dbd26227b39db48322b9a2e Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 10:50:52 +0200 Subject: [PATCH 64/70] Refactoring --- Source/Core/AST/BigBlocksResolutionContext.cs | 107 +++++++++--------- 1 file changed, 56 insertions(+), 51 deletions(-) diff --git a/Source/Core/AST/BigBlocksResolutionContext.cs b/Source/Core/AST/BigBlocksResolutionContext.cs index 9e6b846b8..1dfdcca23 100644 --- a/Source/Core/AST/BigBlocksResolutionContext.cs +++ b/Source/Core/AST/BigBlocksResolutionContext.cs @@ -352,12 +352,12 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) { Contract.Requires(stmtList != null); Contract.Requires(blocks != null); - List cmdPrefixToApply = stmtList.PrefixCommands; + var cmdPrefixToApply = stmtList.PrefixCommands; - int n = stmtList.BigBlocks.Count; + int remainingBigBlocks = stmtList.BigBlocks.Count; foreach (var bigBlock in stmtList.BigBlocks) { - n--; + remainingBigBlocks--; Contract.Assert(bigBlock.LabelName != null); List theSimpleCmds; if (cmdPrefixToApply == null) @@ -376,7 +376,7 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) { // this BigBlock has the very same components as a Block Contract.Assert(bigBlock.ec == null); - Block block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, bigBlock.tc); + var block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, bigBlock.tc); blocks.Add(block); } else @@ -386,24 +386,24 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) case null: { TransferCmd trCmd; - if (n == 0 && runOffTheEndLabel != null) + if (remainingBigBlocks == 0 && runOffTheEndLabel != null) { // goto the given label instead of the textual successor block - trCmd = new GotoCmd(stmtList.EndCurly, new List {runOffTheEndLabel}); + trCmd = new GotoCmd(stmtList.EndCurly, new List {runOffTheEndLabel}); } else { trCmd = GotoSuccessor(stmtList.EndCurly, bigBlock); } - Block block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, trCmd); + var block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, trCmd); blocks.Add(block); break; } case BreakCmd ec: { Contract.Assert(ec.BreakEnclosure != null); - Block block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, GotoSuccessor(ec.tok, ec.BreakEnclosure)); + var block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, GotoSuccessor(ec.tok, ec.BreakEnclosure)); blocks.Add(block); break; } @@ -411,20 +411,23 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) { var freshPrefix = FreshPrefix(); string loopHeadLabel = freshPrefix + "_LoopHead"; - string /*!*/ - loopBodyLabel = freshPrefix + "_LoopBody"; + string loopBodyLabel = freshPrefix + "_LoopBody"; string loopDoneLabel = freshPrefix + "_LoopDone"; - List ssBody = new List(); - List ssDone = new List(); + var ssBody = new List(); + var ssDone = new List(); if (ec.Guard != null) { - var ac = new AssumeCmd(ec.tok, ec.Guard); - ac.Attributes = new QKeyValue(ec.tok, "partition", new List(), null); + var ac = new AssumeCmd(ec.tok, ec.Guard) + { + Attributes = new QKeyValue(ec.tok, "partition", new List(), null) + }; ssBody.Add(ac); - ac = new AssumeCmd(ec.tok, Expr.Not(ec.Guard)); - ac.Attributes = new QKeyValue(ec.tok, "partition", new List(), null); + ac = new AssumeCmd(ec.tok, Expr.Not(ec.Guard)) + { + Attributes = new QKeyValue(ec.tok, "partition", new List(), null) + }; ssDone.Add(ac); } @@ -432,8 +435,8 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) bool bodyGuardTakenCareOf = ec.Body.PrefixFirstBlock(ssBody, ref loopBodyLabel); // ... goto LoopHead; - Block block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, - new GotoCmd(ec.tok, new List {loopHeadLabel})); + var block = new Block(bigBlock.tok, bigBlock.LabelName, theSimpleCmds, + new GotoCmd(ec.tok, new List {loopHeadLabel})); blocks.Add(block); // LoopHead: assert/assume loop_invariant; goto LoopDone, LoopBody; @@ -448,14 +451,14 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) } block = new Block(ec.tok, loopHeadLabel, ssHead, - new GotoCmd(ec.tok, new List {loopDoneLabel, loopBodyLabel})); + new GotoCmd(ec.tok, new List {loopDoneLabel, loopBodyLabel})); blocks.Add(block); if (!bodyGuardTakenCareOf) { // LoopBody: assume guard; goto firstLoopBlock; block = new Block(ec.tok, loopBodyLabel, ssBody, - new GotoCmd(ec.tok, new List {ec.Body.BigBlocks[0].LabelName})); + new GotoCmd(ec.tok, new List {ec.Body.BigBlocks[0].LabelName})); blocks.Add(block); } @@ -464,10 +467,10 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) // LoopDone: assume !guard; goto loopSuccessor; TransferCmd trCmd; - if (n == 0 && runOffTheEndLabel != null) + if (remainingBigBlocks == 0 && runOffTheEndLabel != null) { // goto the given label instead of the textual successor block - trCmd = new GotoCmd(ec.tok, new List {runOffTheEndLabel}); + trCmd = new GotoCmd(ec.tok, new List {runOffTheEndLabel}); } else { @@ -478,12 +481,12 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) blocks.Add(block); break; } - case IfCmd ifcmd: + case IfCmd ifCmd: { string predLabel = bigBlock.LabelName; - List predCmds = theSimpleCmds; + var predCmds = theSimpleCmds; - for (; ifcmd != null; ifcmd = ifcmd.ElseIf) + for (; ifCmd != null; ifCmd = ifCmd.ElseIf) { var freshPrefix = FreshPrefix(); string thenLabel = freshPrefix + "_Then"; @@ -493,64 +496,66 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) var ssThen = new List(); var ssElse = new List(); - if (ifcmd.Guard != null) + if (ifCmd.Guard != null) { - var ac = new AssumeCmd(ifcmd.tok, ifcmd.Guard); - ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); + var ac = new AssumeCmd(ifCmd.tok, ifCmd.Guard); + ac.Attributes = new QKeyValue(ifCmd.tok, "partition", new List(), null); ssThen.Add(ac); - ac = new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard)); - ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); + ac = new AssumeCmd(ifCmd.tok, Expr.Not(ifCmd.Guard)); + ac.Attributes = new QKeyValue(ifCmd.tok, "partition", new List(), null); ssElse.Add(ac); } // Try to squeeze in ssThen/ssElse into the first block of ifcmd.thn/ifcmd.elseBlock - bool thenGuardTakenCareOf = ifcmd.Thn.PrefixFirstBlock(ssThen, ref thenLabel); + bool thenGuardTakenCareOf = ifCmd.Thn.PrefixFirstBlock(ssThen, ref thenLabel); bool elseGuardTakenCareOf = false; - if (ifcmd.ElseBlock != null) + if (ifCmd.ElseBlock != null) { - elseGuardTakenCareOf = ifcmd.ElseBlock.PrefixFirstBlock(ssElse, ref elseLabel); + elseGuardTakenCareOf = ifCmd.ElseBlock.PrefixFirstBlock(ssElse, ref elseLabel); } // ... goto Then, Else; var jumpBlock = new Block(bigBlock.tok, predLabel, predCmds, - new GotoCmd(ifcmd.tok, new List {thenLabel, elseLabel})); + new GotoCmd(ifCmd.tok, new List {thenLabel, elseLabel})); blocks.Add(jumpBlock); if (!thenGuardTakenCareOf) { // Then: assume guard; goto firstThenBlock; - var thenJumpBlock = new Block(ifcmd.tok, thenLabel, ssThen, - new GotoCmd(ifcmd.tok, new List {ifcmd.Thn.BigBlocks[0].LabelName})); + var thenJumpBlock = new Block(ifCmd.tok, thenLabel, ssThen, + new GotoCmd(ifCmd.tok, new List {ifCmd.Thn.BigBlocks[0].LabelName})); blocks.Add(thenJumpBlock); } // recurse to create the blocks for the then branch - CreateBlocks(ifcmd.Thn, n == 0 ? runOffTheEndLabel : null); + CreateBlocks(ifCmd.Thn, remainingBigBlocks == 0 ? runOffTheEndLabel : null); - if (ifcmd.ElseBlock != null) + if (ifCmd.ElseBlock != null) { - Contract.Assert(ifcmd.ElseIf == null); + Contract.Assert(ifCmd.ElseIf == null); if (!elseGuardTakenCareOf) { // Else: assume !guard; goto firstElseBlock; - var elseJumpBlock = new Block(ifcmd.tok, elseLabel, ssElse, - new GotoCmd(ifcmd.tok, new List {ifcmd.ElseBlock.BigBlocks[0].LabelName})); + var elseJumpBlock = new Block(ifCmd.tok, elseLabel, ssElse, + new GotoCmd(ifCmd.tok, new List {ifCmd.ElseBlock.BigBlocks[0].LabelName})); blocks.Add(elseJumpBlock); } // recurse to create the blocks for the else branch - CreateBlocks(ifcmd.ElseBlock, n == 0 ? runOffTheEndLabel : null); + CreateBlocks(ifCmd.ElseBlock, remainingBigBlocks == 0 ? runOffTheEndLabel : null); } - else if (ifcmd.ElseIf != null) + else if (ifCmd.ElseIf != null) { // this is an "else if" predLabel = elseLabel; predCmds = new List(); - if (ifcmd.Guard != null) + if (ifCmd.Guard != null) { - var ac = new AssumeCmd(ifcmd.tok, Expr.Not(ifcmd.Guard)); - ac.Attributes = new QKeyValue(ifcmd.tok, "partition", new List(), null); + var ac = new AssumeCmd(ifCmd.tok, Expr.Not(ifCmd.Guard)) + { + Attributes = new QKeyValue(ifCmd.tok, "partition", new List(), null) + }; predCmds.Add(ac); } } @@ -559,17 +564,17 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) // no else alternative is specified, so else branch is just "skip" // Else: assume !guard; goto ifSuccessor; TransferCmd trCmd; - if (n == 0 && runOffTheEndLabel != null) + if (remainingBigBlocks == 0 && runOffTheEndLabel != null) { // goto the given label instead of the textual successor block - trCmd = new GotoCmd(ifcmd.tok, new List {runOffTheEndLabel}); + trCmd = new GotoCmd(ifCmd.tok, new List {runOffTheEndLabel}); } else { - trCmd = GotoSuccessor(ifcmd.tok, bigBlock); + trCmd = GotoSuccessor(ifCmd.tok, bigBlock); } - var block = new Block(ifcmd.tok, elseLabel, ssElse, trCmd); + var block = new Block(ifCmd.tok, elseLabel, ssElse, trCmd); blocks.Add(block); } } @@ -588,7 +593,7 @@ TransferCmd GotoSuccessor(IToken tok, BigBlock b) Contract.Ensures(Contract.Result() != null); if (b.successorBigBlock != null) { - return new GotoCmd(tok, new List {b.successorBigBlock.LabelName}); + return new GotoCmd(tok, new List {b.successorBigBlock.LabelName}); } else { From 5271205f8c4042022dfef329e64b3eb0c051ac47 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 11:00:34 +0200 Subject: [PATCH 65/70] Moves classes into separate files --- Source/Core/AST/{ => Commands}/AbsyCmd.cs | 494 ------------------ Source/Core/AST/{ => Commands}/CallCmd.cs | 0 Source/Core/AST/{ => Commands}/ChangeScope.cs | 0 .../Core/AST/{ => Commands}/HideRevealCmd.cs | 0 Source/Core/AST/HigherBoogie/BigBlock.cs | 123 +++++ .../BigBlocksResolutionContext.cs | 6 +- Source/Core/AST/HigherBoogie/BreakCmd.cs | 28 + Source/Core/AST/{ => HigherBoogie}/IfCmd.cs | 0 Source/Core/AST/HigherBoogie/StmtList.cs | 134 +++++ .../Core/AST/HigherBoogie/StmtListBuilder.cs | 98 ++++ Source/Core/AST/HigherBoogie/StructuredCmd.cs | 38 ++ .../HigherBoogie/StructuredCmdContracts.cs | 18 + Source/Core/AST/HigherBoogie/WhileCmd.cs | 74 +++ .../Core/AST/{Blocks => LowerBoogie}/Block.cs | 0 .../AST/{Blocks => LowerBoogie}/GotoCmd.cs | 0 .../AST/{Blocks => LowerBoogie}/ReturnCmd.cs | 0 .../{Blocks => LowerBoogie}/TransferCmd.cs | 0 .../TransferCmdContracts.cs | 0 18 files changed, 516 insertions(+), 497 deletions(-) rename Source/Core/AST/{ => Commands}/AbsyCmd.cs (82%) rename Source/Core/AST/{ => Commands}/CallCmd.cs (100%) rename Source/Core/AST/{ => Commands}/ChangeScope.cs (100%) rename Source/Core/AST/{ => Commands}/HideRevealCmd.cs (100%) create mode 100644 Source/Core/AST/HigherBoogie/BigBlock.cs rename Source/Core/AST/{ => HigherBoogie}/BigBlocksResolutionContext.cs (99%) create mode 100644 Source/Core/AST/HigherBoogie/BreakCmd.cs rename Source/Core/AST/{ => HigherBoogie}/IfCmd.cs (100%) create mode 100644 Source/Core/AST/HigherBoogie/StmtList.cs create mode 100644 Source/Core/AST/HigherBoogie/StmtListBuilder.cs create mode 100644 Source/Core/AST/HigherBoogie/StructuredCmd.cs create mode 100644 Source/Core/AST/HigherBoogie/StructuredCmdContracts.cs create mode 100644 Source/Core/AST/HigherBoogie/WhileCmd.cs rename Source/Core/AST/{Blocks => LowerBoogie}/Block.cs (100%) rename Source/Core/AST/{Blocks => LowerBoogie}/GotoCmd.cs (100%) rename Source/Core/AST/{Blocks => LowerBoogie}/ReturnCmd.cs (100%) rename Source/Core/AST/{Blocks => LowerBoogie}/TransferCmd.cs (100%) rename Source/Core/AST/{Blocks => LowerBoogie}/TransferCmdContracts.cs (100%) diff --git a/Source/Core/AST/AbsyCmd.cs b/Source/Core/AST/Commands/AbsyCmd.cs similarity index 82% rename from Source/Core/AST/AbsyCmd.cs rename to Source/Core/AST/Commands/AbsyCmd.cs index f42a4be00..e10114c03 100644 --- a/Source/Core/AST/AbsyCmd.cs +++ b/Source/Core/AST/Commands/AbsyCmd.cs @@ -3,503 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Diagnostics.Contracts; -using Set = Microsoft.Boogie.GSet; namespace Microsoft.Boogie { - //--------------------------------------------------------------------- - // BigBlock - public class BigBlock - { - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(tok != null); - Contract.Invariant(Anonymous || this.labelName != null); - Contract.Invariant(this._ec == null || this._tc == null); - Contract.Invariant(this._simpleCmds != null); - } - - public readonly IToken /*!*/ - tok; - - public readonly bool Anonymous; - - private string labelName; - - public string LabelName - { - get - { - Contract.Ensures(Anonymous || Contract.Result() != null); - return this.labelName; - } - set - { - Contract.Requires(Anonymous || value != null); - this.labelName = value; - } - } - - [Rep] private List /*!*/ _simpleCmds; - - /// - /// These come before the structured command - /// - public List /*!*/ simpleCmds - { - get - { - Contract.Ensures(Contract.Result>() != null); - return this._simpleCmds; - } - set - { - Contract.Requires(value != null); - this._simpleCmds = value; - } - } - - private StructuredCmd _ec; - - public StructuredCmd ec - { - get { return this._ec; } - set - { - Contract.Requires(value == null || this.tc == null); - this._ec = value; - } - } - - private TransferCmd _tc; - - public TransferCmd tc - { - get { return this._tc; } - set - { - Contract.Requires(value == null || this.ec == null); - this._tc = value; - } - } - - public BigBlock - successorBigBlock; // semantic successor (may be a back-edge, pointing back to enclosing while statement); null if successor is end of procedure body (or if field has not yet been initialized) - - public BigBlock(IToken tok, string labelName, [Captured] List simpleCmds, StructuredCmd ec, TransferCmd tc) - { - Contract.Requires(simpleCmds != null); - Contract.Requires(tok != null); - Contract.Requires(ec == null || tc == null); - this.tok = tok; - this.Anonymous = labelName == null; - this.labelName = labelName; - this._simpleCmds = simpleCmds; - this._ec = ec; - this._tc = tc; - } - - public void Emit(TokenTextWriter stream, int level) - { - Contract.Requires(stream != null); - if (!Anonymous) - { - stream.WriteLine(level, "{0}:", - stream.Options.PrintWithUniqueASTIds - ? String.Format("h{0}^^{1}", this.GetHashCode(), this.LabelName) - : this.LabelName); - } - - foreach (Cmd /*!*/ c in this.simpleCmds) - { - Contract.Assert(c != null); - c.Emit(stream, level + 1); - } - - if (this.ec != null) - { - this.ec.Emit(stream, level + 1); - } - else if (this.tc != null) - { - this.tc.Emit(stream, level + 1); - } - } - } - - public class StmtList - { - [Rep] private readonly List /*!*/ bigBlocks; - - public IList /*!*/ BigBlocks - { - get - { - Contract.Ensures(Contract.Result>() != null); - Contract.Ensures(Contract.Result>().IsReadOnly); - return this.bigBlocks.AsReadOnly(); - } - } - - public List PrefixCommands; - - public readonly IToken /*!*/ - EndCurly; - - public StmtList ParentContext; - public BigBlock ParentBigBlock; - - private readonly HashSet /*!*/ - labels = new HashSet(); - - public void AddLabel(string label) - { - labels.Add(label); - } - - public IEnumerable /*!*/ Labels - { - get - { - Contract.Ensures(cce.NonNullElements(Contract.Result /*!*/>())); - return this.labels.AsEnumerable(); - } - } - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(EndCurly != null); - Contract.Invariant(cce.NonNullElements(this.bigBlocks)); - Contract.Invariant(cce.NonNullElements(this.labels)); - } - - public StmtList(IList /*!*/ bigblocks, IToken endCurly) - { - Contract.Requires(endCurly != null); - Contract.Requires(cce.NonNullElements(bigblocks)); - Contract.Requires(bigblocks.Count > 0); - this.bigBlocks = new List(bigblocks); - this.EndCurly = endCurly; - } - - // prints the list of statements, not the surrounding curly braces - public void Emit(TokenTextWriter stream, int level) - { - Contract.Requires(stream != null); - bool needSeperator = false; - foreach (BigBlock b in BigBlocks) - { - Contract.Assert(b != null); - Contract.Assume(cce.IsPeerConsistent(b)); - if (needSeperator) - { - stream.WriteLine(); - } - - b.Emit(stream, level); - needSeperator = true; - } - } - - /// - /// Tries to insert the commands "prefixCmds" at the beginning of the first block - /// of the StmtList, and returns "true" iff it succeeded. - /// In the event of success, the "suggestedLabel" returns as the name of the - /// block inside StmtList where "prefixCmds" were inserted. This name may be the - /// same as the one passed in, in case this StmtList has no preference as to what - /// to call its first block. In the event of failure, "suggestedLabel" is returned - /// as its input value. - /// Note, to be conservative (that is, ignoring the possible optimization that this - /// method enables), this method can do nothing and return false. - /// - public bool PrefixFirstBlock([Captured] List prefixCmds, ref string suggestedLabel) - { - Contract.Requires(suggestedLabel != null); - Contract.Requires(prefixCmds != null); - Contract.Ensures(Contract.Result() || - cce.Owner.None(prefixCmds)); // "prefixCmds" is captured only on success - Contract.Assume(PrefixCommands == null); // prefix has not been used - - BigBlock bb0 = BigBlocks[0]; - if (prefixCmds.Count == 0) - { - // This is always a success, since there is nothing to insert. Now, decide - // which name to use for the first block. - if (bb0.Anonymous) - { - bb0.LabelName = suggestedLabel; - } - else - { - Contract.Assert(bb0.LabelName != null); - suggestedLabel = bb0.LabelName; - } - - return true; - } - else - { - // There really is something to insert. We can do this inline only if the first - // block is anonymous (which implies there is no branch to it from within the block). - if (bb0.Anonymous) - { - PrefixCommands = prefixCmds; - bb0.LabelName = suggestedLabel; - return true; - } - else - { - return false; - } - } - } - } - - /// - /// The AST for Boogie structured commands was designed to support backward compatibility with - /// the Boogie unstructured commands. This has made the structured commands hard to construct. - /// The StmtListBuilder class makes it easier to build structured commands. - /// - public class StmtListBuilder - { - readonly List /*!*/ bigBlocks = new(); - - string label; - List simpleCmds; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(cce.NonNullElements(bigBlocks)); - } - - void Dump(StructuredCmd scmd, TransferCmd tcmd) - { - Contract.Requires(scmd == null || tcmd == null); - Contract.Ensures(label == null && simpleCmds == null); - if (label == null && simpleCmds == null && scmd == null && tcmd == null) - { - // nothing to do - } - else - { - if (simpleCmds == null) - { - simpleCmds = new List(); - } - - bigBlocks.Add(new BigBlock(Token.NoToken, label, simpleCmds, scmd, tcmd)); - label = null; - simpleCmds = null; - } - } - - /// - /// Collects the StmtList built so far and returns it. The StmtListBuilder should no longer - /// be used once this method has been invoked. - /// - public StmtList Collect(IToken endCurlyBrace) - { - Contract.Requires(endCurlyBrace != null); - Contract.Ensures(Contract.Result() != null); - Dump(null, null); - if (bigBlocks.Count == 0) - { - simpleCmds = new List(); // the StmtList constructor doesn't like an empty list of BigBlock's - Dump(null, null); - } - - return new StmtList(bigBlocks, endCurlyBrace); - } - - public void Add(Cmd cmd) - { - Contract.Requires(cmd != null); - if (simpleCmds == null) - { - simpleCmds = new List(); - } - - simpleCmds.Add(cmd); - } - - public void Add(StructuredCmd scmd) - { - Contract.Requires(scmd != null); - Dump(scmd, null); - } - - public void Add(TransferCmd tcmd) - { - Contract.Requires(tcmd != null); - Dump(null, tcmd); - } - - public void AddLabelCmd(string label) - { - Contract.Requires(label != null); - Dump(null, null); - this.label = label; - } - - public void AddLocalVariable(string name) - { - Contract.Requires(name != null); - // TODO - } - } - - [ContractClass(typeof(StructuredCmdContracts))] - public abstract class StructuredCmd - { - private IToken /*!*/ - _tok; - - public IToken /*!*/ tok - { - get - { - Contract.Ensures(Contract.Result() != null); - return this._tok; - } - set - { - Contract.Requires(value != null); - this._tok = value; - } - } - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(this._tok != null); - } - - public StructuredCmd(IToken tok) - { - Contract.Requires(tok != null); - this._tok = tok; - } - - public abstract void Emit(TokenTextWriter /*!*/ stream, int level); - } - - [ContractClassFor(typeof(StructuredCmd))] - public abstract class StructuredCmdContracts : StructuredCmd - { - public override void Emit(TokenTextWriter stream, int level) - { - Contract.Requires(stream != null); - throw new NotImplementedException(); - } - - public StructuredCmdContracts() : base(null) - { - } - } - - public class WhileCmd : StructuredCmd - { - [Peer] public Expr Guard; - - public List Invariants; - - public List Yields; - - public StmtList Body; - - [ContractInvariantMethod] - void ObjectInvariant() - { - Contract.Invariant(Body != null); - Contract.Invariant(cce.NonNullElements(Invariants)); - } - - public WhileCmd(IToken tok, [Captured] Expr guard, List invariants, List yields, StmtList body) - : base(tok) - { - Contract.Requires(cce.NonNullElements(invariants)); - Contract.Requires(body != null); - Contract.Requires(tok != null); - this.Guard = guard; - this.Invariants = invariants; - this.Yields = yields; - this.Body = body; - } - - public override void Emit(TokenTextWriter stream, int level) - { - stream.Write(level, "while ("); - if (Guard == null) - { - stream.Write("*"); - } - else - { - Guard.Emit(stream); - } - - stream.WriteLine(")"); - - foreach (var yield in Yields) - { - stream.Write(level + 1, "invariant"); - yield.Emit(stream, level + 1); - } - foreach (var inv in Invariants) - { - if (inv is AssumeCmd) - { - stream.Write(level + 1, "free invariant "); - } - else - { - stream.Write(level + 1, "invariant "); - } - - Cmd.EmitAttributes(stream, inv.Attributes); - inv.Expr.Emit(stream); - stream.WriteLine(";"); - } - - stream.WriteLine(level, "{"); - Body.Emit(stream, level + 1); - stream.WriteLine(level, "}"); - } - } - - public class BreakCmd : StructuredCmd - { - public string Label; - public BigBlock BreakEnclosure; - - public BreakCmd(IToken tok, string label) - : base(tok) - { - Contract.Requires(tok != null); - this.Label = label; - } - - public override void Emit(TokenTextWriter stream, int level) - { - if (Label == null) - { - stream.WriteLine(level, "break;"); - } - else - { - stream.WriteLine(level, "break {0};", Label); - } - } - } - - //--------------------------------------------------------------------- - // Block - - //--------------------------------------------------------------------- - // Commands [ContractClassFor(typeof(Cmd))] public abstract class CmdContracts : Cmd { diff --git a/Source/Core/AST/CallCmd.cs b/Source/Core/AST/Commands/CallCmd.cs similarity index 100% rename from Source/Core/AST/CallCmd.cs rename to Source/Core/AST/Commands/CallCmd.cs diff --git a/Source/Core/AST/ChangeScope.cs b/Source/Core/AST/Commands/ChangeScope.cs similarity index 100% rename from Source/Core/AST/ChangeScope.cs rename to Source/Core/AST/Commands/ChangeScope.cs diff --git a/Source/Core/AST/HideRevealCmd.cs b/Source/Core/AST/Commands/HideRevealCmd.cs similarity index 100% rename from Source/Core/AST/HideRevealCmd.cs rename to Source/Core/AST/Commands/HideRevealCmd.cs diff --git a/Source/Core/AST/HigherBoogie/BigBlock.cs b/Source/Core/AST/HigherBoogie/BigBlock.cs new file mode 100644 index 000000000..dc0370a4a --- /dev/null +++ b/Source/Core/AST/HigherBoogie/BigBlock.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class BigBlock +{ + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(tok != null); + Contract.Invariant(Anonymous || this.labelName != null); + Contract.Invariant(this._ec == null || this._tc == null); + Contract.Invariant(this._simpleCmds != null); + } + + public readonly IToken /*!*/ + tok; + + public readonly bool Anonymous; + + private string labelName; + + public string LabelName + { + get + { + Contract.Ensures(Anonymous || Contract.Result() != null); + return this.labelName; + } + set + { + Contract.Requires(Anonymous || value != null); + this.labelName = value; + } + } + + [Rep] private List /*!*/ _simpleCmds; + + /// + /// These come before the structured command + /// + public List /*!*/ simpleCmds + { + get + { + Contract.Ensures(Contract.Result>() != null); + return this._simpleCmds; + } + set + { + Contract.Requires(value != null); + this._simpleCmds = value; + } + } + + private StructuredCmd _ec; + + public StructuredCmd ec + { + get { return this._ec; } + set + { + Contract.Requires(value == null || this.tc == null); + this._ec = value; + } + } + + private TransferCmd _tc; + + public TransferCmd tc + { + get { return this._tc; } + set + { + Contract.Requires(value == null || this.ec == null); + this._tc = value; + } + } + + public BigBlock SuccessorBigBlock; // semantic successor (may be a back-edge, pointing back to enclosing while statement); null if successor is end of procedure body (or if field has not yet been initialized) + + public BigBlock(IToken tok, string labelName, [Captured] List simpleCmds, StructuredCmd ec, TransferCmd tc) + { + Contract.Requires(simpleCmds != null); + Contract.Requires(tok != null); + Contract.Requires(ec == null || tc == null); + this.tok = tok; + this.Anonymous = labelName == null; + this.labelName = labelName; + this._simpleCmds = simpleCmds; + this._ec = ec; + this._tc = tc; + } + + public void Emit(TokenTextWriter stream, int level) + { + Contract.Requires(stream != null); + if (!Anonymous) + { + stream.WriteLine(level, "{0}:", + stream.Options.PrintWithUniqueASTIds + ? String.Format("h{0}^^{1}", this.GetHashCode(), this.LabelName) + : this.LabelName); + } + + foreach (Cmd /*!*/ c in this.simpleCmds) + { + Contract.Assert(c != null); + c.Emit(stream, level + 1); + } + + if (this.ec != null) + { + this.ec.Emit(stream, level + 1); + } + else if (this.tc != null) + { + this.tc.Emit(stream, level + 1); + } + } +} \ No newline at end of file diff --git a/Source/Core/AST/BigBlocksResolutionContext.cs b/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs similarity index 99% rename from Source/Core/AST/BigBlocksResolutionContext.cs rename to Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs index 1dfdcca23..bbc781606 100644 --- a/Source/Core/AST/BigBlocksResolutionContext.cs +++ b/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs @@ -323,7 +323,7 @@ void RecordSuccessors(StmtList stmtList, BigBlock successor) for (int i = stmtList.BigBlocks.Count; 0 <= --i;) { BigBlock big = stmtList.BigBlocks[i]; - big.successorBigBlock = successor; + big.SuccessorBigBlock = successor; if (big.ec is WhileCmd) { @@ -591,9 +591,9 @@ TransferCmd GotoSuccessor(IToken tok, BigBlock b) Contract.Requires(b != null); Contract.Requires(tok != null); Contract.Ensures(Contract.Result() != null); - if (b.successorBigBlock != null) + if (b.SuccessorBigBlock != null) { - return new GotoCmd(tok, new List {b.successorBigBlock.LabelName}); + return new GotoCmd(tok, new List {b.SuccessorBigBlock.LabelName}); } else { diff --git a/Source/Core/AST/HigherBoogie/BreakCmd.cs b/Source/Core/AST/HigherBoogie/BreakCmd.cs new file mode 100644 index 000000000..3ea89b616 --- /dev/null +++ b/Source/Core/AST/HigherBoogie/BreakCmd.cs @@ -0,0 +1,28 @@ +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class BreakCmd : StructuredCmd +{ + public string Label; + public BigBlock BreakEnclosure; + + public BreakCmd(IToken tok, string label) + : base(tok) + { + Contract.Requires(tok != null); + this.Label = label; + } + + public override void Emit(TokenTextWriter stream, int level) + { + if (Label == null) + { + stream.WriteLine(level, "break;"); + } + else + { + stream.WriteLine(level, "break {0};", Label); + } + } +} \ No newline at end of file diff --git a/Source/Core/AST/IfCmd.cs b/Source/Core/AST/HigherBoogie/IfCmd.cs similarity index 100% rename from Source/Core/AST/IfCmd.cs rename to Source/Core/AST/HigherBoogie/IfCmd.cs diff --git a/Source/Core/AST/HigherBoogie/StmtList.cs b/Source/Core/AST/HigherBoogie/StmtList.cs new file mode 100644 index 000000000..51407e1c7 --- /dev/null +++ b/Source/Core/AST/HigherBoogie/StmtList.cs @@ -0,0 +1,134 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; + +namespace Microsoft.Boogie; + +public class StmtList +{ + [Rep] private readonly List /*!*/ bigBlocks; + + public IList /*!*/ BigBlocks + { + get + { + Contract.Ensures(Contract.Result>() != null); + Contract.Ensures(Contract.Result>().IsReadOnly); + return this.bigBlocks.AsReadOnly(); + } + } + + public List PrefixCommands; + + public readonly IToken /*!*/ + EndCurly; + + public StmtList ParentContext; + public BigBlock ParentBigBlock; + + private readonly HashSet /*!*/ + labels = new HashSet(); + + public void AddLabel(string label) + { + labels.Add(label); + } + + public IEnumerable /*!*/ Labels + { + get + { + Contract.Ensures(cce.NonNullElements(Contract.Result /*!*/>())); + return this.labels.AsEnumerable(); + } + } + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(EndCurly != null); + Contract.Invariant(cce.NonNullElements(this.bigBlocks)); + Contract.Invariant(cce.NonNullElements(this.labels)); + } + + public StmtList(IList /*!*/ bigblocks, IToken endCurly) + { + Contract.Requires(endCurly != null); + Contract.Requires(cce.NonNullElements(bigblocks)); + Contract.Requires(bigblocks.Count > 0); + this.bigBlocks = new List(bigblocks); + this.EndCurly = endCurly; + } + + // prints the list of statements, not the surrounding curly braces + public void Emit(TokenTextWriter stream, int level) + { + Contract.Requires(stream != null); + bool needSeperator = false; + foreach (BigBlock b in BigBlocks) + { + Contract.Assert(b != null); + Contract.Assume(cce.IsPeerConsistent(b)); + if (needSeperator) + { + stream.WriteLine(); + } + + b.Emit(stream, level); + needSeperator = true; + } + } + + /// + /// Tries to insert the commands "prefixCmds" at the beginning of the first block + /// of the StmtList, and returns "true" iff it succeeded. + /// In the event of success, the "suggestedLabel" returns as the name of the + /// block inside StmtList where "prefixCmds" were inserted. This name may be the + /// same as the one passed in, in case this StmtList has no preference as to what + /// to call its first block. In the event of failure, "suggestedLabel" is returned + /// as its input value. + /// Note, to be conservative (that is, ignoring the possible optimization that this + /// method enables), this method can do nothing and return false. + /// + public bool PrefixFirstBlock([Captured] List prefixCmds, ref string suggestedLabel) + { + Contract.Requires(suggestedLabel != null); + Contract.Requires(prefixCmds != null); + Contract.Ensures(Contract.Result() || + cce.Owner.None(prefixCmds)); // "prefixCmds" is captured only on success + Contract.Assume(PrefixCommands == null); // prefix has not been used + + BigBlock bb0 = BigBlocks[0]; + if (prefixCmds.Count == 0) + { + // This is always a success, since there is nothing to insert. Now, decide + // which name to use for the first block. + if (bb0.Anonymous) + { + bb0.LabelName = suggestedLabel; + } + else + { + Contract.Assert(bb0.LabelName != null); + suggestedLabel = bb0.LabelName; + } + + return true; + } + else + { + // There really is something to insert. We can do this inline only if the first + // block is anonymous (which implies there is no branch to it from within the block). + if (bb0.Anonymous) + { + PrefixCommands = prefixCmds; + bb0.LabelName = suggestedLabel; + return true; + } + else + { + return false; + } + } + } +} \ No newline at end of file diff --git a/Source/Core/AST/HigherBoogie/StmtListBuilder.cs b/Source/Core/AST/HigherBoogie/StmtListBuilder.cs new file mode 100644 index 000000000..dd7b67666 --- /dev/null +++ b/Source/Core/AST/HigherBoogie/StmtListBuilder.cs @@ -0,0 +1,98 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +/// +/// The AST for Boogie structured commands was designed to support backward compatibility with +/// the Boogie unstructured commands. This has made the structured commands hard to construct. +/// The StmtListBuilder class makes it easier to build structured commands. +/// +public class StmtListBuilder +{ + readonly List /*!*/ bigBlocks = new(); + + string label; + List simpleCmds; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(cce.NonNullElements(bigBlocks)); + } + + void Dump(StructuredCmd scmd, TransferCmd tcmd) + { + Contract.Requires(scmd == null || tcmd == null); + Contract.Ensures(label == null && simpleCmds == null); + if (label == null && simpleCmds == null && scmd == null && tcmd == null) + { + // nothing to do + } + else + { + if (simpleCmds == null) + { + simpleCmds = new List(); + } + + bigBlocks.Add(new BigBlock(Token.NoToken, label, simpleCmds, scmd, tcmd)); + label = null; + simpleCmds = null; + } + } + + /// + /// Collects the StmtList built so far and returns it. The StmtListBuilder should no longer + /// be used once this method has been invoked. + /// + public StmtList Collect(IToken endCurlyBrace) + { + Contract.Requires(endCurlyBrace != null); + Contract.Ensures(Contract.Result() != null); + Dump(null, null); + if (bigBlocks.Count == 0) + { + simpleCmds = new List(); // the StmtList constructor doesn't like an empty list of BigBlock's + Dump(null, null); + } + + return new StmtList(bigBlocks, endCurlyBrace); + } + + public void Add(Cmd cmd) + { + Contract.Requires(cmd != null); + if (simpleCmds == null) + { + simpleCmds = new List(); + } + + simpleCmds.Add(cmd); + } + + public void Add(StructuredCmd scmd) + { + Contract.Requires(scmd != null); + Dump(scmd, null); + } + + public void Add(TransferCmd tcmd) + { + Contract.Requires(tcmd != null); + Dump(null, tcmd); + } + + public void AddLabelCmd(string label) + { + Contract.Requires(label != null); + Dump(null, null); + this.label = label; + } + + public void AddLocalVariable(string name) + { + Contract.Requires(name != null); + // TODO + } +} \ No newline at end of file diff --git a/Source/Core/AST/HigherBoogie/StructuredCmd.cs b/Source/Core/AST/HigherBoogie/StructuredCmd.cs new file mode 100644 index 000000000..f95ef14db --- /dev/null +++ b/Source/Core/AST/HigherBoogie/StructuredCmd.cs @@ -0,0 +1,38 @@ +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +[ContractClass(typeof(StructuredCmdContracts))] +public abstract class StructuredCmd +{ + private IToken /*!*/ + _tok; + + public IToken /*!*/ tok + { + get + { + Contract.Ensures(Contract.Result() != null); + return this._tok; + } + set + { + Contract.Requires(value != null); + this._tok = value; + } + } + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(this._tok != null); + } + + public StructuredCmd(IToken tok) + { + Contract.Requires(tok != null); + this._tok = tok; + } + + public abstract void Emit(TokenTextWriter /*!*/ stream, int level); +} \ No newline at end of file diff --git a/Source/Core/AST/HigherBoogie/StructuredCmdContracts.cs b/Source/Core/AST/HigherBoogie/StructuredCmdContracts.cs new file mode 100644 index 000000000..b363253d5 --- /dev/null +++ b/Source/Core/AST/HigherBoogie/StructuredCmdContracts.cs @@ -0,0 +1,18 @@ +using System; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +[ContractClassFor(typeof(StructuredCmd))] +public abstract class StructuredCmdContracts : StructuredCmd +{ + public override void Emit(TokenTextWriter stream, int level) + { + Contract.Requires(stream != null); + throw new NotImplementedException(); + } + + public StructuredCmdContracts() : base(null) + { + } +} \ No newline at end of file diff --git a/Source/Core/AST/HigherBoogie/WhileCmd.cs b/Source/Core/AST/HigherBoogie/WhileCmd.cs new file mode 100644 index 000000000..135bd43ae --- /dev/null +++ b/Source/Core/AST/HigherBoogie/WhileCmd.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using System.Diagnostics.Contracts; + +namespace Microsoft.Boogie; + +public class WhileCmd : StructuredCmd +{ + [Peer] public Expr Guard; + + public List Invariants; + + public List Yields; + + public StmtList Body; + + [ContractInvariantMethod] + void ObjectInvariant() + { + Contract.Invariant(Body != null); + Contract.Invariant(cce.NonNullElements(Invariants)); + } + + public WhileCmd(IToken tok, [Captured] Expr guard, List invariants, List yields, StmtList body) + : base(tok) + { + Contract.Requires(cce.NonNullElements(invariants)); + Contract.Requires(body != null); + Contract.Requires(tok != null); + this.Guard = guard; + this.Invariants = invariants; + this.Yields = yields; + this.Body = body; + } + + public override void Emit(TokenTextWriter stream, int level) + { + stream.Write(level, "while ("); + if (Guard == null) + { + stream.Write("*"); + } + else + { + Guard.Emit(stream); + } + + stream.WriteLine(")"); + + foreach (var yield in Yields) + { + stream.Write(level + 1, "invariant"); + yield.Emit(stream, level + 1); + } + foreach (var inv in Invariants) + { + if (inv is AssumeCmd) + { + stream.Write(level + 1, "free invariant "); + } + else + { + stream.Write(level + 1, "invariant "); + } + + Cmd.EmitAttributes(stream, inv.Attributes); + inv.Expr.Emit(stream); + stream.WriteLine(";"); + } + + stream.WriteLine(level, "{"); + Body.Emit(stream, level + 1); + stream.WriteLine(level, "}"); + } +} \ No newline at end of file diff --git a/Source/Core/AST/Blocks/Block.cs b/Source/Core/AST/LowerBoogie/Block.cs similarity index 100% rename from Source/Core/AST/Blocks/Block.cs rename to Source/Core/AST/LowerBoogie/Block.cs diff --git a/Source/Core/AST/Blocks/GotoCmd.cs b/Source/Core/AST/LowerBoogie/GotoCmd.cs similarity index 100% rename from Source/Core/AST/Blocks/GotoCmd.cs rename to Source/Core/AST/LowerBoogie/GotoCmd.cs diff --git a/Source/Core/AST/Blocks/ReturnCmd.cs b/Source/Core/AST/LowerBoogie/ReturnCmd.cs similarity index 100% rename from Source/Core/AST/Blocks/ReturnCmd.cs rename to Source/Core/AST/LowerBoogie/ReturnCmd.cs diff --git a/Source/Core/AST/Blocks/TransferCmd.cs b/Source/Core/AST/LowerBoogie/TransferCmd.cs similarity index 100% rename from Source/Core/AST/Blocks/TransferCmd.cs rename to Source/Core/AST/LowerBoogie/TransferCmd.cs diff --git a/Source/Core/AST/Blocks/TransferCmdContracts.cs b/Source/Core/AST/LowerBoogie/TransferCmdContracts.cs similarity index 100% rename from Source/Core/AST/Blocks/TransferCmdContracts.cs rename to Source/Core/AST/LowerBoogie/TransferCmdContracts.cs From fa0019d7b3422893defbb875538392deea2e3ced Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 12:52:36 +0200 Subject: [PATCH 66/70] Refactoring --- .../BigBlocksResolutionContext.cs | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs b/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs index bbc781606..a2853c336 100644 --- a/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs +++ b/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs @@ -104,7 +104,7 @@ public List /*!*/ Blocks NameAnonymousBlocks(stmtList); // determine successor blocks - RecordSuccessors(stmtList, null); + AssignSuccessors(stmtList, null); if (this.errorHandler.count == startErrorCount) { @@ -288,61 +288,56 @@ void CheckLegalLabels(StmtList stmtList, StmtList parentContext, BigBlock parent } } - void NameAnonymousBlocks(StmtList stmtList) + private void NameAnonymousBlocks(StmtList stmtList) { Contract.Requires(stmtList != null); foreach (BigBlock b in stmtList.BigBlocks) { - if (b.LabelName == null) - { - b.LabelName = FreshPrefix(); - } + b.LabelName ??= FreshPrefix(); - if (b.ec is WhileCmd) + if (b.ec is WhileCmd whileCmd) { - WhileCmd wcmd = (WhileCmd) b.ec; - NameAnonymousBlocks(wcmd.Body); + NameAnonymousBlocks(whileCmd.Body); } else { - for (IfCmd ifcmd = b.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.ElseIf) + for (IfCmd ifCmd = b.ec as IfCmd; ifCmd != null; ifCmd = ifCmd.ElseIf) { - NameAnonymousBlocks(ifcmd.Thn); - if (ifcmd.ElseBlock != null) + NameAnonymousBlocks(ifCmd.Thn); + if (ifCmd.ElseBlock != null) { - NameAnonymousBlocks(ifcmd.ElseBlock); + NameAnonymousBlocks(ifCmd.ElseBlock); } } } } } - void RecordSuccessors(StmtList stmtList, BigBlock successor) + private static void AssignSuccessors(StmtList stmtList, BigBlock next) { Contract.Requires(stmtList != null); for (int i = stmtList.BigBlocks.Count; 0 <= --i;) { - BigBlock big = stmtList.BigBlocks[i]; - big.SuccessorBigBlock = successor; + var current = stmtList.BigBlocks[i]; + current.SuccessorBigBlock = next; - if (big.ec is WhileCmd) + if (current.ec is WhileCmd whileCmd) { - WhileCmd wcmd = (WhileCmd) big.ec; - RecordSuccessors(wcmd.Body, big); + AssignSuccessors(whileCmd.Body, current); } else { - for (IfCmd ifcmd = big.ec as IfCmd; ifcmd != null; ifcmd = ifcmd.ElseIf) + for (IfCmd ifCmd = current.ec as IfCmd; ifCmd != null; ifCmd = ifCmd.ElseIf) { - RecordSuccessors(ifcmd.Thn, successor); - if (ifcmd.ElseBlock != null) + AssignSuccessors(ifCmd.Thn, next); + if (ifCmd.ElseBlock != null) { - RecordSuccessors(ifcmd.ElseBlock, successor); + AssignSuccessors(ifCmd.ElseBlock, next); } } } - successor = big; + next = current; } } @@ -586,7 +581,7 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) } } - TransferCmd GotoSuccessor(IToken tok, BigBlock b) + private static TransferCmd GotoSuccessor(IToken tok, BigBlock b) { Contract.Requires(b != null); Contract.Requires(tok != null); From ccdf5c8a953c673616693ee79f43d600ee44e429 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 13:32:02 +0200 Subject: [PATCH 67/70] Add back comment --- Source/Core/AST/LowerBoogie/Block.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/AST/LowerBoogie/Block.cs b/Source/Core/AST/LowerBoogie/Block.cs index 89a6c08cd..8b43370ab 100644 --- a/Source/Core/AST/LowerBoogie/Block.cs +++ b/Source/Core/AST/LowerBoogie/Block.cs @@ -7,7 +7,7 @@ namespace Microsoft.Boogie; public sealed class Block : Absy { - public string Label { get; set; } + public string Label { get; set; } // Note, Label is mostly readonly, but it can change to the name of a nearby block during block coalescing and empty-block removal [field: Rep] [field: ElementsPeer] From 7c69e05a40d6baef369db7cc7fc7dea66551136a Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 13:20:00 +0200 Subject: [PATCH 68/70] Part refactoring part prep for allow_split attribute --- Source/Core/AST/AbsyQuant.cs | 10 ------ .../BigBlocksResolutionContext.cs | 26 ++++++++++----- Source/Core/AST/HigherBoogie/IfCmd.cs | 1 + Source/Core/AST/ICarriesAttributes.cs | 33 ++++++++++++++----- Source/Core/AST/LowerBoogie/GotoCmd.cs | 20 +++++------ Source/Graph/Graph.cs | 28 +++++++++------- 6 files changed, 68 insertions(+), 50 deletions(-) diff --git a/Source/Core/AST/AbsyQuant.cs b/Source/Core/AST/AbsyQuant.cs index 301a3bf70..a385b2e6e 100644 --- a/Source/Core/AST/AbsyQuant.cs +++ b/Source/Core/AST/AbsyQuant.cs @@ -543,16 +543,6 @@ public static Expr FindExprAttribute(QKeyValue kv, string name) return null; } - // Return 'true' if {:name true} or {:name} is an attribute in 'kv' - public static bool FindBoolAttribute(QKeyValue kv, string name) - { - Contract.Requires(name != null); - kv = FindAttribute(kv, qkv => qkv.Key == name && (qkv.Params.Count == 0 || - (qkv.Params.Count == 1 && qkv.Params[0] is LiteralExpr && - ((LiteralExpr) qkv.Params[0]).IsTrue))); - return kv != null; - } - public static int FindIntAttribute(QKeyValue kv, string name, int defl) { Contract.Requires(name != null); diff --git a/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs b/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs index a2853c336..b0103fc25 100644 --- a/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs +++ b/Source/Core/AST/HigherBoogie/BigBlocksResolutionContext.cs @@ -163,11 +163,11 @@ void CheckLegalLabels(StmtList stmtList, StmtList parentContext, BigBlock parent stmtList.ParentBigBlock = parentBigBlock; // record the labels declared in this StmtList - foreach (BigBlock b in stmtList.BigBlocks) + foreach (BigBlock bigBLock in stmtList.BigBlocks) { - if (b.LabelName != null) + if (bigBLock.LabelName != null) { - string n = b.LabelName; + string n = bigBLock.LabelName; if (n.StartsWith(prefix)) { if (prefix.Length < n.Length && n[prefix.Length] == '0') @@ -180,7 +180,7 @@ void CheckLegalLabels(StmtList stmtList, StmtList parentContext, BigBlock parent } } - stmtList.AddLabel(b.LabelName); + stmtList.AddLabel(bigBLock.LabelName); } } @@ -493,12 +493,16 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) var ssElse = new List(); if (ifCmd.Guard != null) { - var ac = new AssumeCmd(ifCmd.tok, ifCmd.Guard); - ac.Attributes = new QKeyValue(ifCmd.tok, "partition", new List(), null); + var ac = new AssumeCmd(ifCmd.tok, ifCmd.Guard) + { + Attributes = new QKeyValue(ifCmd.tok, "partition", new List(), null) + }; ssThen.Add(ac); - ac = new AssumeCmd(ifCmd.tok, Expr.Not(ifCmd.Guard)); - ac.Attributes = new QKeyValue(ifCmd.tok, "partition", new List(), null); + ac = new AssumeCmd(ifCmd.tok, Expr.Not(ifCmd.Guard)) + { + Attributes = new QKeyValue(ifCmd.tok, "partition", new List(), null) + }; ssElse.Add(ac); } @@ -511,8 +515,12 @@ private void CreateBlocks(StmtList stmtList, string runOffTheEndLabel) } // ... goto Then, Else; + var jump = new GotoCmd(ifCmd.tok, new List {thenLabel, elseLabel}) + { + Attributes = ifCmd.Attributes + }; var jumpBlock = new Block(bigBlock.tok, predLabel, predCmds, - new GotoCmd(ifCmd.tok, new List {thenLabel, elseLabel})); + jump); blocks.Add(jumpBlock); if (!thenGuardTakenCareOf) diff --git a/Source/Core/AST/HigherBoogie/IfCmd.cs b/Source/Core/AST/HigherBoogie/IfCmd.cs index dfbe74009..b7a6551a7 100644 --- a/Source/Core/AST/HigherBoogie/IfCmd.cs +++ b/Source/Core/AST/HigherBoogie/IfCmd.cs @@ -4,6 +4,7 @@ namespace Microsoft.Boogie; public class IfCmd : StructuredCmd { + public QKeyValue Attributes; public Expr Guard; private StmtList /*!*/ thn; diff --git a/Source/Core/AST/ICarriesAttributes.cs b/Source/Core/AST/ICarriesAttributes.cs index 1f9bbf412..a14a846be 100644 --- a/Source/Core/AST/ICarriesAttributes.cs +++ b/Source/Core/AST/ICarriesAttributes.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.Contracts; using System.Linq; namespace Microsoft.Boogie; @@ -40,22 +41,36 @@ public List FindLayers() return layers.Distinct().OrderBy(l => l).ToList(); } - // Look for {:name string} in list of attributes. - public string FindStringAttribute(string name) - { - return QKeyValue.FindStringAttribute(Attributes, name); - } - public void AddStringAttribute(IToken tok, string name, string parameter) - { - Attributes = new QKeyValue(tok, name, new List() {parameter}, Attributes); - } +} +public static class QKeyValueExtensions { + // Return 'true' if {:name true} or {:name} is an attribute in 'kv' + public static bool FindBoolAttribute(this QKeyValue kv, string name) + { + Contract.Requires(name != null); + kv = QKeyValue.FindAttribute(kv, qkv => qkv.Key == name && (qkv.Params.Count == 0 || + (qkv.Params.Count == 1 && qkv.Params[0] is LiteralExpr && + ((LiteralExpr) qkv.Params[0]).IsTrue))); + return kv != null; + } } public static class CarriesAttributesExtensions { + + // Look for {:name string} in list of attributes. + public static string FindStringAttribute(this ICarriesAttributes destination, string name) + { + return QKeyValue.FindStringAttribute(destination.Attributes, name); + } + + public static void AddStringAttribute(this ICarriesAttributes destination, IToken tok, string name, string parameter) + { + destination.Attributes = new QKeyValue(tok, name, new List() {parameter}, destination.Attributes); + } + public static void CopyIdFrom(this ICarriesAttributes destination, IToken tok, ICarriesAttributes src) { var id = src.FindStringAttribute("id"); diff --git a/Source/Core/AST/LowerBoogie/GotoCmd.cs b/Source/Core/AST/LowerBoogie/GotoCmd.cs index 790988969..6b1989b2b 100644 --- a/Source/Core/AST/LowerBoogie/GotoCmd.cs +++ b/Source/Core/AST/LowerBoogie/GotoCmd.cs @@ -5,7 +5,7 @@ namespace Microsoft.Boogie; -public class GotoCmd : TransferCmd +public class GotoCmd : TransferCmd, ICarriesAttributes { [Rep] public List LabelNames; [Rep] public List LabelTargets; @@ -19,27 +19,27 @@ void ObjectInvariant() } [NotDelayed] - public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq) + public GotoCmd(IToken /*!*/ tok, List /*!*/ labels) : base(tok) { Contract.Requires(tok != null); - Contract.Requires(labelSeq != null); - this.LabelNames = labelSeq; + Contract.Requires(labels != null); + this.LabelNames = labels; } - public GotoCmd(IToken /*!*/ tok, List /*!*/ labelSeq, List /*!*/ blockSeq) + public GotoCmd(IToken /*!*/ tok, List /*!*/ labels, List /*!*/ blockSeq) : base(tok) { Contract.Requires(tok != null); - Contract.Requires(labelSeq != null); + Contract.Requires(labels != null); Contract.Requires(blockSeq != null); - Debug.Assert(labelSeq.Count == blockSeq.Count); - for (int i = 0; i < labelSeq.Count; i++) + Debug.Assert(labels.Count == blockSeq.Count); + for (int i = 0; i < labels.Count; i++) { - Debug.Assert(Equals(labelSeq[i], cce.NonNull(blockSeq[i]).Label)); + Debug.Assert(Equals(labels[i], cce.NonNull(blockSeq[i]).Label)); } - this.LabelNames = labelSeq; + this.LabelNames = labels; this.LabelTargets = blockSeq; } diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index 2af21485a..c0c994ae5 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -67,7 +67,7 @@ public class DomRelation private int sourceNum; // (number for) root of the graph private readonly Node source; // root of the graph private readonly Graph graph; - private Dictionary> immediateDominatorMap; + private Dictionary> immediateDominateesMap; [NotDelayed] internal DomRelation(Graph g, Node source) @@ -79,12 +79,12 @@ internal DomRelation(Graph g, Node source) NewComputeDominators(); } - public Dictionary> ImmediateDominatorMap + public Dictionary> ImmediateDominateesMap { get { - Contract.Assume(immediateDominatorMap != null); - return immediateDominatorMap; + Contract.Assume(immediateDominateesMap != null); + return immediateDominateesMap; } } @@ -298,26 +298,25 @@ private void NewComputeDominators() #region Populate the Immediate Dominator Map - int sourceNum = nodeToPostOrderNumber[source]; - immediateDominatorMap = new Dictionary>(); + immediateDominateesMap = new Dictionary>(); for (int i = 1; i <= n; i++) { Node node = postOrderNumberToNode[i]; - Node idomNode = postOrderNumberToNode[nodeNumberToImmediateDominator[i]]; + Node immediateDominator = postOrderNumberToNode[nodeNumberToImmediateDominator[i]]; if (i == sourceNum && nodeNumberToImmediateDominator[i] == sourceNum) { continue; } - if (immediateDominatorMap.ContainsKey(idomNode)) + if (immediateDominateesMap.ContainsKey(immediateDominator)) { - immediateDominatorMap[idomNode].Add(node); + immediateDominateesMap[immediateDominator].Add(node); } else { - List l = new List(); + var l = new List(); l.Add(node); - immediateDominatorMap.Add(idomNode, l); + immediateDominateesMap.Add(immediateDominator, l); } } @@ -415,6 +414,11 @@ public Node LeastCommonAncestor(Node n1, Node n2) int lca = Intersect(num1, num2, nodeNumberToImmediateDominator); return postOrderNumberToNode[lca]; } + + public Node GetImmediateDominator(Node node) + { + return postOrderNumberToNode[nodeNumberToImmediateDominator[nodeToPostOrderNumber[node]]]; + } } public class Graph @@ -696,7 +700,7 @@ public Dictionary> ImmediateDominatorMap dominatorMap = new DomRelation(this, source); } - return dominatorMap.ImmediateDominatorMap; + return dominatorMap.ImmediateDominateesMap; } } From f91ccc5a4800ed64c7c49cc995ea7ddaf86b9a82 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 13:57:18 +0200 Subject: [PATCH 69/70] Fix comp errors --- Source/Core/AST/Absy.cs | 12 +-- Source/Core/AST/Commands/AbsyCmd.cs | 4 +- Source/Core/AST/Commands/CallCmd.cs | 4 +- Source/Core/AST/HigherBoogie/IfCmd.cs | 4 +- Source/Core/AST/Implementation.cs | 2 +- Source/Core/AST/Program.cs | 6 +- .../LiveVariableAnalysis.cs | 2 +- Source/Core/BoogiePL.atg | 10 ++- Source/Core/CivlAttributes.cs | 2 +- Source/Core/FunctionDependencyChecker.cs | 16 ++-- Source/Core/Inline.cs | 2 +- Source/Core/Parser.cs | 6 +- Source/Core/ResolutionContext.cs | 4 +- Source/Core/VariableDependenceAnalyser.cs | 2 +- Source/ExecutionEngine/ExecutionEngine.cs | 2 +- Source/Graph/Graph.cs | 25 +++++- .../Houdini/AnnotationDependenceAnalyser.cs | 8 +- Source/Houdini/Houdini.cs | 2 +- Source/VCGeneration/ConditionGeneration.cs | 10 +-- .../VCGeneration/Prune/DependencyEvaluator.cs | 2 +- Source/VCGeneration/Prune/Pruner.cs | 2 +- Source/VCGeneration/SmokeTester.cs | 2 +- Source/VCGeneration/Splits/BlockRewriter.cs | 86 ++++++++++++++----- .../Splits/FocusAttributeHandler.cs | 2 +- .../Splits/SplitAttributeHandler.cs | 2 +- .../VerificationConditionGenerator.cs | 18 ++-- Source/VCGeneration/Wlp.cs | 4 +- 27 files changed, 156 insertions(+), 85 deletions(-) diff --git a/Source/Core/AST/Absy.cs b/Source/Core/AST/Absy.cs index d8f53dbca..ce8a06d9f 100644 --- a/Source/Core/AST/Absy.cs +++ b/Source/Core/AST/Absy.cs @@ -1300,7 +1300,7 @@ public override void Resolve(ResolutionContext rc) public void ResolveWhere(ResolutionContext rc) { - if (QKeyValue.FindBoolAttribute(Attributes, "assumption") && this.TypedIdent.WhereExpr != null) + if (Attributes.FindBoolAttribute("assumption") && this.TypedIdent.WhereExpr != null) { rc.Error(tok, "assumption variable may not be declared with a where clause"); } @@ -1315,7 +1315,7 @@ public override void Typecheck(TypecheckingContext tc) { (this as ICarriesAttributes).TypecheckAttributes(tc); this.TypedIdent.Typecheck(tc); - if (QKeyValue.FindBoolAttribute(Attributes, "assumption") && !this.TypedIdent.Type.IsBool) + if (Attributes.FindBoolAttribute("assumption") && !this.TypedIdent.Type.IsBool) { tc.Error(tok, "assumption variable must be of type 'bool'"); } @@ -2059,7 +2059,7 @@ public override void Emit(TokenTextWriter stream, int level) stream.Write(this, level, (AlwaysRevealed ? "revealed " : "") + "function "); EmitAttributes(stream); - if (Body != null && !QKeyValue.FindBoolAttribute(Attributes, "inline")) + if (Body != null && !Attributes.FindBoolAttribute("inline")) { Contract.Assert(DefinitionBody == null); // Boogie inlines any function whose .Body field is non-null. The parser populates the .Body field @@ -2069,7 +2069,7 @@ public override void Emit(TokenTextWriter stream, int level) stream.Write("{:inline} "); } - if (DefinitionBody != null && !QKeyValue.FindBoolAttribute(Attributes, "define")) + if (DefinitionBody != null && !Attributes.FindBoolAttribute("define")) { // Boogie defines any function whose .DefinitionBody field is non-null. The parser populates the .DefinitionBody field // if the :define attribute is present, but if someone creates the Boogie file directly as an AST, then @@ -2350,7 +2350,7 @@ public String ErrorMessage public bool CanAlwaysAssume() { - return Free && QKeyValue.FindBoolAttribute(Attributes, "always_assume"); + return Free && Attributes.FindBoolAttribute("always_assume"); } @@ -2490,7 +2490,7 @@ public String ErrorMessage public bool CanAlwaysAssume () { - return Free && QKeyValue.FindBoolAttribute(this.Attributes, "always_assume"); + return Free && Attributes.FindBoolAttribute("always_assume"); } public Ensures(IToken token, bool free, Expr condition, string comment, QKeyValue kv) diff --git a/Source/Core/AST/Commands/AbsyCmd.cs b/Source/Core/AST/Commands/AbsyCmd.cs index e10114c03..b2a2c4c45 100644 --- a/Source/Core/AST/Commands/AbsyCmd.cs +++ b/Source/Core/AST/Commands/AbsyCmd.cs @@ -41,7 +41,7 @@ public static void ComputeChecksums(CoreOptions options, Cmd cmd, Implementation } if (cmd is AssumeCmd assumeCmd - && QKeyValue.FindBoolAttribute(assumeCmd.Attributes, "assumption_variable_initialization")) + && assumeCmd.Attributes.FindBoolAttribute("assumption_variable_initialization")) { // Ignore assumption variable initializations. assumeCmd.Checksum = currentChecksum; @@ -509,7 +509,7 @@ public override void Resolve(ResolutionContext rc) continue; } var decl = lhs.AssignedVariable.Decl; - if (lhs.AssignedVariable.Decl != null && QKeyValue.FindBoolAttribute(decl.Attributes, "assumption")) + if (lhs.AssignedVariable.Decl != null && decl.Attributes.FindBoolAttribute("assumption")) { var rhs = Rhss[i] as NAryExpr; if (rhs == null diff --git a/Source/Core/AST/Commands/CallCmd.cs b/Source/Core/AST/Commands/CallCmd.cs index d39768d22..a58ddfb6e 100644 --- a/Source/Core/AST/Commands/CallCmd.cs +++ b/Source/Core/AST/Commands/CallCmd.cs @@ -1052,12 +1052,12 @@ protected override Cmd ComputeDesugaring(PrintOptions options) #region stratified inlining support - if (QKeyValue.FindBoolAttribute(e.Attributes, "si_fcall")) + if (e.Attributes.FindBoolAttribute("si_fcall")) { assume.Attributes = Attributes; } - if (QKeyValue.FindBoolAttribute(e.Attributes, "candidate")) + if (e.Attributes.FindBoolAttribute("candidate")) { assume.Attributes = new QKeyValue(Token.NoToken, "candidate", new List(), assume.Attributes); assume.Attributes.AddParam(this.callee); diff --git a/Source/Core/AST/HigherBoogie/IfCmd.cs b/Source/Core/AST/HigherBoogie/IfCmd.cs index b7a6551a7..a08dd9501 100644 --- a/Source/Core/AST/HigherBoogie/IfCmd.cs +++ b/Source/Core/AST/HigherBoogie/IfCmd.cs @@ -54,7 +54,8 @@ void ObjectInvariant() Contract.Invariant(this.elseIf == null || this.elseBlock == null); } - public IfCmd(IToken /*!*/ tok, Expr guard, StmtList /*!*/ thn, IfCmd elseIf, StmtList elseBlock) + public IfCmd(IToken /*!*/ tok, Expr guard, StmtList /*!*/ thn, IfCmd elseIf, StmtList elseBlock, + QKeyValue attributes = null) : base(tok) { Contract.Requires(tok != null); @@ -64,6 +65,7 @@ public IfCmd(IToken /*!*/ tok, Expr guard, StmtList /*!*/ thn, IfCmd elseIf, Stm this.thn = thn; this.elseIf = elseIf; this.elseBlock = elseBlock; + Attributes = attributes; } public override void Emit(TokenTextWriter stream, int level) diff --git a/Source/Core/AST/Implementation.cs b/Source/Core/AST/Implementation.cs index 7a6c565ac..d1b497dfb 100644 --- a/Source/Core/AST/Implementation.cs +++ b/Source/Core/AST/Implementation.cs @@ -151,7 +151,7 @@ public bool IsSkipVerification(CoreOptions options) } if (options.StratifiedInlining > 0) { - return !QKeyValue.FindBoolAttribute(Attributes, "entrypoint"); + return !Attributes.FindBoolAttribute("entrypoint"); } return false; diff --git a/Source/Core/AST/Program.cs b/Source/Core/AST/Program.cs index 82ec6e4ba..810b73232 100644 --- a/Source/Core/AST/Program.cs +++ b/Source/Core/AST/Program.cs @@ -63,7 +63,7 @@ public override void Resolve(ResolutionContext rc) ResolveTypes(rc); var prunedTopLevelDeclarations = new List(); - foreach (var d in TopLevelDeclarations.Where(d => !QKeyValue.FindBoolAttribute(d.Attributes, "ignore"))) + foreach (var d in TopLevelDeclarations.Where(d => !d.Attributes.FindBoolAttribute("ignore"))) { // resolve all the declarations that have not been resolved yet if (!(d is TypeCtorDecl || d is TypeSynonymDecl)) @@ -98,7 +98,7 @@ private void ResolveTypes(ResolutionContext rc) // first resolve type constructors foreach (var d in TopLevelDeclarations.OfType()) { - if (!QKeyValue.FindBoolAttribute(d.Attributes, "ignore")) + if (!d.Attributes.FindBoolAttribute("ignore")) { d.Resolve(rc); } @@ -110,7 +110,7 @@ private void ResolveTypes(ResolutionContext rc) foreach (var d in TopLevelDeclarations.OfType()) { Contract.Assert(d != null); - if (!QKeyValue.FindBoolAttribute(d.Attributes, "ignore")) + if (!d.Attributes.FindBoolAttribute("ignore")) { synonymDecls.Add(d); } diff --git a/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs b/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs index 98fdef014..8b5e95ac5 100644 --- a/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs +++ b/Source/Core/Analysis/LiveVariableAnalysis/LiveVariableAnalysis.cs @@ -141,7 +141,7 @@ public void Propagate(Cmd cmd, HashSet /*!*/ liveSet) foreach (IdentifierExpr /*!*/ expr in havocCmd.Vars) { Contract.Assert(expr != null); - if (expr.Decl != null && !(QKeyValue.FindBoolAttribute(expr.Decl.Attributes, "assumption") && + if (expr.Decl != null && !(expr.Decl.Attributes.FindBoolAttribute("assumption") && expr.Decl.Name.StartsWith("a##cached##"))) { liveSet.Remove(expr.Decl); diff --git a/Source/Core/BoogiePL.atg b/Source/Core/BoogiePL.atg index eecb93c6b..5836eb2f2 100644 --- a/Source/Core/BoogiePL.atg +++ b/Source/Core/BoogiePL.atg @@ -529,11 +529,11 @@ Function<.out List/*!*/ ds.> } if (definition != null) { // generate either an axiom or a function body - if (QKeyValue.FindBoolAttribute(kv, "inline")) { - if (QKeyValue.FindBoolAttribute(kv, "define")) + if (kv.FindBoolAttribute("inline")) { + if (kv.FindBoolAttribute("define")) SemErr("function cannot have both :inline and :define attributes"); func.Body = definition; - } else if (QKeyValue.FindBoolAttribute(kv, "define")) { + } else if (kv.FindBoolAttribute("define")) { if (func.TypeParameters.Count > 0) SemErr("function with :define attribute has to be monomorphic"); func.DefinitionBody = func.CreateFunctionDefinition(definition); @@ -1062,8 +1062,10 @@ IfCmd StmtList/*!*/ thn; IfCmd/*!*/ elseIf; IfCmd elseIfOption = null; StmtList/*!*/ els; StmtList elseOption = null; + QKeyValue kv = null; .) "if" (. x = t; .) + { Attribute } Guard "{" StmtList [ "else" @@ -1072,7 +1074,7 @@ IfCmd StmtList (. elseOption = els; .) ) ] - (. ifcmd = new IfCmd(x, guard, thn, elseIfOption, elseOption); .) + (. ifcmd = new IfCmd(x, guard, thn, elseIfOption, elseOption, kv); .) . WhileCmd diff --git a/Source/Core/CivlAttributes.cs b/Source/Core/CivlAttributes.cs index ad5250f33..8c6e77732 100644 --- a/Source/Core/CivlAttributes.cs +++ b/Source/Core/CivlAttributes.cs @@ -127,7 +127,7 @@ public static List FindAllAttributes(this ICarriesAttributes obj, str public static bool HasAttribute(this ICarriesAttributes obj, string name) { - return QKeyValue.FindBoolAttribute(obj.Attributes, name); + return obj.Attributes.FindBoolAttribute(name); } public static bool RemoveAttributes(ICarriesAttributes obj, Func cond) diff --git a/Source/Core/FunctionDependencyChecker.cs b/Source/Core/FunctionDependencyChecker.cs index a3a958fd9..fce5eb802 100644 --- a/Source/Core/FunctionDependencyChecker.cs +++ b/Source/Core/FunctionDependencyChecker.cs @@ -16,17 +16,17 @@ public static bool Check(Program program) { checkingContext.Error(function.tok, "Parameter to :inline attribute on a function must be Boolean"); } - if (QKeyValue.FindBoolAttribute(function.Attributes, "inline") && - QKeyValue.FindBoolAttribute(function.Attributes, "define")) + if (function.Attributes.FindBoolAttribute("inline") && + function.Attributes.FindBoolAttribute("define")) { checkingContext.Error(function.tok, "A function may not have both :inline and :define attributes"); } - if (QKeyValue.FindBoolAttribute(function.Attributes, "inline") && + if (function.Attributes.FindBoolAttribute("inline") && function.Body == null) { checkingContext.Error(function.tok, "Function with :inline attribute must have a body"); } - if (QKeyValue.FindBoolAttribute(function.Attributes, "define") && + if (function.Attributes.FindBoolAttribute("define") && function.DefinitionBody == null) { checkingContext.Error(function.tok, "Function with :define attribute must have a body"); @@ -84,13 +84,13 @@ private FunctionDependencyChecker() public override Function VisitFunction(Function node) { - if (QKeyValue.FindBoolAttribute(node.Attributes, "inline")) + if (node.Attributes.FindBoolAttribute("inline")) { this.enclosingFunction = node; base.Visit(node.Body); this.enclosingFunction = null; } - else if (QKeyValue.FindBoolAttribute(node.Attributes, "define")) + else if (node.Attributes.FindBoolAttribute("define")) { this.enclosingFunction = node; base.Visit(node.DefinitionBody.Args[1]); @@ -103,8 +103,8 @@ public override Expr VisitNAryExpr(NAryExpr node) { if (node.Fun is FunctionCall functionCall) { - if (QKeyValue.FindBoolAttribute(functionCall.Func.Attributes, "inline") || - QKeyValue.FindBoolAttribute(functionCall.Func.Attributes, "define")) + if (functionCall.Func.Attributes.FindBoolAttribute("inline") || + functionCall.Func.Attributes.FindBoolAttribute("define")) { functionDependencyGraph.AddEdge(enclosingFunction, functionCall.Func); } diff --git a/Source/Core/Inline.cs b/Source/Core/Inline.cs index 3162cddbe..d67732fa2 100644 --- a/Source/Core/Inline.cs +++ b/Source/Core/Inline.cs @@ -580,7 +580,7 @@ private Cmd InlinedRequires(CallCmd callCmd, Requires req) private Cmd InlinedEnsures(CallCmd callCmd, Ensures ens) { - if (QKeyValue.FindBoolAttribute(ens.Attributes, "InlineAssume")) + if (ens.Attributes.FindBoolAttribute("InlineAssume")) { return new AssumeCmd(ens.tok, codeCopier.CopyExpr(ens.Condition)); } diff --git a/Source/Core/Parser.cs b/Source/Core/Parser.cs index cc67d930f..68966a376 100644 --- a/Source/Core/Parser.cs +++ b/Source/Core/Parser.cs @@ -501,11 +501,11 @@ void Function(out List/*!*/ ds) { } if (definition != null) { // generate either an axiom or a function body - if (QKeyValue.FindBoolAttribute(kv, "inline")) { - if (QKeyValue.FindBoolAttribute(kv, "define")) + if (kv.FindBoolAttribute("inline")) { + if (kv.FindBoolAttribute("define")) SemErr("function cannot have both :inline and :define attributes"); func.Body = definition; - } else if (QKeyValue.FindBoolAttribute(kv, "define")) { + } else if (kv.FindBoolAttribute("define")) { if (func.TypeParameters.Count > 0) SemErr("function with :define attribute has to be monomorphic"); func.DefinitionBody = func.CreateFunctionDefinition(definition); diff --git a/Source/Core/ResolutionContext.cs b/Source/Core/ResolutionContext.cs index 47fe6bd57..ee240bde7 100644 --- a/Source/Core/ResolutionContext.cs +++ b/Source/Core/ResolutionContext.cs @@ -560,12 +560,12 @@ T SelectNonExtern(T a, T b) where T : Declaration Contract.Result() == b); T ignore, keep; - if (QKeyValue.FindBoolAttribute(a.Attributes, "extern")) + if (a.Attributes.FindBoolAttribute("extern")) { ignore = a; keep = b; } - else if (QKeyValue.FindBoolAttribute(b.Attributes, "extern")) + else if (b.Attributes.FindBoolAttribute("extern")) { ignore = b; keep = a; diff --git a/Source/Core/VariableDependenceAnalyser.cs b/Source/Core/VariableDependenceAnalyser.cs index f9243ac20..ad6f33558 100644 --- a/Source/Core/VariableDependenceAnalyser.cs +++ b/Source/Core/VariableDependenceAnalyser.cs @@ -425,7 +425,7 @@ private HashSet GetControlDependencyVariables(string proc, B foreach (Cmd c in succ.Cmds) { AssumeCmd a = c as AssumeCmd; - if (a != null && QKeyValue.FindBoolAttribute(a.Attributes, "partition")) + if (a != null && a.Attributes.FindBoolAttribute("partition")) { var varCollector = new VariableCollector(); varCollector.VisitExpr(a.Expr); diff --git a/Source/ExecutionEngine/ExecutionEngine.cs b/Source/ExecutionEngine/ExecutionEngine.cs index 7cc6abc0c..f206c6612 100644 --- a/Source/ExecutionEngine/ExecutionEngine.cs +++ b/Source/ExecutionEngine/ExecutionEngine.cs @@ -436,7 +436,7 @@ public PipelineOutcome ResolveAndTypecheck(Program program, string bplFileName, Options.OutputWriter.WriteLine("Datatypes only supported with monomorphic encoding"); return PipelineOutcome.FatalError; } - else if (program.TopLevelDeclarations.OfType().Any(f => QKeyValue.FindBoolAttribute(f.Attributes, "define"))) + else if (program.TopLevelDeclarations.OfType().Any(f => f.Attributes.FindBoolAttribute("define"))) { Options.OutputWriter.WriteLine("Functions with :define attribute only supported with monomorphic encoding"); return PipelineOutcome.FatalError; diff --git a/Source/Graph/Graph.cs b/Source/Graph/Graph.cs index c0c994ae5..3a9861b63 100644 --- a/Source/Graph/Graph.cs +++ b/Source/Graph/Graph.cs @@ -346,12 +346,11 @@ private static int Intersect(int b1, int b2, int[] doms) private void PostOrderVisit(Node /*!*/ n, HashSet visited, ref int currentNumber) { Contract.Requires(n != null); - if (visited.Contains(n)) + if (!visited.Add(n)) { return; } - visited.Add(n); foreach (Node /*!*/ child in graph.Successors(n)) { Contract.Assert(child != null); @@ -419,6 +418,28 @@ public Node GetImmediateDominator(Node node) { return postOrderNumberToNode[nodeNumberToImmediateDominator[nodeToPostOrderNumber[node]]]; } + + public IEnumerable GetNodesUntilImmediateDominator(Node node) + { + var dominator = GetImmediateDominator(node); + + var toVisit = new Stack(graph.Predecessors(node)); + while (toVisit.Any()) + { + var current = toVisit.Pop(); + if (Equals(current, dominator)) + { + continue; + } + + yield return current; + + foreach (var predecessor in graph.Predecessors(node)) + { + toVisit.Push(predecessor); + } + } + } } public class Graph diff --git a/Source/Houdini/AnnotationDependenceAnalyser.cs b/Source/Houdini/AnnotationDependenceAnalyser.cs index e88ca2fb9..0ad80927d 100644 --- a/Source/Houdini/AnnotationDependenceAnalyser.cs +++ b/Source/Houdini/AnnotationDependenceAnalyser.cs @@ -232,7 +232,7 @@ private IEnumerable AnnotationInstances() { yield return new AnnotationInstance(c, impl.Name, p.Expr); } - else if ((p is AssertCmd) && QKeyValue.FindBoolAttribute(p.Attributes, "originated_from_invariant")) + else if ((p is AssertCmd) && p.Attributes.FindBoolAttribute("originated_from_invariant")) { var tag = GetTagFromNonCandidateAttributes(p.Attributes); if (tag != null) @@ -467,7 +467,7 @@ private IEnumerable GetNonCandidateAnnotations() continue; } - if (!QKeyValue.FindBoolAttribute(Assertion.Attributes, "originated_from_invariant")) + if (!Assertion.Attributes.FindBoolAttribute("originated_from_invariant")) { continue; } @@ -528,7 +528,7 @@ private IEnumerable GetNonCandidateAnnotations() private IEnumerable GetCandidates() { return prog.Variables.Where(Item => - QKeyValue.FindBoolAttribute(Item.Attributes, "existential")).Select(Item => Item.Name); + Item.Attributes.FindBoolAttribute("existential")).Select(Item => Item.Name); } @@ -611,7 +611,7 @@ public StagedHoudiniPlan ApplyStages() newCmds.Add(new AssumeCmd(a.tok, Houdini.AddConditionToCandidate(a.Expr, Expr.Ident(stageToCompleteBoolean[Plan.StageForAnnotation(c).GetId()]), c), a.Attributes)); } - else if (QKeyValue.FindBoolAttribute(a.Attributes, "originated_from_invariant")) + else if (a.Attributes.FindBoolAttribute("originated_from_invariant")) { string tag = GetTagFromNonCandidateAttributes(a.Attributes); if (tag == null) diff --git a/Source/Houdini/Houdini.cs b/Source/Houdini/Houdini.cs index 4efc13c77..2afaddbf4 100644 --- a/Source/Houdini/Houdini.cs +++ b/Source/Houdini/Houdini.cs @@ -1617,7 +1617,7 @@ private async Task HoudiniVerifyCurrent(HoudiniSession session, int stage, IRead public static void ApplyAssignment(Program prog, HoudiniOutcome outcome) { var Candidates = prog.TopLevelDeclarations.OfType().Where( - Item => QKeyValue.FindBoolAttribute(Item.Attributes, "existential")).Select(Item => Item.Name); + Item => Item.Attributes.FindBoolAttribute("existential")).Select(Item => Item.Name); // Treat all assertions // TODO: do we need to also consider assumptions? diff --git a/Source/VCGeneration/ConditionGeneration.cs b/Source/VCGeneration/ConditionGeneration.cs index 8c442d59a..58581fe69 100644 --- a/Source/VCGeneration/ConditionGeneration.cs +++ b/Source/VCGeneration/ConditionGeneration.cs @@ -1037,7 +1037,7 @@ protected void TurnIntoPassiveCmd(TextWriter traceWriter, Cmd c, Block enclosing } } else if (pc is AssumeCmd - && QKeyValue.FindBoolAttribute(pc.Attributes, "precondition_previous_snapshot") + && pc.Attributes.FindBoolAttribute("precondition_previous_snapshot") && pc.SugaredCmdChecksum != null) { if (!relevantDoomedAssumpVars.Any() @@ -1062,7 +1062,7 @@ protected void TurnIntoPassiveCmd(TextWriter traceWriter, Cmd c, Block enclosing dropCmd = true; } } - else if (pc is AssumeCmd && QKeyValue.FindBoolAttribute(pc.Attributes, "assumption_variable_initialization")) + else if (pc is AssumeCmd && pc.Attributes.FindBoolAttribute("assumption_variable_initialization")) { var identExpr = pc.Expr as IdentifierExpr; if (identExpr != null && identExpr.Decl != null && !incarnationMap.ContainsKey(identExpr.Decl)) @@ -1210,7 +1210,7 @@ protected void TurnIntoPassiveCmd(TextWriter traceWriter, Cmd c, Block enclosing { var identExpr = assign.Lhss[0].AsExpr as IdentifierExpr; if (identExpr != null && identExpr.Decl != null && - QKeyValue.FindBoolAttribute(identExpr.Decl.Attributes, "assumption") && + identExpr.Decl.Attributes.FindBoolAttribute("assumption") && incarnationMap.TryGetValue(identExpr.Decl, out var incarnation)) { TraceCachingAction(traceWriter, assign, CachingAction.AssumeNegationOfAssumptionVariable); @@ -1238,7 +1238,7 @@ protected void TurnIntoPassiveCmd(TextWriter traceWriter, Cmd c, Block enclosing // invariant) in the previous snapshot and, consequently, the corresponding assumption did not affect the // anything after the loop. We can achieve this by simply not updating/adding it in the incarnation map. List havocVars = hc.Vars.Where(v => - !(QKeyValue.FindBoolAttribute(v.Decl.Attributes, "assumption") && v.Decl.Name.StartsWith("a##cached##"))) + !(v.Decl.Attributes.FindBoolAttribute("assumption") && v.Decl.Name.StartsWith("a##cached##"))) .ToList(); // First, compute the new incarnations foreach (IdentifierExpr ie in havocVars) @@ -1273,7 +1273,7 @@ protected void TurnIntoPassiveCmd(TextWriter traceWriter, Cmd c, Block enclosing // assume v_post ==> v_pre; foreach (IdentifierExpr ie in havocVars) { - if (QKeyValue.FindBoolAttribute(ie.Decl.Attributes, "assumption")) + if (ie.Decl.Attributes.FindBoolAttribute("assumption")) { var preInc = (Expr) (preHavocIncarnationMap[ie.Decl].Clone()); var postInc = (Expr) (incarnationMap[ie.Decl].Clone()); diff --git a/Source/VCGeneration/Prune/DependencyEvaluator.cs b/Source/VCGeneration/Prune/DependencyEvaluator.cs index 85da548a6..8999f8e65 100644 --- a/Source/VCGeneration/Prune/DependencyEvaluator.cs +++ b/Source/VCGeneration/Prune/DependencyEvaluator.cs @@ -26,7 +26,7 @@ public class DependencyEvaluator : ReadOnlyVisitor protected void AddIncoming(Declaration newIncoming) { - if (QKeyValue.FindBoolAttribute(declaration.Attributes, "include_dep")) { + if (declaration.Attributes.FindBoolAttribute("include_dep")) { incomingSets.Add(new[] { newIncoming }); } } diff --git a/Source/VCGeneration/Prune/Pruner.cs b/Source/VCGeneration/Prune/Pruner.cs index 165eb5239..39d479405 100644 --- a/Source/VCGeneration/Prune/Pruner.cs +++ b/Source/VCGeneration/Prune/Pruner.cs @@ -71,7 +71,7 @@ public static IEnumerable GetLiveDeclarations(VCGenOptions options, blocksVisitor.Visit(block); } - var keepRoots = program.TopLevelDeclarations.Where(d => QKeyValue.FindBoolAttribute(d.Attributes, "keep")); + var keepRoots = program.TopLevelDeclarations.Where(d => d.Attributes.FindBoolAttribute("keep")); var reachableDeclarations = GraphAlgorithms.FindReachableNodesInGraphWithMergeNodes(program.DeclarationDependencies, blocksVisitor.Outgoing.Concat(keepRoots).ToHashSet(), TraverseDeclaration).ToHashSet(); return program.TopLevelDeclarations.Where(d => diff --git a/Source/VCGeneration/SmokeTester.cs b/Source/VCGeneration/SmokeTester.cs index 3f49e5207..350567ebb 100644 --- a/Source/VCGeneration/SmokeTester.cs +++ b/Source/VCGeneration/SmokeTester.cs @@ -242,7 +242,7 @@ async Task CheckUnreachable(TextWriter traceWriter, Block cur, List s foreach (Cmd cmd in seq) { AssertCmd assrt = cmd as AssertCmd; - if (assrt != null && QKeyValue.FindBoolAttribute(assrt.Attributes, "PossiblyUnreachable")) + if (assrt != null && assrt.Attributes.FindBoolAttribute("PossiblyUnreachable")) { return false; } diff --git a/Source/VCGeneration/Splits/BlockRewriter.cs b/Source/VCGeneration/Splits/BlockRewriter.cs index 8795387fb..56737e7e2 100644 --- a/Source/VCGeneration/Splits/BlockRewriter.cs +++ b/Source/VCGeneration/Splits/BlockRewriter.cs @@ -48,34 +48,80 @@ public IEnumerable GetSplitsForIsolatedPaths(Block lastBlock, IRead while (blockToVisit.Any()) { var path = blockToVisit.Pop(); var firstBlock = path.Peek(); - IEnumerable predecessors = firstBlock.Predecessors; + List predecessors = firstBlock.Predecessors; if (blocksToInclude != null) { - predecessors = predecessors.Where(blocksToInclude.Contains); + predecessors = predecessors.Where(blocksToInclude.Contains).ToList(); } - var hadPredecessors = false; - foreach (var predecessor in predecessors) { - hadPredecessors = true; - blockToVisit.Push(path.Push(predecessor)); - } - - if (!hadPredecessors) { - var newBlocks = ComputeNewBlocks(path.ToHashSet(), - (oldBlock, newBlock) => { - newBlock.Cmds = oldBlock == lastBlock ? oldBlock.Cmds : oldBlock.Cmds.Select(TransformAssertCmd).ToList(); - if (oldBlock == lastBlock) { - newBlock.TransferCmd = new ReturnCmd(origin); + switch (predecessors.Count) + { + case 0: + var newBlocks = ComputeNewBlocks(path.ToHashSet(), + (oldBlock, newBlock) => { + newBlock.Cmds = oldBlock == lastBlock ? oldBlock.Cmds : oldBlock.Cmds.Select(TransformAssertCmd).ToList(); + if (oldBlock == lastBlock) { + newBlock.TransferCmd = new ReturnCmd(origin); + } + }); + + var nonDominatedBranches = path.Where(block => + !filteredDag.DominatorMap.DominatedBy(lastBlock, block)).ToList(); + + yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), newBlocks); + break; + case 1: + blockToVisit.Push(path.Push(predecessors.First())); + break; + default: + var immediateDominator = Dag.DominatorMap.GetImmediateDominator(firstBlock); + if (immediateDominator.TransferCmd is GotoCmd gotoCmd && gotoCmd.Attributes.FindBoolAttribute("allow_split")) + { + foreach (var predecessor in predecessors) + { + blockToVisit.Push(path.Push(predecessor)); } - }); - - var nonDominatedBranches = path.Where(block => - !filteredDag.DominatorMap.DominatedBy(lastBlock, block)).ToList(); - - yield return CreateSplit(new PathOrigin(origin, nonDominatedBranches), newBlocks); + } + else + { + var nextPath = path; + foreach (var inBetweenNode in Dag.DominatorMap.GetNodesUntilImmediateDominator(firstBlock)) + { + nextPath = nextPath.Push(inBetweenNode); + } + + nextPath = nextPath.Push(immediateDominator); + blockToVisit.Push(nextPath); + } + + break; } + + } } + private static IEnumerable GetNodesBetween(List predecessors, ImmutableStack path, + Block immediateDominator) + { + var smallStack = new Stack(predecessors); + var nextPath = path; + while (smallStack.Any()) + { + var addition = smallStack.Pop(); + if (addition == immediateDominator) + { + continue; + } + + foreach (var pred in addition.Predecessors) + { + smallStack.Push(pred); + } + nextPath = nextPath.Push(addition); + } + return nextPath.Push(immediateDominator); + } + public List ComputeNewBlocks( ISet blocksToInclude, ISet freeAssumeBlocks) { diff --git a/Source/VCGeneration/Splits/FocusAttributeHandler.cs b/Source/VCGeneration/Splits/FocusAttributeHandler.cs index c3db1c586..64554de12 100644 --- a/Source/VCGeneration/Splits/FocusAttributeHandler.cs +++ b/Source/VCGeneration/Splits/FocusAttributeHandler.cs @@ -114,6 +114,6 @@ private static HashSet DominatedBlocks(List topologicallySortedBlo } private static bool IsFocusCmd(Cmd c) { - return c is PredicateCmd p && QKeyValue.FindBoolAttribute(p.Attributes, "focus"); + return c is PredicateCmd p && p.Attributes.FindBoolAttribute("focus"); } } \ No newline at end of file diff --git a/Source/VCGeneration/Splits/SplitAttributeHandler.cs b/Source/VCGeneration/Splits/SplitAttributeHandler.cs index 85bcc73a8..d1e12a2ad 100644 --- a/Source/VCGeneration/Splits/SplitAttributeHandler.cs +++ b/Source/VCGeneration/Splits/SplitAttributeHandler.cs @@ -74,7 +74,7 @@ private static bool ShouldSplitHere(Cmd c) { return false; } - return QKeyValue.FindBoolAttribute(predicateCmd.Attributes, "split_here"); + return predicateCmd.Attributes.FindBoolAttribute("split_here"); } private static Dictionary GetMapFromBlockStartToSplit(List blocks, Dictionary> splitsPerBlock) { diff --git a/Source/VCGeneration/VerificationConditionGenerator.cs b/Source/VCGeneration/VerificationConditionGenerator.cs index 1b957d2ae..83e5b7cd5 100644 --- a/Source/VCGeneration/VerificationConditionGenerator.cs +++ b/Source/VCGeneration/VerificationConditionGenerator.cs @@ -326,7 +326,7 @@ void ExpandAsserts(Implementation impl) attr = ae.Ensures.Attributes; } - if (QKeyValue.FindExprAttribute(attr, "expand") != null || QKeyValue.FindBoolAttribute(attr, "expand")) + if (QKeyValue.FindExprAttribute(attr, "expand") != null || attr.FindBoolAttribute("expand")) { int depth = QKeyValue.FindIntAttribute(attr, "expand", 100); Func fe = e => Expr.Or(a.Expr, e); @@ -699,7 +699,7 @@ public void PassifyImpl(ImplementationRun run, out ModelViewInfo modelViewInfo) new QKeyValue(lvar.tok, "where", new List(new object[] { idExp }), null)); cc.Add(c); } - else if (QKeyValue.FindBoolAttribute(lvar.Attributes, "assumption")) + else if (lvar.Attributes.FindBoolAttribute("assumption")) { cc.Add(new AssumeCmd(lvar.tok, idExp, new QKeyValue(lvar.tok, "assumption_variable_initialization", new List(), null))); @@ -752,7 +752,7 @@ public void PassifyImpl(ImplementationRun run, out ModelViewInfo modelViewInfo) modelViewInfo = new ModelViewInfo(program, impl); Convert2PassiveCmd(run, modelViewInfo); - if (QKeyValue.FindBoolAttribute(impl.Attributes, "may_unverified_instrumentation")) + if (impl.Attributes.FindBoolAttribute("may_unverified_instrumentation")) { InstrumentWithMayUnverifiedConditions(impl, exitBlock); } @@ -922,13 +922,13 @@ static HashSet JoinVariableSets(HashSet c0, HashSet variables) @@ -985,8 +985,8 @@ static bool IsConjunctionOfAssumptionVariables(Expr expr, out HashSet private void HandleSelectiveChecking(Implementation impl) { - if (QKeyValue.FindBoolAttribute(impl.Attributes, "selective_checking") || - QKeyValue.FindBoolAttribute(impl.Proc.Attributes, "selective_checking")) + if (impl.Attributes.FindBoolAttribute("selective_checking") || + impl.Proc.Attributes.FindBoolAttribute("selective_checking")) { var startPoints = new List(); foreach (var b in impl.Blocks) @@ -994,7 +994,7 @@ private void HandleSelectiveChecking(Implementation impl) foreach (Cmd c in b.Cmds) { var p = c as PredicateCmd; - if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) + if (p != null && p.Attributes.FindBoolAttribute("start_checking_here")) { startPoints.Add(b); break; @@ -1047,7 +1047,7 @@ private void HandleSelectiveChecking(Implementation impl) foreach (Cmd c in b.Cmds) { var p = c as PredicateCmd; - if (p != null && QKeyValue.FindBoolAttribute(p.Attributes, "start_checking_here")) + if (p != null && p.Attributes.FindBoolAttribute("start_checking_here")) { copyMode = true; } diff --git a/Source/VCGeneration/Wlp.cs b/Source/VCGeneration/Wlp.cs index 84090f10a..7857e5142 100644 --- a/Source/VCGeneration/Wlp.cs +++ b/Source/VCGeneration/Wlp.cs @@ -212,12 +212,12 @@ internal static VCExpr Cmd(Block b, Cmd cmd, VCExpr N, VCContext ctxt) var assumeId = QKeyValue.FindStringAttribute(ac.Attributes, "id"); if (assumeId != null && ctxt.Options.TrackVerificationCoverage) { - var isTry = QKeyValue.FindBoolAttribute(ac.Attributes, "try"); + var isTry = ac.Attributes.FindBoolAttribute("try"); var v = gen.Variable(assumeId, Microsoft.Boogie.Type.Bool, isTry ? VCExprVarKind.Try : VCExprVarKind.Assume); expr = gen.Function(VCExpressionGenerator.NamedAssumeOp, v, gen.ImpliesSimp(v, expr)); } - var soft = QKeyValue.FindBoolAttribute(ac.Attributes, "soft"); + var soft = ac.Attributes.FindBoolAttribute("soft"); var softWeight = QKeyValue.FindIntAttribute(ac.Attributes, "soft", 0); if ((soft || 0 < softWeight) && assumeId != null) { From 45164d569aa2127fbf2416d71aa714c323496650 Mon Sep 17 00:00:00 2001 From: Remy Willems Date: Fri, 4 Oct 2024 14:00:47 +0200 Subject: [PATCH 70/70] Updates --- Source/Core/Parser.cs | 6 +++++- .../isolateAssertion/isolateAssertion.bpl | 8 +++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Source/Core/Parser.cs b/Source/Core/Parser.cs index 68966a376..0dfe83bdb 100644 --- a/Source/Core/Parser.cs +++ b/Source/Core/Parser.cs @@ -1563,9 +1563,13 @@ void IfCmd(out IfCmd/*!*/ ifcmd) { StmtList/*!*/ thn; IfCmd/*!*/ elseIf; IfCmd elseIfOption = null; StmtList/*!*/ els; StmtList elseOption = null; + QKeyValue kv = null; Expect(57); x = t; + while (la.kind == 26) { + Attribute(ref kv); + } Guard(out guard); Expect(26); StmtList(out thn); @@ -1580,7 +1584,7 @@ void IfCmd(out IfCmd/*!*/ ifcmd) { elseOption = els; } else SynErr(149); } - ifcmd = new IfCmd(x, guard, thn, elseIfOption, elseOption); + ifcmd = new IfCmd(x, guard, thn, elseIfOption, elseOption, kv); } void WhileCmd(out WhileCmd wcmd) { diff --git a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl index 59cd30f93..b8a304765 100644 --- a/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl +++ b/Test/implementationDivision/isolateAssertion/isolateAssertion.bpl @@ -5,13 +5,19 @@ procedure IsolateAssertion(x: int, y: int) { var z: int; z := 0; - if (x > 0) { + if {:allow_split} (x > 0) { z := z + 1; } else { z := z + 2; } if (y > 0) { + z := z + 0; + } else { + z := z + 0; + } + + if {:allow_split} (y > 0) { z := z + 3; } else { z := z + 4;