Skip to content

Commit

Permalink
First optimization: Create sort MagicWandSnapFunction (MWSF) with its…
Browse files Browse the repository at this point in the history
… own function definitions to replace wand maps.
  • Loading branch information
manud99 committed May 6, 2024
1 parent 3b4b76d commit 77324f4
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(declare-fun MWSF_apply ($MWSF $Snap) $Snap)
6 changes: 4 additions & 2 deletions src/main/scala/decider/TermToSMTLib2Converter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class TermToSMTLib2Converter

case sorts.FieldPermFunction() => text("$FPM")
case sorts.PredicatePermFunction() => text("$PPM")

case sorts.MagicWandSnapFunction => text("$MWSF")
}

def convert(d: Decl): String = {
Expand Down Expand Up @@ -314,8 +316,8 @@ class TermToSMTLib2Converter
val docBindings = ssep((bindings.toSeq map (p => parens(render(p._1) <+> render(p._2)))).to(collection.immutable.Seq), space)
parens(text("let") <+> parens(docBindings) <+> render(body))

case MagicWandSnapshot(_, _, wandMap) =>
render(wandMap)
case MagicWandSnapshot(_, _, wandMap) => render(wandMap)
case MWSFLookup(mwsf, snap) => renderApp("MWSF_apply", Seq(mwsf, snap), sorts.Snap)

case _: MagicWandChunkTerm
| _: Quantification =>
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/rules/MagicWandSupporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ object magicWandSupporter extends SymbolicExecutionRules {
val preMark = v3.decider.setPathConditionMark()

v3.decider.prover.comment(s"Create WandMap for wand $wand")
val wandSnapshot = MagicWandSnapshot(freshSnapRoot, snapRhs, v3.decider.fresh("$wm", sorts.Map(sorts.Snap, sorts.Snap)))
val wandSnapshot = MagicWandSnapshot(freshSnapRoot, snapRhs, v3.decider.fresh("mwsf", sorts.MagicWandSnapFunction))
v3.decider.assumeDefinition(wandSnapshot.definition)

// If the wand is part of a quantified expression
Expand Down Expand Up @@ -292,7 +292,7 @@ object magicWandSupporter extends SymbolicExecutionRules {
case Quantification(Forall, wandSnapshot.abstractLhs :: Nil, body: Term, _, _, _, _) => body
case p => p
}),
Trigger(MapLookup(wandSnapshot.wandMap, wandSnapshot.abstractLhs)),
Trigger(MWSFLookup(wandSnapshot.wandMap, wandSnapshot.abstractLhs)),
)

appendToResults(s5, ch, v4.decider.pcs.after(preMark), pcsQuantified +: pcsWithoutAbstractLhs, v4)
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/rules/Producer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -397,14 +397,14 @@ object producer extends ProductionRules {
Q(s2, v1)})

case wand: ast.MagicWand =>
val snapRhs = sf(sorts.Map(sorts.Snap, sorts.Snap), v)
val snapRhs = sf(sorts.MagicWandSnapFunction, v)

// Create Map that takes a snapshot, which represent the values of the consumed LHS of the wand,
// and relates it to the snapshot of the RHS. We use this to preserve values of the LHS in the RHS snapshot.
v.decider.prover.comment(s"Produce new magic wand snapshot map for wand $wand")
val abstractLhs = v.decider.fresh(sorts.Snap)
val wandMap = v.decider.fresh("$wm", sorts.Map(sorts.Snap, sorts.Snap))
val magicWandSnapshot = MagicWandSnapshot(abstractLhs, MapLookup(snapRhs, abstractLhs), wandMap)
val wandMap = v.decider.fresh("$mwsf", sorts.MagicWandSnapFunction)
val magicWandSnapshot = MagicWandSnapshot(abstractLhs, MWSFLookup(snapRhs, abstractLhs), wandMap)

// We assume that the wandMap that we get from `snapRhs`, which potentially is nested in a binary tree,
// is the same as our newly created wandMap.
Expand Down
35 changes: 29 additions & 6 deletions src/main/scala/state/Terms.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ object sorts {
override lazy val toString = id.toString
}

object MagicWandSnapFunction extends Sort {
val id: Identifier = Identifier("MWSF")
override lazy val toString: String = id.toString
}

case class FieldPermFunction() extends Sort {
val id = Identifier("FPM")
override lazy val toString = id.toString
Expand Down Expand Up @@ -2308,11 +2313,11 @@ object PredicateTrigger extends PreciseCondFlyweightFactory[(String, Term, Seq[T
class MagicWandSnapshot(val abstractLhs: Var, val rhsSnapshot: Term, val wandMap: Term) extends Term with ConditionalFlyweight[Term, MagicWandSnapshot] {
utils.assertSort(abstractLhs, "abstract lhs", sorts.Snap)
utils.assertSort(rhsSnapshot, "rhs snapshot", sorts.Snap)
utils.assertSort(wandMap, "wand map", sorts.Map(sorts.Snap, sorts.Snap))
utils.assertSort(wandMap, "wand map", sorts.MagicWandSnapFunction)

override val sort: Sort = sorts.Map(sorts.Snap, sorts.Snap)
override val sort: Sort = sorts.MagicWandSnapFunction

override lazy val toString = s"wandSnap(wandMap = $wandMap)"
override lazy val toString = s"wandSnap($wandMap)"

override val equalityDefiningMembers: Term = wandMap

Expand All @@ -2323,8 +2328,8 @@ class MagicWandSnapshot(val abstractLhs: Var, val rhsSnapshot: Term, val wandMap
*/
def definition: Term = Forall(
abstractLhs,
MapLookup(wandMap, abstractLhs) === rhsSnapshot,
Trigger(MapLookup(wandMap, abstractLhs))
MWSFLookup(wandMap, abstractLhs) === rhsSnapshot,
Trigger(MWSFLookup(wandMap, abstractLhs))
)

/**
Expand All @@ -2334,7 +2339,7 @@ class MagicWandSnapshot(val abstractLhs: Var, val rhsSnapshot: Term, val wandMap
* @param snapLhs The snapshot of the left-hand side that should be applied to the magic wand map.
* @return The snapshot of the right-hand side that preserves the values of the left-hand side.
*/
def applyToWandMap(snapLhs: Term): Term = MapLookup(wandMap, snapLhs)
def applyToWandMap(snapLhs: Term): Term = MWSFLookup(wandMap, snapLhs)
}

object MagicWandSnapshot {
Expand All @@ -2348,6 +2353,24 @@ object MagicWandSnapshot {
def unapply(snap: MagicWandSnapshot): Option[(Var, Term, Term)] = Some(snap.abstractLhs, snap.rhsSnapshot, snap.wandMap)
}

class MWSFLookup(val mwsf: Term, val snap: Term) extends Term with ConditionalFlyweightBinaryOp[MWSFLookup] {
val sort: Sort = sorts.Snap
override def p0: Term = mwsf
override def p1: Term = snap
override lazy val toString = s"$mwsf[$snap]"
}

object MWSFLookup extends PreciseCondFlyweightFactory[(Term, Term), MWSFLookup] {
override def apply(pair: (Term, Term)) = {
val (mwsf, snap) = pair
utils.assertSort(mwsf, "mwsf", sorts.MagicWandSnapFunction)
utils.assertSort(snap, "snap", sorts.Snap)
createIfNonExistent(pair)
}

override def actualCreate(args: (Term, Term)): MWSFLookup = new MWSFLookup(args._1, args._2)
}

class MagicWandChunkTerm(val chunk: MagicWandChunk) extends Term with ConditionalFlyweight[MagicWandChunk, MagicWandChunkTerm] {
override val sort = sorts.Unit /* TODO: Does this make sense? */
override lazy val toString = s"wand@${chunk.id.ghostFreeWand.pos}}"
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/state/Utils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ package object utils {
case MapDomain(t) => MapDomain(go(t))
case MapRange(t) => MapRange(go(t))
case MagicWandSnapshot(t0, t1, t2) => MagicWandSnapshot(go(t0), go(t1), go(t2))
case MWSFLookup(t0, t1) => MWSFLookup(go(t0), go(t1))
case Combine(t0, t1) => Combine(go(t0), go(t1))
case First(t) => First(go(t))
case Second(t) => Second(go(t))
Expand Down
11 changes: 0 additions & 11 deletions src/main/scala/supporters/DefaultMapsContributor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,4 @@ class DefaultMapsContributor(val domainTranslator: DomainsTranslator[Term], conf
assert(argumentSorts.size == 2)
sorts.Map(argumentSorts.head, argumentSorts.tail.head)
}

override def computeGroundTypeInstances(program: ast.Program): InsertionOrderedSet[BuiltinDomainType] = {
var setTypeInstances = super.computeGroundTypeInstances(program)

// Packaging and applying magic wands depends on Maps of type [[viper.silicon.state.terms.sorts.Snap]]
if (program.existsDefined { case ast.MagicWand(_, _) => true }) {
setTypeInstances ++= InsertionOrderedSet(Set(ast.MapType(ViperEmbedding(sorts.Snap), ViperEmbedding(sorts.Snap))))
}

setTypeInstances
}
}
5 changes: 0 additions & 5 deletions src/main/scala/supporters/DefaultSetsContributor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ class DefaultSetsContributor(val domainTranslator: DomainsTranslator[Term], conf
case ast.MapType(keyType, valueType) => Set(ast.SetType(keyType), ast.SetType(valueType))
}.flatten

// Packaging and applying magic wands depends on Sets of type [[viper.silicon.state.terms.sorts.Snap]]
if (program.existsDefined { case ast.MagicWand(_, _) => true }) {
setTypeInstances ++= InsertionOrderedSet(Set(ast.SetType(ViperEmbedding(sorts.Snap))))
}

setTypeInstances
}

Expand Down
92 changes: 92 additions & 0 deletions src/main/scala/supporters/MagicWandSnapFunctionsContributor.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2011-2024 ETH Zurich.

package viper.silicion.supporters

import viper.silicon.common.collections.immutable.InsertionOrderedSet
import viper.silicon.interfaces.{PreambleContributor, PreambleReader}
import viper.silicon.interfaces.decider.ProverLike
import viper.silicon.state.terms.{Sort, SortDecl, sorts}
import viper.silver.ast
import viper.silver.ast.Program

/**
* Add function declarations when the proof makes use of MagicWandSnapFunctions (MWSF).
* Those are used to preserve values across multiple applications of a magic wand, e.g. by using an applying expression.
*
* @param preambleReader Reader that returns the content of some given SMT2 files.
*/
class MagicWandSnapFunctionsContributor(preambleReader: PreambleReader[String, String])
extends PreambleContributor[Sort, String, String] {

/** File which contains all function declarations in the SMT2 syntax. */
private val FILE_DECLARATIONS = "/magic_wand_snap_functions_declarations.smt2"

/** Set for the sort [[viper.silicon.state.terms.sorts.MagicWandSnapFunction]] */
private var collectedSorts: InsertionOrderedSet[Sort] = InsertionOrderedSet.empty

/** Set for all function declaration related to [[viper.silicon.state.terms.sorts.MagicWandSnapFunction]] */
private var collectedFunctionDecls: Iterable[String] = Seq.empty

/* Functionality */

/**
* Add all function declarations when a program contains magic wands.
*
* @param program AST of the program to prove.
*/
override def analyze(program: Program): Unit = {
// If there are not magic wands, do not add any definitions or axioms
if (!program.existsDefined { case ast.MagicWand(_, _) => true }) return

collectedSorts = InsertionOrderedSet(sorts.MagicWandSnapFunction)
collectedFunctionDecls = preambleReader.readPreamble(FILE_DECLARATIONS)
}

/** Sorts to add to the preamble of the SMT proof script. */
override def sortsAfterAnalysis: Iterable[Sort] = collectedSorts

/** Add all sorts needed to the preamble using `sink`. */
override def declareSortsAfterAnalysis(sink: ProverLike): Unit = {
sortsAfterAnalysis foreach (s => sink.declare(SortDecl(s)))
}

/** Symbols to add to the preamble of the SMT proof script. */
override def symbolsAfterAnalysis: Iterable[String] =
extractPreambleLines(collectedFunctionDecls)

/** Add all symbols needed to the preamble using `sink`. */
override def declareSymbolsAfterAnalysis(sink: ProverLike): Unit =
emitPreambleLines(sink, collectedFunctionDecls)

/** Axioms to add to the preamble of the SMT proof script. Currently, there are none. */
override def axiomsAfterAnalysis: Iterable[String] = Seq.empty

/** Add all axioms needed to the preamble using `sink`. Currently, there are none. */
override def emitAxiomsAfterAnalysis(sink: ProverLike): Unit = {}

/** Helper function to transform the lines returned by the `PreambleReader`. */
private def extractPreambleLines(lines: Iterable[String]*): Iterable[String] =
lines.flatten

/** Helper function to emit all lines using `sink`. */
private def emitPreambleLines(sink: ProverLike, lines: Iterable[String]*): Unit = {
lines foreach { declaration =>
sink.emit(declaration)
}
}

/* Lifetime */

def reset(): Unit = {
collectedSorts = InsertionOrderedSet.empty
collectedFunctionDecls = Seq.empty
}

def stop(): Unit = {}

def start(): Unit = {}
}
8 changes: 8 additions & 0 deletions src/main/scala/verifier/DefaultMainVerifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package viper.silicon.verifier

import viper.silicion.supporters.MagicWandSnapFunctionsContributor
import viper.silicon.Config.{ExhaleMode, JoinMode}

import java.text.SimpleDateFormat
Expand Down Expand Up @@ -73,6 +74,7 @@ class DefaultMainVerifier(config: Config,
protected val fieldValueFunctionsContributor = new DefaultFieldValueFunctionsContributor(preambleReader, symbolConverter, termConverter, config)
protected val predSnapGenerator = new PredicateSnapGenerator(symbolConverter, snapshotSupporter)
protected val predicateAndWandSnapFunctionsContributor = new DefaultPredicateAndWandSnapFunctionsContributor(preambleReader, termConverter, predSnapGenerator, config)
protected val magicWandSnapFunctionsContributor = new MagicWandSnapFunctionsContributor(preambleReader)

private val _verificationPoolManager: VerificationPoolManager = new VerificationPoolManager(this)
def verificationPoolManager: VerificationPoolManager = _verificationPoolManager
Expand All @@ -82,6 +84,7 @@ class DefaultMainVerifier(config: Config,
sequencesContributor, setsContributor, multisetsContributor, mapsContributor, domainsContributor,
fieldValueFunctionsContributor,
predSnapGenerator, predicateAndWandSnapFunctionsContributor,
magicWandSnapFunctionsContributor,
functionsSupporter, predicateSupporter,
_verificationPoolManager,
MultiRunRecorders /* In lieu of a better place, include MultiRunRecorders singleton here */
Expand Down Expand Up @@ -459,6 +462,7 @@ class DefaultMainVerifier(config: Config,
domainsContributor,
fieldValueFunctionsContributor,
predicateAndWandSnapFunctionsContributor,
magicWandSnapFunctionsContributor,
functionsSupporter,
predicateSupporter
)
Expand All @@ -471,6 +475,7 @@ class DefaultMainVerifier(config: Config,
domainsContributor,
fieldValueFunctionsContributor,
predicateAndWandSnapFunctionsContributor,
magicWandSnapFunctionsContributor,
functionsSupporter,
predicateSupporter
)
Expand All @@ -483,6 +488,7 @@ class DefaultMainVerifier(config: Config,
domainsContributor,
fieldValueFunctionsContributor,
predicateAndWandSnapFunctionsContributor,
magicWandSnapFunctionsContributor,
functionsSupporter,
predicateSupporter
)
Expand All @@ -500,6 +506,7 @@ class DefaultMainVerifier(config: Config,
domainsContributor,
fieldValueFunctionsContributor,
predicateAndWandSnapFunctionsContributor,
magicWandSnapFunctionsContributor,
functionsSupporter,
predicateSupporter
)
Expand All @@ -512,6 +519,7 @@ class DefaultMainVerifier(config: Config,
domainsContributor,
fieldValueFunctionsContributor,
predicateAndWandSnapFunctionsContributor,
magicWandSnapFunctionsContributor,
functionsSupporter,
predicateSupporter
)
Expand Down

0 comments on commit 77324f4

Please sign in to comment.