Skip to content

Commit 14728c0

Browse files
authored
Simplify unfolds by reusing results of predicate verification (#929)
1 parent 5c6ede8 commit 14728c0

19 files changed

Lines changed: 497 additions & 227 deletions

src/main/scala/Config.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,13 @@ class Config(args: Seq[String]) extends SilFrontendConfig(args, "Silicon") {
193193
noshort = true
194194
)
195195

196+
val enableSimplifiedUnfolds: ScallopOption[Boolean] = opt[Boolean]("enableSimplifiedUnfolds",
197+
descr = ( "Enable an optimization that reuses the results of verifying a predicate to simplify the process "
198+
+ "of unfolding it (via an unfold statement or an unfolding expression)."),
199+
default = Some(false),
200+
noshort = true
201+
)
202+
196203
val logLevel: ScallopOption[String] = opt[String]("logLevel",
197204
descr = "One of the log levels ALL, TRACE, DEBUG, INFO, WARN, ERROR, OFF",
198205
default = None,

src/main/scala/interfaces/Preamble.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,8 @@ trait PreambleContributor[+SO, +SY, +AX] extends StatefulComponent {
2929
def sortsAfterAnalysis: Iterable[SO]
3030
def declareSortsAfterAnalysis(sink: ProverLike): Unit
3131

32-
def symbolsAfterAnalysis: Iterable[SY]
3332
def declareSymbolsAfterAnalysis(sink: ProverLike): Unit
3433

35-
def axiomsAfterAnalysis: Iterable[AX]
3634
def emitAxiomsAfterAnalysis(sink: ProverLike): Unit
3735
}
3836

src/main/scala/interfaces/state/Chunks.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
package viper.silicon.interfaces.state
88

9+
import viper.silicon
910
import viper.silicon.resources.ResourceID
1011
import viper.silicon.state.terms.{Term, Var}
1112
import viper.silver.ast
1213

13-
trait Chunk
14-
14+
trait Chunk {
15+
def substitute(terms: silicon.Map[Term, Term]): Chunk
16+
}
1517
trait ChunkIdentifer
1618

1719
trait GeneralChunk extends Chunk {
@@ -22,6 +24,8 @@ trait GeneralChunk extends Chunk {
2224
def permMinus(perm: Term, permExp: Option[ast.Exp]): GeneralChunk
2325
def permPlus(perm: Term, permExp: Option[ast.Exp]): GeneralChunk
2426

27+
def permScale(perm: Term, permExp: Option[ast.Exp]): GeneralChunk
28+
2529
val permExp: Option[ast.Exp]
2630
}
2731

src/main/scala/rules/Consumer.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ object consumer extends ConsumptionRules {
383383
val debugExp = Option.when(withExp)(DebugExp.createInstance(s"Field Trigger: ${eRcvrNew.get.toString}.${field.name}"))
384384
v2.decider.assume(FieldTrigger(field.name, smDef1.sm, tRcvr), debugExp)
385385
// v2.decider.assume(PermAtMost(tPerm, FullPerm()))
386-
s2.copy(smCache = smCache1)
386+
s2.copy(smCache = smCache1, functionRecorder = s2.functionRecorder.recordFvfAndDomain(smDef1))
387387
} else {
388388
s2
389389
}
@@ -427,7 +427,7 @@ object consumer extends ConsumptionRules {
427427
s2, predicate, s2.predicateFormalVarMap(predicate), relevantChunks, v2)
428428
val debugExp = Option.when(withExp)(DebugExp.createInstance(s"PredicateTrigger(${predicate.name}(${eArgsNew.mkString(", ")}))", isInternal_ = true))
429429
v2.decider.assume(PredicateTrigger(predicate.name, smDef1.sm, tArgs), debugExp)
430-
s2.copy(smCache = smCache1)
430+
s2.copy(smCache = smCache1, functionRecorder = s2.functionRecorder.recordFvfAndDomain(smDef1))
431431
} else {
432432
s2
433433
}
@@ -497,7 +497,7 @@ object consumer extends ConsumptionRules {
497497
val predName = MagicWandIdentifier(wand, s.program).toString
498498
val debugExp = Option.when(withExp)(DebugExp.createInstance(s"PredicateTrigger($predName($argsString))", isInternal_ = true))
499499
v1.decider.assume(PredicateTrigger(predName, smDef1.sm, tArgs), debugExp)
500-
s1.copy(smCache = smCache1)
500+
s1.copy(smCache = smCache1, functionRecorder = s1.functionRecorder.recordFvfAndDomain(smDef1))
501501
} else {
502502
s1
503503
}

src/main/scala/rules/Evaluator.scala

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package viper.silicon.rules
88

9+
import viper.silicon
910
import viper.silicon.debugger.DebugExp
1011
import viper.silicon.Config.JoinMode
1112
import viper.silver.ast
@@ -316,7 +317,7 @@ object evaluator extends EvaluationRules {
316317
val smLookup = Lookup(fa.field.name, smDef1.sm, tRcvr)
317318
val fr2 =
318319
s2.functionRecorder.recordSnapshot(fa, v1.decider.pcs.branchConditions, smLookup)
319-
.recordFvfAndDomain(smDef1)
320+
.recordFvfAndDomain(smDef1).recordPermMap(pmDef1)
320321
val s3 = s2.copy(functionRecorder = fr2)
321322
Q(s3, smLookup, newFa, v1)
322323
}
@@ -578,7 +579,8 @@ object evaluator extends EvaluationRules {
578579
quantifiedChunkSupporter.summarisingPermissionMap(
579580
s1, wand, formalVars, relevantChunks, null, v1)
580581

581-
(s1.copy(pmCache = pmCache), pmDef)
582+
val newFr = s1.functionRecorder.recordPermMap(pmDef)
583+
(s1.copy(pmCache = pmCache, functionRecorder = newFr), pmDef)
582584
}
583585
(s2, PredicatePermLookup(identifier.toString, pmDef.pm, args))
584586

@@ -594,8 +596,8 @@ object evaluator extends EvaluationRules {
594596
val (pmDef, pmCache) =
595597
quantifiedChunkSupporter.summarisingPermissionMap(
596598
s1, field, Seq(`?r`), relevantChunks, null, v1)
597-
598-
(s1.copy(pmCache = pmCache), pmDef)
599+
val newFr = s1.functionRecorder.recordPermMap(pmDef)
600+
(s1.copy(pmCache = pmCache, functionRecorder = newFr), pmDef)
599601
}
600602
val currentPermAmount = PermLookup(field.name, pmDef.pm, args.head)
601603
v1.decider.prover.comment(s"perm($resacc) ~~> assume upper permission bound")
@@ -1029,16 +1031,33 @@ object evaluator extends EvaluationRules {
10291031
val argsPairs: List[(Term, Option[ast.Exp])] = if (withExp) tArgs zip eArgsNew.get.map(Some(_)) else tArgs zip Seq.fill(tArgs.size)(None)
10301032
val insg = s7.g + Store(predicate.formalArgs map (_.localVar) zip argsPairs)
10311033
val s7a = s7.copy(g = insg).setConstrainable(s7.constrainableARPs, false)
1032-
produce(s7a, toSf(snap.get), body, pve, v4)((s8, v5) => {
1033-
val s9 = s8.copy(g = s7.g,
1034-
functionRecorder = s8.functionRecorder.changeDepthBy(-1),
1035-
recordVisited = s3.recordVisited,
1036-
permissionScalingFactor = s6.permissionScalingFactor,
1037-
permissionScalingFactorExp = s6.permissionScalingFactorExp,
1038-
constrainableARPs = s1.constrainableARPs)
1039-
.decCycleCounter(predicate)
1040-
val s10 = v5.stateConsolidator(s9).consolidateOptionally(s9, v5)
1041-
eval(s10, eIn, pve, v5)((s9, t9, e9, v9) => QB(s9, (t9, e9), v9))})})
1034+
1035+
if (s7a.predicateData(predicate).predContents.isDefined) {
1036+
val toReplace: silicon.Map[Term, Term] = silicon.Map.from(s7a.predicateData(predicate).params.get.zip(Seq(snap.get) ++ tArgs))
1037+
predicateSupporter.producePredicateContents(s7a, s7a.predicateData(predicate).predContents.get, toReplace, v4, true)((s8, v5) => {
1038+
val s9 = s8.copy(g = s7.g,
1039+
functionRecorder = s8.functionRecorder.changeDepthBy(-1),
1040+
recordVisited = s3.recordVisited,
1041+
permissionScalingFactor = s6.permissionScalingFactor,
1042+
permissionScalingFactorExp = s6.permissionScalingFactorExp,
1043+
constrainableARPs = s1.constrainableARPs)
1044+
.decCycleCounter(predicate)
1045+
val s10 = v5.stateConsolidator(s9).consolidateOptionally(s9, v5)
1046+
eval(s10, eIn, pve, v5)((s9, t9, e9, v9) => QB(s9, (t9, e9), v9))
1047+
})
1048+
} else {
1049+
produce(s7a, toSf(snap.get), body, pve, v4)((s8, v5) => {
1050+
val s9 = s8.copy(g = s7.g,
1051+
functionRecorder = s8.functionRecorder.changeDepthBy(-1),
1052+
recordVisited = s3.recordVisited,
1053+
permissionScalingFactor = s6.permissionScalingFactor,
1054+
permissionScalingFactorExp = s6.permissionScalingFactorExp,
1055+
constrainableARPs = s1.constrainableARPs)
1056+
.decCycleCounter(predicate)
1057+
val s10 = v5.stateConsolidator(s9).consolidateOptionally(s9, v5)
1058+
eval(s10, eIn, pve, v5)((s9, t9, e9, v9) => QB(s9, (t9, e9), v9))})
1059+
}
1060+
})
10421061
})(join(eIn.typ, "joined_unfolding", s2.relevantQuantifiedVariables.map(_._1),
10431062
Option.when(withExp)(s2.relevantQuantifiedVariables.map(_._2.get)), v2))((s12, r12, v7)
10441063
=> {
@@ -1049,7 +1068,8 @@ object evaluator extends EvaluationRules {
10491068
createFailure(pve dueTo NonPositivePermission(ePerm.get), v2, s2, IsPositive(tPerm), ePermNew.map(p => ast.PermGtCmp(p, ast.NoPerm()())(p.pos, p.info, p.errT)))}))
10501069
} else {
10511070
val unknownValue = v.decider.appliedFresh("recunf", v.symbolConverter.toSort(eIn.typ), s.relevantQuantifiedVariables.map(_._1))
1052-
Q(s, unknownValue, Option.when(withExp)(ast.LocalVarWithVersion("unknownValue", eIn.typ)(eIn.pos, eIn.info, eIn.errT)), v)
1071+
val newFuncRec = s.functionRecorder.recordFreshSnapshot(unknownValue.applicable.asInstanceOf[Function])
1072+
Q(s.copy(functionRecorder = newFuncRec), unknownValue, Option.when(withExp)(ast.LocalVarWithVersion("unknownValue", eIn.typ)(eIn.pos, eIn.info, eIn.errT)), v)
10531073
}
10541074

10551075
case ast.Applying(wand, eIn) =>

0 commit comments

Comments
 (0)