Skip to content

Commit fd87482

Browse files
committed
Second optimization: When there is no applying expression use the original approach using MagicWandSnapSingleton.
1 parent 77324f4 commit fd87482

6 files changed

Lines changed: 136 additions & 44 deletions

File tree

src/main/scala/decider/TermToSMTLib2Converter.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ class TermToSMTLib2Converter
316316
val docBindings = ssep((bindings.toSeq map (p => parens(render(p._1) <+> render(p._2)))).to(collection.immutable.Seq), space)
317317
parens(text("let") <+> parens(docBindings) <+> render(body))
318318

319-
case MagicWandSnapshot(_, _, wandMap) => render(wandMap)
319+
case snap: MagicWandSnapSingleton => renderBinaryOp("$Snap.combine", snap)
320+
case MagicWandSnapFunction(_, _, wandMap) => render(wandMap)
320321
case MWSFLookup(mwsf, snap) => renderApp("MWSF_apply", Seq(mwsf, snap), sorts.Snap)
321322

322323
case _: MagicWandChunkTerm

src/main/scala/decider/TermToZ3APIConverter.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ class TermToZ3APIConverter
435435

436436
case bop: Combine =>
437437
ctx.mkApp(combineConstructor, convertTerm(bop.p0), convertTerm(bop.p1))
438+
case MagicWandSnapSingleton(abstractLhs, rhsSnapshot) =>
439+
ctx.mkApp(combineConstructor, convertTerm(abstractLhs), convertTerm(rhsSnapshot))
438440

439441
case SortWrapper(t, to) =>
440442
createApp(convertId(SortWrapperId(t.sort, to)), Seq(t), to)

src/main/scala/rules/MagicWandSupporter.scala

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
package viper.silicon.rules
88

99
import viper.silver.ast
10-
import viper.silver.ast.{Exp, Stmt}
10+
import viper.silver.ast.{Applying, Exp, Program, Stmt}
1111
import viper.silver.cfg.Edge
1212
import viper.silver.cfg.silver.SilverCfg.SilverBlock
1313
import viper.silver.verifier.PartialVerificationError
@@ -16,7 +16,7 @@ import viper.silicon.decider.RecordedPathConditions
1616
import viper.silicon.interfaces._
1717
import viper.silicon.interfaces.state._
1818
import viper.silicon.state._
19-
import viper.silicon.state.terms.{MagicWandSnapshot, _}
19+
import viper.silicon.state.terms.{MagicWandSnapshot, MagicWandSnapSingleton, MagicWandSnapFunction, _}
2020
import viper.silicon.utils.{freshSnap, toSf}
2121
import viper.silicon.verifier.Verifier
2222

@@ -212,10 +212,6 @@ object magicWandSupporter extends SymbolicExecutionRules {
212212
val s = if (state.exhaleExt) state else
213213
state.copy(reserveHeaps = Heap() :: state.h :: Nil)
214214

215-
// v.logger.debug(s"wand = $wand")
216-
// v.logger.debug("c.reserveHeaps:")
217-
// s.reserveHeaps.map(v.stateFormatter.format).foreach(str => v.logger.debug(str, 2))
218-
219215
val stackSize = 3 + s.reserveHeaps.tail.size
220216
// IMPORTANT: Size matches structure of reserveHeaps at [State RHS] below
221217
var recordedBranches: Seq[(State, Stack[Term], Stack[Option[Exp]], Vector[Term], Chunk)] = Nil
@@ -260,8 +256,13 @@ object magicWandSupporter extends SymbolicExecutionRules {
260256
val preMark = v3.decider.setPathConditionMark()
261257

262258
v3.decider.prover.comment(s"Create WandMap for wand $wand")
263-
val wandSnapshot = MagicWandSnapshot(freshSnapRoot, snapRhs, v3.decider.fresh("mwsf", sorts.MagicWandSnapFunction))
264-
v3.decider.assumeDefinition(wandSnapshot.definition)
259+
val wandSnapshot: MagicWandSnapshot = if (useMWSF(s4.program)) {
260+
val fct = MagicWandSnapFunction(freshSnapRoot, snapRhs, v3.decider.fresh("mwsf", sorts.MagicWandSnapFunction))
261+
v3.decider.assumeDefinition(fct.definition)
262+
fct
263+
} else {
264+
MagicWandSnapSingleton(freshSnapRoot, snapRhs)
265+
}
265266

266267
// If the wand is part of a quantified expression
267268
if (s4.qpMagicWands.contains(MagicWandIdentifier(wand, s.program))) {
@@ -285,16 +286,18 @@ object magicWandSupporter extends SymbolicExecutionRules {
285286
// Partition path conditions into a set which include the abstractLhs and those which do not
286287
val (pcsWithAbstractLhs, pcsWithoutAbstractLhs) = conservedPcs.flatMap(_.conditionalized).partition(pcs => pcs.contains(wandSnapshot.abstractLhs))
287288
// For all path conditions which include the abstractLhs, add those as part of the definition of the wandMap in the same forall quantifier
288-
val pcsQuantified = Forall(
289-
wandSnapshot.abstractLhs,
290-
And(pcsWithAbstractLhs.map {
291-
// Remove redundant forall quantifiers with the same quantified variable
292-
case Quantification(Forall, wandSnapshot.abstractLhs :: Nil, body: Term, _, _, _, _) => body
293-
case p => p
294-
}),
295-
Trigger(MWSFLookup(wandSnapshot.wandMap, wandSnapshot.abstractLhs)),
296-
)
297-
289+
val pcsQuantified = wandSnapshot match {
290+
case _: MagicWandSnapSingleton => And(pcsWithAbstractLhs)
291+
case wandSnapshot: MagicWandSnapFunction => Forall(
292+
wandSnapshot.abstractLhs,
293+
And(pcsWithAbstractLhs.map {
294+
// Remove redundant forall quantifiers with the same quantified variable
295+
case Quantification(Forall, wandSnapshot.abstractLhs :: Nil, body: Term, _, _, _, _) => body
296+
case p => p
297+
}),
298+
Trigger(MWSFLookup(wandSnapshot.wandMap, wandSnapshot.abstractLhs)),
299+
)
300+
}
298301
appendToResults(s5, ch, v4.decider.pcs.after(preMark), pcsQuantified +: pcsWithoutAbstractLhs, v4)
299302
Success()
300303
})
@@ -418,8 +421,11 @@ object magicWandSupporter extends SymbolicExecutionRules {
418421

419422
// If the snapWand is a (wrapped) MagicWandSnapshot then lookup the snapshot of the right-hand side by applying snapLhs.
420423
val magicWandSnapshotLookup = snapWand match {
421-
case snapshot: MagicWandSnapshot => snapshot.applyToWandMap(snapLhs)
422-
case SortWrapper(snapshot: MagicWandSnapshot, _) => snapshot.applyToWandMap(snapLhs)
424+
case snapshot: MagicWandSnapSingleton =>
425+
v2.decider.assume(snapLhs === snapshot.abstractLhs)
426+
snapshot.rhsSnapshot
427+
case snapshot: MagicWandSnapFunction => snapshot.applyToWandMap(snapLhs)
428+
case SortWrapper(snapshot: MagicWandSnapFunction, _) => snapshot.applyToWandMap(snapLhs)
423429
// Fallback solution for quantified magic wands
424430
case predicateLookup: PredicateLookup =>
425431
v2.decider.assume(snapLhs === First(snapWand))
@@ -524,4 +530,15 @@ object magicWandSupporter extends SymbolicExecutionRules {
524530
s.reserveCfgs.head.outEdges(b)
525531
else
526532
s.methodCfg.outEdges(b)
533+
534+
/**
535+
* Determine, whether to use MWSF or MagicWandSnapshot.
536+
* MWSF have the advantage over MagicWandSnapshot that they can be applied multiple times.
537+
* However, they are slower than MagicWandSnapshots.
538+
*
539+
* @param program AST program which is verified.
540+
* @return Boolean indicating whether to use MWSF instead of MagicWandSnapshot.
541+
*/
542+
def useMWSF(program: Program): Boolean =
543+
program.existsDefined { case Applying(_, _) => true }
527544
}

src/main/scala/rules/Producer.scala

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -397,18 +397,25 @@ object producer extends ProductionRules {
397397
Q(s2, v1)})
398398

399399
case wand: ast.MagicWand =>
400-
val snapRhs = sf(sorts.MagicWandSnapFunction, v)
401-
402-
// Create Map that takes a snapshot, which represent the values of the consumed LHS of the wand,
403-
// and relates it to the snapshot of the RHS. We use this to preserve values of the LHS in the RHS snapshot.
404-
v.decider.prover.comment(s"Produce new magic wand snapshot map for wand $wand")
405-
val abstractLhs = v.decider.fresh(sorts.Snap)
406-
val wandMap = v.decider.fresh("$mwsf", sorts.MagicWandSnapFunction)
407-
val magicWandSnapshot = MagicWandSnapshot(abstractLhs, MWSFLookup(snapRhs, abstractLhs), wandMap)
408-
409-
// We assume that the wandMap that we get from `snapRhs`, which potentially is nested in a binary tree,
410-
// is the same as our newly created wandMap.
411-
v.decider.assumeDefinition(magicWandSnapshot.definition)
400+
val magicWandSnapshot: MagicWandSnapshot = if (magicWandSupporter.useMWSF(s.program)) {
401+
val snapRhs = sf(sorts.MagicWandSnapFunction, v)
402+
403+
// Create Map that takes a snapshot, which represent the values of the consumed LHS of the wand,
404+
// and relates it to the snapshot of the RHS. We use this to preserve values of the LHS in the RHS snapshot.
405+
v.decider.prover.comment(s"Produce new magic wand snapshot map for wand $wand")
406+
val abstractLhs = v.decider.fresh(sorts.Snap)
407+
val wandMap = v.decider.fresh("$mwsf", sorts.MagicWandSnapFunction)
408+
val snapFunction = MagicWandSnapFunction(abstractLhs, MWSFLookup(snapRhs, abstractLhs), wandMap)
409+
410+
// We assume that the wandMap that we get from `snapRhs`, which potentially is nested in a binary tree,
411+
// is the same as our newly created wandMap.
412+
v.decider.assumeDefinition(snapFunction.definition)
413+
snapFunction
414+
415+
} else {
416+
val snap = sf(sorts.Snap, v)
417+
MagicWandSnapSingleton(snap)
418+
}
412419

413420
magicWandSupporter.createChunk(s, wand, magicWandSnapshot, pve, v)((s1, chWand, v1) =>
414421
chunkSupporter.produce(s1, s1.h, chWand, v1)((s2, h2, v2) =>

src/main/scala/state/Terms.scala

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,6 +2300,68 @@ object PredicateTrigger extends PreciseCondFlyweightFactory[(String, Term, Seq[T
23002300

23012301
/* Magic wands */
23022302

2303+
abstract class MagicWandSnapshot(val abstractLhs: Term, val rhsSnapshot: Term) extends Term {
2304+
utils.assertSort(abstractLhs, "abstract lhs", sorts.Snap)
2305+
utils.assertSort(rhsSnapshot, "rhs snapshot", sorts.Snap)
2306+
}
2307+
2308+
/**
2309+
* Snapshot for a magic wand. It represents a function which can be used only once
2310+
* by equating a snapshot from consuming the wand's LHS with `abstractLhs`.
2311+
*
2312+
* For a snapshot which can be applied multiple times see [[MagicWandSnapFunction]].
2313+
*
2314+
* @param abstractLhs The abstract snapshot which acts as a placeholder in the
2315+
* `rhsSnapshot` for the values when applying the magic wand.
2316+
* @param rhsSnapshot Snapshot which can be used when producing the wand's RHS.
2317+
*/
2318+
class MagicWandSnapSingleton(override val abstractLhs: Term, override val rhsSnapshot: Term)
2319+
extends MagicWandSnapshot(abstractLhs, rhsSnapshot)
2320+
with ConditionalFlyweightBinaryOp[MagicWandSnapSingleton] {
2321+
2322+
override lazy val toString: String = s"wandSnap(lhs = $abstractLhs, rhs = $rhsSnapshot)"
2323+
2324+
/* ConditionalFlyweightBinaryOp members */
2325+
2326+
override def sort: Sort = sorts.Snap
2327+
2328+
override def p0: Term = abstractLhs
2329+
2330+
override def p1: Term = rhsSnapshot
2331+
}
2332+
2333+
object MagicWandSnapSingleton {
2334+
var pool = new TrieMap[(Term, Term), MagicWandSnapSingleton]()
2335+
2336+
/** Craete a new [[MagicWandSnapSingleton]] from a single snapshot term. */
2337+
def apply(snapshot: Term): MagicWandSnapshot = {
2338+
assert(snapshot.sort == sorts.Snap, s"MagicWandSnapshot.apply expects sorts Snap but got ${snapshot.sort}")
2339+
2340+
snapshot match {
2341+
case snap: MagicWandSnapSingleton => snap
2342+
case _ => MagicWandSnapSingleton(First(snapshot), Second(snapshot))
2343+
}
2344+
}
2345+
2346+
/** Create a new [[MagicWandSnapSingleton]] from two snapshot terms. */
2347+
def apply(abstractLhs: Term, rhsSnapshot: Term): MagicWandSnapSingleton =
2348+
createIfNonExistent((abstractLhs, rhsSnapshot))
2349+
2350+
/** Destruct a [[MagicWandSnapSingleton]] instance. Used in [[viper.silicon.state.utils.transform]] */
2351+
def unapply(mws: MagicWandSnapSingleton): Some[(Term, Term)] =
2352+
Some((mws.abstractLhs, mws.rhsSnapshot))
2353+
2354+
private def createIfNonExistent(args: (Term, Term)): MagicWandSnapSingleton = {
2355+
if (Verifier.config.useFlyweight) {
2356+
pool.getOrElseUpdate(args, actualCreate(args))
2357+
} else {
2358+
actualCreate(args)
2359+
}
2360+
}
2361+
2362+
private def actualCreate(tuple: (Term, Term)) = new MagicWandSnapSingleton(tuple._1, tuple._2)
2363+
}
2364+
23032365
/**
23042366
* Represents a snapshot of a magic wand, which is a map from Snap to Snap.
23052367
*
@@ -2310,7 +2372,10 @@ object PredicateTrigger extends PreciseCondFlyweightFactory[(String, Term, Seq[T
23102372
* In the symbolic execution this represents a function that when we apply a magic wand
23112373
* it consumes the left-hand side and uses the resulting snapshot to look up which right-hand side should be produced.
23122374
*/
2313-
class MagicWandSnapshot(val abstractLhs: Var, val rhsSnapshot: Term, val wandMap: Term) extends Term with ConditionalFlyweight[Term, MagicWandSnapshot] {
2375+
class MagicWandSnapFunction(override val abstractLhs: Var, override val rhsSnapshot: Term, val wandMap: Term)
2376+
extends MagicWandSnapshot(abstractLhs, rhsSnapshot)
2377+
with ConditionalFlyweight[Term, MagicWandSnapFunction] {
2378+
23142379
utils.assertSort(abstractLhs, "abstract lhs", sorts.Snap)
23152380
utils.assertSort(rhsSnapshot, "rhs snapshot", sorts.Snap)
23162381
utils.assertSort(wandMap, "wand map", sorts.MagicWandSnapFunction)
@@ -2342,15 +2407,14 @@ class MagicWandSnapshot(val abstractLhs: Var, val rhsSnapshot: Term, val wandMap
23422407
def applyToWandMap(snapLhs: Term): Term = MWSFLookup(wandMap, snapLhs)
23432408
}
23442409

2345-
object MagicWandSnapshot {
2346-
/**
2347-
* Create a new instance of `MagicWandSnapshot`.
2348-
*
2349-
* @see [[viper.silicon.state.terms.MagicWandSnapshot]]
2350-
*/
2351-
def apply(abstractLhs: Var, rhsSnapshot: Term, wandMap: Term): MagicWandSnapshot = new MagicWandSnapshot(abstractLhs, rhsSnapshot, wandMap)
2410+
object MagicWandSnapFunction {
2411+
/** Create a new instance of [[viper.silicon.state.terms.MagicWandSnapFunction]]. */
2412+
def apply(abstractLhs: Var, rhsSnapshot: Term, wandMap: Term): MagicWandSnapFunction =
2413+
new MagicWandSnapFunction(abstractLhs, rhsSnapshot, wandMap)
23522414

2353-
def unapply(snap: MagicWandSnapshot): Option[(Var, Term, Term)] = Some(snap.abstractLhs, snap.rhsSnapshot, snap.wandMap)
2415+
/** Destructs an instance of [[viper.silicon.state.terms.MagicWandSnapFunction]]. */
2416+
def unapply(snap: MagicWandSnapFunction): Option[(Var, Term, Term)] =
2417+
Some(snap.abstractLhs, snap.rhsSnapshot, snap.wandMap)
23542418
}
23552419

23562420
class MWSFLookup(val mwsf: Term, val snap: Term) extends Term with ConditionalFlyweightBinaryOp[MWSFLookup] {
@@ -2361,7 +2425,7 @@ class MWSFLookup(val mwsf: Term, val snap: Term) extends Term with ConditionalFl
23612425
}
23622426

23632427
object MWSFLookup extends PreciseCondFlyweightFactory[(Term, Term), MWSFLookup] {
2364-
override def apply(pair: (Term, Term)) = {
2428+
override def apply(pair: (Term, Term)): MWSFLookup = {
23652429
val (mwsf, snap) = pair
23662430
utils.assertSort(mwsf, "mwsf", sorts.MagicWandSnapFunction)
23672431
utils.assertSort(snap, "snap", sorts.Snap)

src/main/scala/state/Utils.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,8 @@ package object utils {
196196
case MapUpdate(t0, t1, t2) => MapUpdate(go(t0), go(t1), go(t2))
197197
case MapDomain(t) => MapDomain(go(t))
198198
case MapRange(t) => MapRange(go(t))
199-
case MagicWandSnapshot(t0, t1, t2) => MagicWandSnapshot(go(t0), go(t1), go(t2))
199+
case MagicWandSnapSingleton(t0, t1) => MagicWandSnapSingleton(go(t0), go(t1))
200+
case MagicWandSnapFunction(t0, t1, t2) => MagicWandSnapFunction(go(t0), go(t1), go(t2))
200201
case MWSFLookup(t0, t1) => MWSFLookup(go(t0), go(t1))
201202
case Combine(t0, t1) => Combine(go(t0), go(t1))
202203
case First(t) => First(go(t))

0 commit comments

Comments
 (0)