-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Code explosion in mergeCalls #146
Comments
I mentioned this at some point (maybe to @gsps ?): we should be able to move the The idea is to change the inox/src/main/scala/inox/solvers/unrolling/TemplateGenerator.scala Lines 370 to 371 in 22de8d6
would become val (trec, tClauses) = mkExprClauses(newBool1, thenn, localSubst, pol)
val (erec, eClauses) = mkExprClauses(newBool2, elze, localSubst, pol)
builder ++= mergeCalls(pathVar, localSubst, tClauses, eClauses) The new def mergeCalls(pathVar: Variable, condVar: Variable, substMap: Map[Variable, Encoded],
thenClauses: TemplateClauses, elseClauses: TemplateClauses): TemplateClauses = {
val builder = new Builder(pathVar, substMap)
builder ++= thenClauses
builder ++ elseClauses
// Clear all guardedExprs in builder since we're going to transform them by merging calls.
// The transformed guardedExprs will be added to builder at the end of the function.
builder.guardedExprs = Map.empty
def collectCalls(expr: Expr): Set[FunctionInvocation] =
exprOps.collect { case fi: FunctionInvocation => Set(fi) case _ => Set.empty[FunctionInvocation] }(expr)
def countCalls(expr: Expr): Int =
exprOps.count { case fi: FunctionInvocation => 1 case _ => 0}(expr)
def replaceCall(call: FunctionInvocation, newExpr: Expr)(e: Expr): Expr =
exprOps.replace(Map(call, newExpr), e)
def getCalls(guardedExprs: Map[Variable, Seq[Expr]]): Map[TypedFunDef, Seq[(FunctionInvocation, Set[Variable])]] =
(for {(b, es) <- guardedExprs; e <- es; fi <- collectCalls(e)) yield (b -> fi))
.groupBy(_._2)
.mapValues(_.map(_._1).toSet)
.toSeq
.groupBy(_._1.tfd)
.mapValues(_.toList.distinct.sortBy(p => countCalls(p._1))) // place inner calls first
.toMap
var thenGuarded = thenClauses._4
var elseGuarded = elseClauses._4
val thenCalls = getCalls(thenGuarded)
val elseCalls = getCalls(elseGuarded)
// We sort common function calls in order to merge nested calls first.
var toMerge = (thenCalls.keys() & elseCalls.keys())
.flatMap(tfd => thenCalls(tfd) zip elseCalls(tfd))
.toSeq
.sortBy(p => countCalls(p._1._1) + countCalls(p._2._1))
while (toMerge.nonEmpty) {
val ((thenCall, thenBlockers), (elseCall, elseBlockers)) = toMerge.head
toMerge = toMerge.tail
val newExpr: Variable = Variable.fresh("call", thenCall.tfd.getType, true)
builder.storeExpr(newExpr)
val replaceThen = replaceCall(thenCall, newExpr) _
val replaceElse = replaceCall(elseCall, newExpr) _
thenGuarded = thenGuarded.mapValues(_.map(replaceThen)
elseGuarded = elseGuarded.mapValues(_.map(replaceElse)
toMerge = toMerge.map(p => (replaceThen(p._1), replaceElse(p._2))
val newBlocker: Variable = Variable.fresh("bm", BooleanType(), true)
// TODO: make this a method in Builder similar to storeCond
builder.storeConds(thenBlockers ++ elseBlockers, newBlocker)
builder.iff(orJoin((thenBlockers ++ elseBlockers).toSeq), newBlocker)
val newArgs = (thenCall.args zip elseCall.args).map { case (thenArg, elseArg) =>
val (newArg, argClauses) = mkExprClauses(newBlocker, IfExpr(condVar, thenArg, elseArg), builder.localSubst)
builder ++= argClauses
newArg
}
val newCall = thenCall.tfd.applied(newArgs)
builder.storeGuarded(newBlocker, Equals(newExpr, newCall))
}
for ((b, es) <- thenGuarded; e <- es) builder.storeGuarded(b, e)
for ((b, es) <- elseGuarded; e <- es) builder.storeGuarded(b, e)
builder.result
} |
Should |
No, I think it shouldn't be necessary. |
cc: @samuelchassot
We found an example where the code simplification becomes very slow, probably due to a code explosion in
mergeCalls
:Can be reproduced with the Stainless code below and the command:
Stainless code
Expression before mergeFunctions
The text was updated successfully, but these errors were encountered: